Adding Content with Tabs
When you want to organize complex task outputs, visual summaries, or debugging information into distinct navigable views, flyte-sdk provides an interactive multi-tab HTML report system.
Each Flyte report is compiled into a single interactive HTML file (report.html) where tabs are rendered as separate HTML content divisions switched via clickable navigation links.
Organizing Content Across Named Tabs
To split output across different sections, fetch or create a tab using flyte.report.get_tab() and append HTML snippets with Tab.log():
import flyte
from flyte import env
@env.task(report=True)
def evaluate_model() -> None:
# Retrieve or automatically create a tab named 'Summary'
summary_tab = flyte.report.get_tab("Summary")
summary_tab.log("<h3>Model Evaluation Overview</h3>")
summary_tab.log("<p>Status: <strong>Completed</strong></p>")
# Create another tab for metrics
metrics_tab = flyte.report.get_tab("Metrics")
metrics_tab.log("<table><tr><th>Epoch</th><th>Loss</th></tr><tr><td>1</td><td>0.12</td></tr></table>")
Setting report=True on the @env.task decorator instructs the task runner to automatically compile and upload the final report.html to the task's output storage upon completion.
Retrieving vs Creating Tabs
flyte.report.get_tab(name, create_if_missing=True) operates on the active report context:
- If
create_if_missing=True(the default), requesting a tab name that does not exist instantiates a newTabinstance, registers it inReport.tabs, and returns it. - If
create_if_missing=False, requesting a nonexistent tab raises aValueError:
import flyte
# This raises ValueError: Tab Metrics does not exist.
tab = flyte.report.get_tab("Metrics", create_if_missing=False)
Appending Content vs Replacing Content
The Tab class provides two primary methods for populating content:
Appending with Tab.log()
Tab.log(content: str) appends an HTML string snippet to the tab's internal content list. When the final report renders, snippets are concatenated with newlines:
import flyte
tab = flyte.report.get_tab("Execution Logs")
tab.log("<div>Step 1: Ingesting dataset...</div>")
tab.log("<div>Step 2: Normalizing features...</div>")
tab.log("<div>Step 3: Training complete.</div>")
Overwriting with Tab.replace()
Tab.replace(content: str) resets the tab's content list to contain only the newly provided HTML string. This is useful when showing progress or replacing preliminary outputs with final summaries:
import flyte
status_tab = flyte.report.get_tab("Status")
# Set initial status
status_tab.replace("<p>Job Status: <em>In Progress</em></p>")
# Overwrite previous status with final output
status_tab.replace("<p>Job Status: <span style='color:green;'>Success</span></p>")
Working with the Default 'main' Tab
Every Report automatically initializes a default tab named "main". Module-level convenience functions allow logging directly to this default tab without calling get_tab():
import flyte
from flyte import env
@env.task(report=True)
def data_pipeline() -> None:
# Logs directly into the default 'main' tab
flyte.report.log("<h2>Main Execution Log</h2>")
flyte.report.log("<p>Processing step 1...</p>")
# Overwrites the contents of the default 'main' tab
flyte.report.replace("<h2>Final Pipeline Results</h2><p>All steps finished.</p>")
Flushing Intermediate Reports
Both flyte.report.log and flyte.report.replace accept an optional do_flush=True parameter. When set, flyte-sdk immediately writes the current report state to remote storage:
import flyte
# Appends to 'main' and immediately flushes report.html to storage
flyte.report.log("<p>Checkpoint reached at epoch 50.</p>", do_flush=True)
# You can also manually flush all tabs at any point:
flyte.report.flush()
Working with Standalone Report Objects
You can instantiate and manage Report and Tab objects directly outside of tasks (for example, in unit tests or local scripts):
from flyte.report import Report
# Create a report instance
report = Report(name="custom-run")
# Access tabs directly from the report instance
overview_tab = report.get_tab("Overview")
overview_tab.log("<h1>Custom Run</h1>")
details_tab = report.get_tab("Details")
details_tab.log("<p>Detailed run metrics go here.</p>")
# Render final HTML string or IPython HTML object
raw_html = report.get_final_report()
Gotchas and Troubleshooting
Content Must Be HTML Snippets (Not Full Documents)
Tab content is injected directly into <div> tags inside the flyte-sdk report template (_template.html). Do not include document-level tags such as <!DOCTYPE html>, <html>, <head>, or <body>. Only supply HTML elements like <div>, <p>, <table>, <img>, or inline styles.
HTML Escaping Behavior
- Tab Titles: The names given in
get_tab(name)are escaped withhtml.escape(key)when generating navigation items (<li>), preventing broken layout from special characters in titles. - Tab Body Content: Tab contents provided to
log()andreplace()are rendered raw without escaping so that your custom formatting, tables, and scripts are preserved. If including untrusted user inputs, sanitize them before passing them totab.log()ortab.replace().
Running Outside Task Contexts
Calling flyte.report.current_report() or flyte.report.get_tab() when not in an active task returns a fallback Report("dummy"). Calling flyte.report.flush() outside a task context is a safe no-op.
Enabling Automatic Report Uploads
If a task does not set report=True in its @env.task(...) decorator, the task runner will not automatically upload report.html when the task finishes. If you want reports generated without report=True, you must explicitly invoke flyte.report.flush() before the task exits.