From 3f667246be4f54230eeab6f56a873048f03debcb Mon Sep 17 00:00:00 2001 From: Brian Love Date: Mon, 18 May 2026 12:57:25 -0700 Subject: [PATCH] fix(c-generative-ui): three demo-polish followups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. render_spec(elements, root) instead of render_spec(spec) — matches what gpt-5 reasoning='minimal' naturally emits; eliminates the visible Pydantic 'spec: Field required' retry artifact in the chat. 2. Tighten prompt: explicit 'if render_spec and the data tools have already been called this turn, you are DONE' — stops the double-loop where gpt-5 re-runs all tools on the second iteration. 3. Welcome chip text now matches the aviation backend ('Airline operations dashboard' / 'Filter to cancelled flights' instead of the off-theme 'Q3 sales dashboard' / 'contact form'). Mirrors to umbrella graph + prompt per the full-copy policy. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/app/generative-ui.component.ts | 4 ++-- .../generative-ui/python/prompts/dashboard.md | 4 ++-- .../chat/generative-ui/python/src/graph.py | 22 +++++++++---------- .../streaming/python/prompts/dashboard.md | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts b/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts index bce0c778..b0560299 100644 --- a/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts +++ b/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts @@ -22,8 +22,8 @@ const dashboardViews = views({ }); const WELCOME_SUGGESTIONS = [ - { label: 'Render a dashboard', value: 'Show me a Q3 sales dashboard with three metrics.' }, - { label: 'Render a form', value: 'Create a contact form with name, email, and message.' }, + { label: 'Airline operations dashboard', value: 'Show me a dashboard of airline operations.' }, + { label: 'Filter to cancelled flights', value: 'Filter to only the cancelled flights.' }, ] as const; @Component({ diff --git a/cockpit/chat/generative-ui/python/prompts/dashboard.md b/cockpit/chat/generative-ui/python/prompts/dashboard.md index 33fe88dd..19e7d966 100644 --- a/cockpit/chat/generative-ui/python/prompts/dashboard.md +++ b/cockpit/chat/generative-ui/python/prompts/dashboard.md @@ -2,7 +2,7 @@ You are a dashboard agent that builds interactive airline-operations KPI dashboards. You have five tools: -- `render_spec(spec)` — Author or update the dashboard layout. The spec is a JSON object describing component types, props, children, and state bindings. See the schema below. +- `render_spec(elements, root)` — Author or update the dashboard layout. `elements` is a dict keyed by component id (each value has `type`, optional `props`, optional `children`); `root` is the id of the top-level component. See the schema below. - `query_airline_kpis()` — Snapshot of operational KPIs: on-time %, flights today, avg delay, load factor. - `query_on_time_trend(months=12)` — On-time performance per month, for the line chart. - `query_flights_by_airline(airlines=None)` — Daily flight counts per airline, for the bar chart. @@ -14,7 +14,7 @@ You are a dashboard agent that builds interactive airline-operations KPI dashboa 1. Call `render_spec` ONCE with a complete dashboard layout — stat cards, charts, table — using `$state` bindings to the slots the data tools populate (see "State Path Conventions" below). 2. In the SAME turn (same tool_calls array), call EACH data tool that backs a component in your spec. Do NOT call tools whose data your spec doesn't reference. -3. After the tools return, return WITHOUT any further tool calls. A separate node will write a brief conversational summary. +3. After the tools return, return WITHOUT any further tool calls. A separate node will write a brief conversational summary. **Critical: if `render_spec` and the data tools have already been called this turn, you are DONE. Return with no tool_calls. Do NOT call `render_spec` again. Do NOT re-call the data tools.** ### When the dashboard exists (follow-up turn) diff --git a/cockpit/chat/generative-ui/python/src/graph.py b/cockpit/chat/generative-ui/python/src/graph.py index 39b4ffdf..0be503c1 100644 --- a/cockpit/chat/generative-ui/python/src/graph.py +++ b/cockpit/chat/generative-ui/python/src/graph.py @@ -38,27 +38,27 @@ class DashboardState(MessagesState): @tool -async def render_spec(spec: dict) -> str: - """Render an interactive dashboard layout from a JSON spec. +async def render_spec(elements: dict, root: str) -> str: + """Render an interactive dashboard layout. - Use this tool to author or update the dashboard layout. The spec is a - JSON object with `elements` (a dict keyed by component id) and `root` - (the id of the top-level component). See the system prompt for the full - schema and component catalog. + Use this tool to author or update the dashboard layout. See the system + prompt for the full component catalog and state binding conventions. - Call this tool FIRST on any turn where the layout needs to be created - or restructured. After calling render_spec, call the data tools needed - to populate the components you authored. + Call this tool AT MOST ONCE per turn — only when the layout needs to + be created (first turn) or restructured (follow-up structural change). + Do NOT call it again to refresh data; the data tools handle that. Args: - spec: The dashboard JSON render spec. + elements: Dict keyed by component id. Each value has `type`, optional + `props`, and optional `children` (list of component ids). + root: The id of the top-level component (must be a key in `elements`). Returns: The spec serialized as JSON. A post-process node (wrap_spec_into_ai) wraps this payload into the AI message content where the chat-lib's content-classifier picks it up. """ - return json.dumps(spec) + return json.dumps({"elements": elements, "root": root}) _ALL_TOOLS = [render_spec, *_DATA_TOOLS] diff --git a/cockpit/langgraph/streaming/python/prompts/dashboard.md b/cockpit/langgraph/streaming/python/prompts/dashboard.md index 33fe88dd..19e7d966 100644 --- a/cockpit/langgraph/streaming/python/prompts/dashboard.md +++ b/cockpit/langgraph/streaming/python/prompts/dashboard.md @@ -2,7 +2,7 @@ You are a dashboard agent that builds interactive airline-operations KPI dashboards. You have five tools: -- `render_spec(spec)` — Author or update the dashboard layout. The spec is a JSON object describing component types, props, children, and state bindings. See the schema below. +- `render_spec(elements, root)` — Author or update the dashboard layout. `elements` is a dict keyed by component id (each value has `type`, optional `props`, optional `children`); `root` is the id of the top-level component. See the schema below. - `query_airline_kpis()` — Snapshot of operational KPIs: on-time %, flights today, avg delay, load factor. - `query_on_time_trend(months=12)` — On-time performance per month, for the line chart. - `query_flights_by_airline(airlines=None)` — Daily flight counts per airline, for the bar chart. @@ -14,7 +14,7 @@ You are a dashboard agent that builds interactive airline-operations KPI dashboa 1. Call `render_spec` ONCE with a complete dashboard layout — stat cards, charts, table — using `$state` bindings to the slots the data tools populate (see "State Path Conventions" below). 2. In the SAME turn (same tool_calls array), call EACH data tool that backs a component in your spec. Do NOT call tools whose data your spec doesn't reference. -3. After the tools return, return WITHOUT any further tool calls. A separate node will write a brief conversational summary. +3. After the tools return, return WITHOUT any further tool calls. A separate node will write a brief conversational summary. **Critical: if `render_spec` and the data tools have already been called this turn, you are DONE. Return with no tool_calls. Do NOT call `render_spec` again. Do NOT re-call the data tools.** ### When the dashboard exists (follow-up turn)