Skip to content

Commit 79f1839

Browse files
committed
feat: ag2 simplified beautiful-chat — A2UI Dynamic + Open Generative UI in one cell
Simplified port of the langgraph-python flagship beautiful-chat. The canonical reference combines THREE features (A2UI Dynamic + Open Generative UI + MCP Apps); the AG2 port ships the first two together in a single dedicated runtime so users can see them coexist. MCP is left to the existing /demos/mcp-apps cell, and the canonical's app-mode toggle / todos canvas is intentionally out of scope. Components: - src/agents/beautiful_chat.py — ConversableAgent that owns generate_a2ui explicitly (mirrors a2ui_dynamic.py); system prompt steers the LLM toward generate_a2ui for catalog-bound visuals and generateSandboxedUi for free-form / educational visualisations. - src/agent_server.py — mounted at /beautiful-chat. - src/app/api/copilotkit-beautiful-chat/route.ts — dedicated runtime with a2ui.injectA2UITool: false (agent owns the tool) and openGenerativeUI.agents: ['beautiful-chat']. Both flags scoped to this cell so they don't leak into the shared /api/copilotkit endpoint. - src/app/demos/beautiful-chat/page.tsx — single CopilotKit provider with both a2ui catalog and openGenerativeUI design skill; reuses the catalog from /demos/declarative-gen-ui to avoid duplication. - src/app/demos/beautiful-chat/README.md — per-demo doc. Manifest: added beautiful-chat to features list and demos section.
1 parent 057954a commit 79f1839

5 files changed

Lines changed: 420 additions & 0 deletions

File tree

showcase/integrations/ag2/src/agent_server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from agents.a2ui_dynamic import a2ui_dynamic_app
2323
from agents.a2ui_fixed import a2ui_fixed_app
2424
from agents.agent_config_agent import agent_config_app
25+
from agents.beautiful_chat import beautiful_chat_app
2526
from agents.byoc_hashbrown_agent import byoc_hashbrown_app
2627
from agents.byoc_json_render_agent import byoc_json_render_app
2728
from agents.headless_complete import headless_complete_app
@@ -73,6 +74,7 @@ async def dispatch(self, request, call_next):
7374
app.mount("/headless-complete", headless_complete_app)
7475
app.mount("/declarative-gen-ui", a2ui_dynamic_app)
7576
app.mount("/a2ui-fixed-schema", a2ui_fixed_app)
77+
app.mount("/beautiful-chat", beautiful_chat_app)
7678
app.mount("/mcp-apps", mcp_apps_app)
7779
app.mount("/open-gen-ui", open_gen_ui_app)
7880
app.mount("/open-gen-ui-advanced", open_gen_ui_advanced_app)
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""AG2 agent for the simplified Beautiful Chat demo.
2+
3+
This is a SIMPLIFIED port of the langgraph-python `beautiful_chat` graph.
4+
The canonical version simultaneously exercises three big features:
5+
6+
1. A2UI Dynamic Schema (a `generate_a2ui` tool whose secondary LLM emits
7+
schema-validated component compositions).
8+
2. Open Generative UI (the runtime auto-registers `generateSandboxedUi`
9+
on the frontend; the agent calls it for richer free-form widgets).
10+
3. MCP Apps (an mcpApps server is mounted on the runtime; its tools and
11+
UI resources are surfaced to the agent).
12+
13+
For AG2 we ship the FIRST TWO surfaces in a single cell: A2UI dynamic
14+
generation for branded, component-bound visuals (KPIs, dashboards, status
15+
reports, simple charts) AND Open Generative UI for free-form / educational
16+
visualisations the catalog cannot express. We deliberately leave MCP out
17+
to keep the AG2 port focused — `/demos/mcp-apps` already covers MCP on
18+
its own.
19+
20+
The agent owns `generate_a2ui` explicitly (mirroring `a2ui_dynamic.py`).
21+
The runtime route at `src/app/api/copilotkit-beautiful-chat/route.ts`
22+
sets `a2ui.injectA2UITool: false` so the runtime doesn't double-bind a
23+
second A2UI tool, and turns on `openGenerativeUI` for this agent so the
24+
runtime injects `generateSandboxedUi` on the frontend.
25+
"""
26+
27+
from __future__ import annotations
28+
29+
import json
30+
import os
31+
from typing import Annotated
32+
33+
import openai
34+
from autogen import ConversableAgent, LLMConfig
35+
from autogen.ag_ui import AGUIStream
36+
from fastapi import FastAPI
37+
38+
from tools import (
39+
build_a2ui_operations_from_tool_call,
40+
RENDER_A2UI_TOOL_SCHEMA,
41+
)
42+
43+
44+
SYSTEM_PROMPT = """You are the Beautiful Chat assistant — a CopilotKit
45+
showcase agent that answers user questions with rich, branded visuals.
46+
47+
You have TWO complementary visual surfaces. Pick whichever fits the
48+
request best, but ALWAYS render something visual rather than replying
49+
with plain text when the question warrants it.
50+
51+
1. `generate_a2ui` — for STRUCTURED, branded visuals composed from a
52+
registered React catalog. Use it for:
53+
- KPI dashboards (Metric + Card + Row/Column layouts)
54+
- Status reports (StatusBadge / Card)
55+
- Pie charts of part-of-whole breakdowns (PieChart)
56+
- Bar charts comparing categories (BarChart)
57+
- Info panels and quick summaries
58+
59+
Pass a single `context` argument summarising the conversation; the
60+
secondary LLM will design the composition against the registered
61+
catalog (Card, StatusBadge, Metric, InfoRow, PrimaryButton,
62+
PieChart, BarChart, plus the basic A2UI primitives).
63+
64+
2. `generateSandboxedUi` — auto-registered by the frontend when Open
65+
Generative UI is enabled. Use it for FREE-FORM visualisations the
66+
catalog cannot express:
67+
- Educational visualisations (algorithm walkthroughs, neural-net
68+
activations, geometric proofs, physics simulations)
69+
- Custom illustrations / diagrams
70+
- Anything intricate that needs inline SVG, CSS animation, or an
71+
interactive sandboxed widget
72+
73+
Output `initialHeight` (typically 480-560), a short
74+
`placeholderMessages` array, complete `css`, then `html` with inline
75+
SVG. No fetch / XHR / localStorage.
76+
77+
Decision rule of thumb: if the request maps to a chart, dashboard,
78+
status report, or KPI summary, prefer `generate_a2ui`. If it asks for a
79+
diagram, animation, or anything outside the catalog's components,
80+
prefer `generateSandboxedUi`. Either way, keep the chat reply to one
81+
short sentence — let the visual do the talking.
82+
"""
83+
84+
85+
async def generate_a2ui(
86+
context: Annotated[str, "Conversation context summary the secondary LLM should design UI from"],
87+
) -> str:
88+
"""Generate dynamic A2UI components based on the conversation.
89+
90+
Mirrors `a2ui_dynamic.py`: a secondary LLM is bound to the
91+
`render_a2ui` tool with `tool_choice` forced, and the resulting
92+
arguments are wrapped into an `a2ui_operations` container the
93+
runtime A2UI middleware detects and forwards to the frontend.
94+
"""
95+
client = openai.OpenAI()
96+
response = client.chat.completions.create(
97+
model="gpt-4.1",
98+
messages=[
99+
{
100+
"role": "system",
101+
"content": context or "Generate a useful dashboard UI.",
102+
},
103+
{
104+
"role": "user",
105+
"content": "Generate a dynamic A2UI dashboard based on the conversation.",
106+
},
107+
],
108+
tools=[{
109+
"type": "function",
110+
"function": RENDER_A2UI_TOOL_SCHEMA,
111+
}],
112+
tool_choice={"type": "function", "function": {"name": "render_a2ui"}},
113+
)
114+
115+
choice = response.choices[0]
116+
if choice.message.tool_calls:
117+
args = json.loads(choice.message.tool_calls[0].function.arguments)
118+
result = build_a2ui_operations_from_tool_call(args)
119+
return json.dumps(result)
120+
121+
return json.dumps({"error": "LLM did not call render_a2ui"})
122+
123+
124+
agent = ConversableAgent(
125+
name="beautiful_chat_assistant",
126+
system_message=SYSTEM_PROMPT,
127+
llm_config=LLMConfig({"model": "gpt-4.1", "stream": True}),
128+
human_input_mode="NEVER",
129+
# The agent may call generate_a2ui (its own backend tool) and
130+
# generateSandboxedUi (frontend tool injected by the OGUI runtime
131+
# middleware). Cap the loop to keep tool storms bounded.
132+
max_consecutive_auto_reply=8,
133+
functions=[generate_a2ui],
134+
)
135+
136+
stream = AGUIStream(agent)
137+
beautiful_chat_app = FastAPI()
138+
beautiful_chat_app.mount("", stream.build_asgi())
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Dedicated runtime for the (simplified) Beautiful Chat showcase cell.
2+
//
3+
// Beautiful Chat combines TWO of the canonical reference's three flagship
4+
// features in a single cell:
5+
// - A2UI Dynamic Schema (branded React catalog, agent-owned `generate_a2ui`)
6+
// - Open Generative UI (auto-injected `generateSandboxedUi` frontend tool)
7+
//
8+
// Splitting into its own endpoint matters because:
9+
// - `openGenerativeUI` flips a global probe flag that, on the shared
10+
// `/api/copilotkit` route, would wipe per-cell `useFrontendTool` /
11+
// `useComponent` registrations (see comment in `copilotkit-ogui/route.ts`).
12+
// - `a2ui.injectA2UITool: false` is required so the runtime doesn't
13+
// double-bind a second A2UI tool over the agent-owned `generate_a2ui`.
14+
//
15+
// References:
16+
// - showcase/integrations/langgraph-python/src/app/api/copilotkit-beautiful-chat/route.ts
17+
// - src/app/api/copilotkit-declarative-gen-ui/route.ts (a2ui scoping pattern)
18+
// - src/app/api/copilotkit-ogui/route.ts (openGenerativeUI scoping pattern)
19+
20+
import { NextRequest, NextResponse } from "next/server";
21+
import {
22+
CopilotRuntime,
23+
ExperimentalEmptyAdapter,
24+
copilotRuntimeNextJSAppRouterEndpoint,
25+
} from "@copilotkit/runtime";
26+
import { HttpAgent } from "@ag-ui/client";
27+
28+
const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000";
29+
30+
const beautifulChatAgent = new HttpAgent({
31+
url: `${AGENT_URL}/beautiful-chat/`,
32+
});
33+
34+
const runtime = new CopilotRuntime({
35+
// @ts-ignore -- see main route.ts
36+
agents: { "beautiful-chat": beautifulChatAgent },
37+
// The agent owns `generate_a2ui` explicitly (see
38+
// src/agents/beautiful_chat.py). The runtime middleware still serialises
39+
// the registered client catalog into agent context and detects
40+
// `a2ui_operations` containers in the tool result; it just must NOT bind
41+
// a second A2UI tool on top.
42+
a2ui: {
43+
injectA2UITool: false,
44+
},
45+
// Turn on Open Generative UI for this agent. The runtime middleware
46+
// injects `generateSandboxedUi` as a frontend tool the LLM can call,
47+
// and converts streaming tool-call deltas into `open-generative-ui`
48+
// activity events the built-in renderer mounts in a sandboxed iframe.
49+
openGenerativeUI: {
50+
agents: ["beautiful-chat"],
51+
},
52+
});
53+
54+
export const POST = async (req: NextRequest) => {
55+
try {
56+
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
57+
endpoint: "/api/copilotkit-beautiful-chat",
58+
serviceAdapter: new ExperimentalEmptyAdapter(),
59+
runtime,
60+
});
61+
return await handleRequest(req);
62+
} catch (error: unknown) {
63+
const e = error as { message?: string; stack?: string };
64+
return NextResponse.json(
65+
{ error: e.message, stack: e.stack },
66+
{ status: 500 },
67+
);
68+
}
69+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Beautiful Chat (Simplified — AG2)
2+
3+
## What This Demo Shows
4+
5+
A simplified port of the langgraph-python `beautiful-chat` flagship cell.
6+
The canonical reference combines THREE big features on a single runtime
7+
(A2UI Dynamic + Open Generative UI + MCP Apps); the AG2 port ships the
8+
first two together in a single cell so you can see them coexist:
9+
10+
- **A2UI Dynamic Schema** — the agent calls `generate_a2ui` to render
11+
branded compositions from a registered React catalog (Card,
12+
StatusBadge, Metric, InfoRow, PieChart, BarChart, plus the basic A2UI
13+
primitives).
14+
- **Open Generative UI** — the runtime auto-injects
15+
`generateSandboxedUi` as a frontend tool, and the agent picks it for
16+
free-form visuals the catalog cannot express (educational
17+
illustrations, algorithm walkthroughs, animations).
18+
19+
MCP Apps is NOT bundled here — see `/demos/mcp-apps` for a dedicated
20+
MCP cell. Keeping MCP separate keeps the AG2 port focused.
21+
22+
## How to Interact
23+
24+
Try asking the agent for both kinds of visuals:
25+
26+
**A2UI (catalog-bound):**
27+
28+
- "Show me a quick KPI dashboard with 3-4 metrics."
29+
- "Show a pie chart of sales by region."
30+
- "Render a bar chart of quarterly revenue."
31+
- "Give me a status report on system health."
32+
33+
**Open Generative UI (free-form):**
34+
35+
- "Animate how a simple feed-forward neural network processes an input."
36+
- "Visualize quicksort on ~10 bars of varying heights."
37+
- "Show a 3D axis visualisation with pitch/yaw/roll labels."
38+
39+
The agent's system prompt (`src/agents/beautiful_chat.py`) has a
40+
decision rule: structured / catalog-shaped requests go through
41+
`generate_a2ui`; everything else goes through `generateSandboxedUi`.
42+
43+
## Technical Details
44+
45+
- **Frontend page:** `./page.tsx` — single combined `<CopilotKit>` with
46+
`a2ui={{ catalog: myCatalog }}` AND
47+
`openGenerativeUI={{ designSkill: ... }}`.
48+
- **Frontend catalog:** imported directly from
49+
`../declarative-gen-ui/a2ui/catalog` — no duplicate copy. That same
50+
catalog backs the dedicated `/demos/declarative-gen-ui` cell.
51+
- **Backend agent:** `src/agents/beautiful_chat.py` — owns
52+
`generate_a2ui` explicitly (mirrors `a2ui_dynamic.py`); the system
53+
prompt steers the LLM toward `generate_a2ui` for structured outputs
54+
and `generateSandboxedUi` for free-form visuals.
55+
- **Backend mount:** `/beautiful-chat` in `src/agent_server.py`.
56+
- **Runtime route:** `src/app/api/copilotkit-beautiful-chat/route.ts`
57+
dedicated endpoint with `a2ui.injectA2UITool: false` (agent owns the
58+
tool) and `openGenerativeUI.agents: ["beautiful-chat"]`. Both flags
59+
are scoped to this cell so they don't leak into the shared
60+
`/api/copilotkit` endpoint.
61+
62+
## Reference
63+
64+
- Canonical (langgraph-python):
65+
`showcase/integrations/langgraph-python/src/app/demos/beautiful-chat/`
66+
- A2UI: https://docs.copilotkit.ai/integrations/langgraph/generative-ui/a2ui
67+
- Open Generative UI: https://docs.copilotkit.ai/generative-ui/open-generative-ui
68+
69+
## What's NOT Ported From the Canonical Reference
70+
71+
- **MCP Apps** — covered separately by `/demos/mcp-apps`.
72+
- **App-mode toggle** (todos canvas, mode-toggle, theme provider,
73+
layout-level chrome) — out of scope for the simplified port; the AG2
74+
cell focuses on the dual-surface chat.
75+
- **A2UI Fixed Schema** inside the same cell — covered separately by
76+
`/demos/a2ui-fixed-schema`.

0 commit comments

Comments
 (0)