Callbacks
Two optional callbacks can be passed at construction time to hook into the rendering
pipeline. Use build() — the recommended entry point — to honour them. plot() remains
available as a raw render with no side-effects.
FigureStrategy(
...,
pre_build_callback=fn, # fires before plot()
post_build_callback=fn, # fires after plot()
)
view = strategy.build(dfs, tr, theme)
pre_build_callback
Fires before plot(). Mutations to payload["dfs"] change what gets plotted.
Mutations to payload["title"], payload["subtitle"], payload["caption"], and
payload["description"] are reflected in the rendered figure and the returned FigureView.
Payload structure:
payload
├── "dfs" dict[str, pl.DataFrame] Replace entries to change what's plotted
├── "strategy" str e.g. "BarChartStrategy" (read-only)
├── "title" str | None Assign to override the figure title
├── "subtitle" str | None Assign to override the figure subtitle
├── "caption" str | None Assign to override FigureView.caption
├── "description" str | None Assign to override FigureView.description
├── "storytelling_context" str | None Assign to override FigureView.storytelling_context
├── "color_rules" list[dict] Serialised color rules (read-only)
├── "variables" dict[str, dict] Variable name/description/value map (read-only)
└── "options" dict[str, dict] Option name/description/value map (read-only)
post_build_callback
Fires after plot(). Can mutate payload["figure"] or overwrite
payload["caption"] / payload["description"] before FigureView is returned.
Payload structure:
payload
├── "figure" go.Figure
├── "caption" str | None Assign to override FigureView.caption
├── "description" str | None Assign to override FigureView.description
├── "storytelling_context" str | None Assign to override FigureView.storytelling_context
├── "metadata" dict[str, Any] Strategy metadata snapshot (read-only)
│ ├── "strategy", "title", "subtitle", "caption", "description",
│ └── "storytelling_context", "color_rules"
└── "data"
└── "<query_key>"
├── "columns" list[str]
└── "rows" list[dict]
Examples
Example 1 — pre-build: LLM-generated data storytelling
def storytelling_callback(payload):
rows = payload["dfs"]["main"].to_dicts()
payload["storytelling_context"] = llm_client.generate(
f"Write a one-sentence insight for a '{payload['title']}' chart: {rows}"
)
strategy = BarChartStrategy(
...,
title="Revenue by Region",
pre_build_callback=storytelling_callback,
)
view = strategy.build(dfs={"main": df}, tr=tr, theme=theme)
# view.storytelling_context is now populated directly from the pre callback
Example 2 — post-build: override caption and description
def annotate(payload):
payload["description"] = "Automatically generated insight."
payload["caption"] = "Source: internal CRM data."
strategy = BarChartStrategy(..., post_build_callback=annotate)
view = strategy.build(dfs={"main": df}, tr=tr, theme=theme)
# view.caption == "Source: internal CRM data."
Example 3 — pre-build: mutate title and description
def enrich(payload):
rows = payload["dfs"]["main"].to_dicts()
payload["title"] = f"Revenue by Region — {len(rows)} entries"
payload["description"] = llm_client.generate(f"Summarise: {rows}")
strategy = BarChartStrategy(..., pre_build_callback=enrich)
view = strategy.build(dfs={"main": df}, tr=tr, theme=theme)
# The figure title and view.description both reflect the callback mutations