forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patha2ui_dynamic.py
More file actions
133 lines (107 loc) · 4.76 KB
/
Copy patha2ui_dynamic.py
File metadata and controls
133 lines (107 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
LangGraph agent for the Declarative Generative UI (A2UI — Dynamic Schema) demo.
Pattern (ported from the canonical
`examples/integrations/langgraph-python/agent/src/a2ui_dynamic_schema.py`):
- The agent binds an explicit `generate_a2ui` tool. When called, `generate_a2ui`
invokes a secondary LLM bound to `render_a2ui` (tool_choice forced) with the
registered client catalog injected as `copilotkit.context`.
- The tool result returns an `a2ui_operations` container which the A2UI
middleware detects in the tool-call result and forwards to the frontend
renderer.
- The runtime (see `src/app/api/copilotkit-declarative-gen-ui/route.ts`) uses
`injectA2UITool: false` because the tool binding is owned by the agent here
(double-injection would duplicate the tool slot).
This mirrors `beautiful_chat.py` which exercises the same pattern for the
flagship combined cell — the pattern is confirmed working there (Pie Chart
click renders a real styled doughnut).
Reference:
examples/integrations/langgraph-python/agent/src/a2ui_dynamic_schema.py
"""
from __future__ import annotations
import json
from typing import Any
from copilotkit import CopilotKitMiddleware, a2ui
from langchain.agents import create_agent
from langchain.tools import ToolRuntime, tool
from langchain_core.messages import SystemMessage
from langchain_core.tools import tool as lc_tool
from langchain_openai import ChatOpenAI
CUSTOM_CATALOG_ID = "declarative-gen-ui-catalog"
@lc_tool
def render_a2ui(
surfaceId: str,
catalogId: str,
components: list[dict],
data: dict | None = None,
) -> str:
"""Render a dynamic A2UI v0.9 surface.
Args:
surfaceId: Unique surface identifier.
catalogId: The catalog ID (use "declarative-gen-ui-catalog").
components: A2UI v0.9 component array (flat format). The root
component must have id "root".
data: Optional initial data model for the surface.
"""
return "rendered"
@tool()
def generate_a2ui(runtime: ToolRuntime[Any]) -> str:
"""Generate dynamic A2UI components based on the conversation.
A secondary LLM designs the UI schema and data. The result is returned as
an `a2ui_operations` container for the A2UI middleware to detect and
forward to the frontend renderer.
"""
messages = runtime.state["messages"][:-1]
# Pull the A2UI component schema + usage guidelines from the runtime's
# `copilotkit.context` (the runtime injects them automatically when the
# frontend registers a catalog via `<CopilotKit a2ui={{ catalog }}>`).
context_entries = runtime.state.get("copilotkit", {}).get("context", [])
context_text = "\n\n".join(
entry.get("value", "")
for entry in context_entries
if isinstance(entry, dict) and entry.get("value")
)
model = ChatOpenAI(model="gpt-4.1")
model_with_tool = model.bind_tools(
[render_a2ui],
tool_choice="render_a2ui",
)
response = model_with_tool.invoke(
[SystemMessage(content=context_text), *messages],
)
if not response.tool_calls:
return json.dumps({"error": "LLM did not call render_a2ui"})
tool_call = response.tool_calls[0]
args = tool_call["args"]
surface_id = args.get("surfaceId", "dynamic-surface")
catalog_id = args.get("catalogId", CUSTOM_CATALOG_ID)
components = args.get("components", [])
data = args.get("data", {})
ops = [
a2ui.create_surface(surface_id, catalog_id=catalog_id),
a2ui.update_components(surface_id, components),
]
if data:
ops.append(a2ui.update_data_model(surface_id, data))
return a2ui.render(operations=ops)
SYSTEM_PROMPT = (
"You are a demo assistant for Declarative Generative UI (A2UI — Dynamic "
"Schema). Whenever a response would benefit from a rich visual — a "
"dashboard, status report, KPI summary, card layout, info grid, a "
"pie/donut chart of part-of-whole breakdowns, a bar chart comparing "
"values across categories, or anything more structured than plain text — "
"call `generate_a2ui` to draw it. The registered catalog includes "
"`Card`, `StatusBadge`, `Metric`, `InfoRow`, `PrimaryButton`, `PieChart`, "
"and `BarChart` (in addition to the basic A2UI primitives). Prefer "
"`PieChart` for part-of-whole breakdowns (sales by region, traffic "
"sources, portfolio allocation) and `BarChart` for comparisons across "
"categories (quarterly revenue, headcount by team, signups per month). "
"`generate_a2ui` takes no arguments and handles the rendering "
"automatically. Keep chat replies to one short sentence; let the UI do "
"the talking."
)
graph = create_agent(
model=ChatOpenAI(model="gpt-4.1"),
tools=[generate_a2ui],
middleware=[CopilotKitMiddleware()],
system_prompt=SYSTEM_PROMPT,
)