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
129 lines (111 loc) · 4.58 KB
/
Copy patha2ui_dynamic.py
File metadata and controls
129 lines (111 loc) · 4.58 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
"""
MS Agent Framework agent for the Declarative Generative UI (A2UI — Dynamic Schema) demo.
Pattern (ported from the LangGraph reference
`showcase/integrations/langgraph-python/src/agents/a2ui_dynamic.py`):
- The agent binds an explicit `generate_a2ui` tool. When called, it invokes a
secondary LLM bound to `render_a2ui` (tool_choice forced) and returns the
resulting `a2ui_operations` container.
- 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).
"""
from __future__ import annotations
import json
from textwrap import dedent
from typing import Annotated
from agent_framework import Agent, BaseChatClient, tool
from agent_framework_ag_ui import AgentFrameworkAgent
from pydantic import Field
from tools import build_a2ui_operations_from_tool_call
CUSTOM_CATALOG_ID = "declarative-gen-ui-catalog"
@tool(
name="generate_a2ui",
description=(
"Generate dynamic A2UI components based on the conversation. "
"A secondary LLM designs the UI schema and data."
),
)
def generate_a2ui(
context: Annotated[
str,
Field(description="Conversation context to generate UI from."),
],
) -> str:
"""Generate dynamic A2UI dashboard from conversation context."""
from openai import OpenAI
client = OpenAI()
tool_schema = {
"type": "function",
"function": {
"name": "render_a2ui",
"description": "Render a dynamic A2UI v0.9 surface.",
"parameters": {
"type": "object",
"properties": {
"surfaceId": {"type": "string"},
"catalogId": {"type": "string"},
"components": {"type": "array", "items": {"type": "object"}},
"data": {"type": "object"},
},
"required": ["surfaceId", "catalogId", "components"],
},
},
}
response = client.chat.completions.create(
model="gpt-4.1",
messages=[
{
"role": "system",
"content": (
context
or f"Generate a useful dashboard UI. Use catalogId='{CUSTOM_CATALOG_ID}'."
),
},
{
"role": "user",
"content": "Generate a dynamic A2UI dashboard based on the conversation.",
},
],
tools=[tool_schema],
tool_choice={"type": "function", "function": {"name": "render_a2ui"}},
)
if not response.choices[0].message.tool_calls:
return json.dumps({"error": "LLM did not call render_a2ui"})
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
# Default the catalog to the dynamic-gen-ui catalog if the LLM omitted it.
args.setdefault("catalogId", CUSTOM_CATALOG_ID)
result = build_a2ui_operations_from_tool_call(args)
return json.dumps(result)
SYSTEM_PROMPT = dedent(
"""
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 a `context` string summarising the user's request
and handles the rendering automatically. Keep chat replies to one short
sentence; let the UI do the talking.
"""
).strip()
def create_agent(chat_client: BaseChatClient) -> AgentFrameworkAgent:
"""Instantiate the MS-Agent-backed declarative-gen-ui agent."""
base_agent = Agent(
client=chat_client,
name="declarative_gen_ui_agent",
instructions=SYSTEM_PROMPT,
tools=[generate_a2ui],
)
return AgentFrameworkAgent(
agent=base_agent,
name="CopilotKitMicrosoftAgentFrameworkAgent",
description="Dynamic A2UI generator that designs rich UI surfaces on demand.",
require_confirmation=False,
)