Dashboard Builder¶
Source: src/memframe/dashboard/
Turn a batch of query results into a self-contained dashboard. You collect the results memFrame already produced (DataFrames, Plotly figures, dicts/metrics, scalars), an optional Pydantic-AI agent designs the layout, and the builder renders a single full-screen Plotly figure (the "canvas") composed of subplots/domains — no Dash or web server required.
Public API¶
DashboardManager collects results, designs a layout, and renders HTML:
| Method | Purpose |
|---|---|
add(title, result, description=None) |
append one result |
collect([(title, result), …]) |
append many results |
summarize() -> list[str] |
compact per-result summaries for the agent |
await design(settings) -> DashboardDesign |
run the designer agent (needs the ai extra) |
render(design) -> str |
build the HTML dashboard |
save(design, filename="dashboard.html") -> str |
write the HTML to disk |
settings is a memframe_ai.AISettings (provider/model/api key). The agent is
imported lazily, so the core library stays free of the pydantic-ai extra
until you actually call design().
Example¶
import pandas as pd
import plotly.express as px
from memframe.dashboard import DashboardManager
from memframe_ai import AISettings
df = pd.DataFrame({"region": ["A", "B", "C"], "sales": [10, 20, 30]})
dm = DashboardManager()
dm.add("Sales by region", px.bar(df, x="region", y="sales"))
dm.add("Raw table", df)
dm.add("Conversion", {"rate": 0.42})
settings = AISettings(provider="openai", model="gpt-4o", api_key="sk-...")
design = await dm.design(settings)
html = dm.render(design) # or: dm.save(design, "dashboard.html")
One-shot from a sentence¶
MemFrame.adashboard(sentence) collapses the whole flow into one call: it
reuses the existing planner + specialist pipeline (.achat()) to decompose a
natural-language sentence into sub-queries, executes them against the active
dataset, then feeds the results through the dashboard designer and renders a
self-contained HTML page. A sync MemFrame.dashboard(sentence) wrapper exists
for non-async contexts.
await mf.aset_active("my_dataset")
await mf.aenable_agent(api_key="sk-...")
html = await mf.adashboard(
"fillna C with mean then calculate the value counts of D and add B with the cleaned C",
)
# In a notebook `adashboard` returns a native Plotly figure (renders inline via
# its mimebundle); in a terminal it returns the self-contained HTML string.
- Async (
adashboard) and requires the agent enabled (aenable_agent). Raises a clear error otherwise, or if there is no active dataset. - Set
show=Falseto skip the env-agnostic display and just get the HTML back. - The return value is env-aware: in a notebook (Jupyter/Colab/VSCode) it returns
the native Plotly
Figureso the cell renders it inline; in a terminal it returns the self-contained HTML string. The display step usesmemframe.utils.plot_renderer.smart_show(notebook renders inline, terminal writesdashboard.htmland opens the browser).
Token safety¶
Raw result values are never sent to an LLM. The planner and specialists
receive only schema, aggregates, and a small capped table preview
(build_domain_context). The dashboard designer receives only the compact
summaries produced by DashboardManager.summarize() — shape, column names, a
2-row sample, and a capped Plotly spec preview. The full DataFrames and figures
stay local and are consumed solely by the renderer.
Layout rules (enforced by the agent + renderer)¶
- Type → widget (hard contract): a
DataFramealways renders as a table, a pre-built PlotlyFigurealways renders as a plot (keep_existing), and adict/numberalways renders as a metric. A DataFrame can never be charted — both the agent prompt and the renderer coercekindfrom the actual result type, so a mislabel cannot produce a wrong widget. - Single-figure canvas: the whole dashboard is ONE responsive Plotly figure.
DataFrame→go.Tabletrace,Figure→ its traces placed in a subplot cell,dict/number →go.Indicator(single value) orgo.Table(multi-key dict). The figure fills the viewport (100vw/100vh,config={"responsive": True}). - Maximum two widgets per row — the renderer greedily packs widgets into rows of one (full width) or two (paired) cells; each cell becomes a subplot.
- Plots: for a raw
DataFramethe renderer builds a chart withplotly.expressusing the agent'schart_type/x/y; a pre-builtFigureis kept as-is with only its title/size/labels adjusted. - Metrics: a
dict/scalarbecomes a KPI (go.Indicator) with optionalprefix/suffix/decimal_places; a multi-key dict becomes a small table. - Themes:
global_theme="light"(default) or"dark", applied via the Plotlyplotly_white/plotly_darktemplate.
Design model¶
design() returns a DashboardDesign (see src/memframe/dashboard/models.py):
a dashboard_title, global_theme, and a list of WidgetDesign items, each
referencing its source result by result_index and carrying a FigureDesign
or MetricDesign. The same model is the contract between the agent and the
renderer, so you can also build a DashboardDesign by hand and call
render() directly without the agent.