Skip to content

Commit 5a0fe4c

Browse files
authored
Merge pull request #48 from CopilotKit/feat/deep-agents
Upgrade to LangChain deep agents
2 parents bd8a83b + b1ef752 commit 5a0fe4c

File tree

8 files changed

+89
-73
lines changed

8 files changed

+89
-73
lines changed

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Open Generative UI
22

3-
An open-source showcase for building rich, interactive AI-generated UI with [CopilotKit](https://copilotkit.ai) and [LangGraph](https://langchain-ai.github.io/langgraph/). Ask the agent to visualize algorithms, create 3D animations, render charts, or generate interactive diagrams — all rendered as live HTML/SVG inside a sandboxed iframe.
3+
An open-source showcase for building rich, interactive AI-generated UI with [CopilotKit](https://copilotkit.ai) and [LangChain Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview). Ask the agent to visualize algorithms, create 3D animations, render charts, or generate interactive diagrams — all rendered as live HTML/SVG inside a sandboxed iframe.
44

55
https://github.com/user-attachments/assets/ed28c734-e54e-4412-873f-4801da544a7f
66

@@ -104,14 +104,27 @@ Turborepo monorepo with three packages:
104104
```
105105
apps/
106106
├── app/ Next.js 16 frontend (CopilotKit v2, React 19, Tailwind 4)
107-
├── agent/ LangGraph Python agent (GPT-5.4, CopilotKit middleware)
107+
├── agent/ Deep Agent (deepagents + CopilotKit middleware, skills-based)
108108
└── mcp/ Standalone MCP server (design system + skills + document assembler)
109109
```
110110

111+
### Deep Agent + Skills
112+
113+
The agent backend uses [LangChain Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview) (`create_deep_agent`) with a skills-based architecture. Instead of injecting all visualization instructions into the system prompt, skills are defined as `SKILL.md` files in `apps/agent/skills/` and loaded on-demand via progressive disclosure:
114+
115+
```
116+
apps/agent/skills/
117+
├── advanced-visualization/SKILL.md # UI mockups, dashboards, Chart.js, generative art
118+
├── master-playbook/SKILL.md # Response philosophy, decision trees, narration patterns
119+
└── svg-diagrams/SKILL.md # SVG generation rules, component patterns, diagram types
120+
```
121+
122+
Deep agents also provide built-in planning (`write_todos`), filesystem tools, and sub-agent support.
123+
111124
### How It Works
112125

113126
1. **User sends a prompt** via the CopilotKit chat UI
114-
2. **Agent decides** whether to respond with text, call a tool, or render a visual component
127+
2. **Deep agent decides** whether to respond with text, call a tool, or render a visual component — consulting relevant skills as needed
115128
3. **`widgetRenderer`** — a frontend `useComponent` hook — receives the agent's HTML and renders it in a sandboxed iframe
116129
4. **Skeleton loading** shows while the iframe loads, then content fades in smoothly
117130
5. **ResizeObserver** inside the iframe reports content height back to the parent for seamless auto-sizing
@@ -154,7 +167,7 @@ apps/
154167

155168
## Tech Stack
156169

157-
Next.js 16, React 19, Tailwind CSS 4, LangGraph, CopilotKit v2, Turborepo, Recharts
170+
Next.js 16, React 19, Tailwind CSS 4, LangChain Deep Agents, LangGraph, CopilotKit v2, Turborepo, Recharts
158171

159172
## License
160173

apps/agent/main.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,24 @@
44
"""
55

66
import os
7+
from pathlib import Path
78

89
from copilotkit import CopilotKitMiddleware
9-
from langchain.agents import create_agent
10+
from deepagents import create_deep_agent
1011
from langchain_openai import ChatOpenAI
1112

1213
from src.query import query_data
1314
from src.todos import AgentState, todo_tools
1415
from src.form import generate_form
1516
from src.templates import template_tools
16-
from skills import load_all_skills
1717

18-
# Load all visualization skills
19-
_skills_text = load_all_skills()
20-
21-
agent = create_agent(
18+
agent = create_deep_agent(
2219
model=ChatOpenAI(model=os.environ.get("LLM_MODEL", "gpt-5.4-2026-03-05")),
2320
tools=[query_data, *todo_tools, generate_form, *template_tools],
2421
middleware=[CopilotKitMiddleware()],
25-
state_schema=AgentState,
26-
system_prompt=f"""
22+
context_schema=AgentState,
23+
skills=[str(Path(__file__).parent / "skills")],
24+
system_prompt="""
2725
You are a helpful assistant that helps users understand CopilotKit and LangGraph used together.
2826
2927
Be brief in your explanations of CopilotKit and LangGraph, 1 to 2 sentences.
@@ -47,10 +45,6 @@
4745
- Pre-styled form elements (buttons, inputs, sliders look native automatically)
4846
- Pre-built SVG CSS classes for color ramps (.c-purple, .c-teal, .c-blue, etc.)
4947
50-
Follow the skills below for how to produce high-quality visuals:
51-
52-
{_skills_text}
53-
5448
## UI Templates
5549
5650
Users can save generated UIs as reusable templates and apply them later.

apps/agent/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ dependencies = [
1616
"copilotkit>=0.1.77",
1717
"langgraph-api>=0.7.16",
1818
"langchain-mcp-adapters>=0.2.1",
19+
"deepagents>=0.1.0",
1920
]

apps/agent/skills/__init__.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

apps/agent/skills/agent-skills-vol2.txt renamed to apps/agent/skills/advanced-visualization/SKILL.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
name: "Advanced Visualization Techniques"
3+
description: "UI mockups, dashboards, advanced interactivity, generative art, simulations, math visualizations, and design system rules for producing rich HTML widget output."
4+
allowed-tools: []
5+
---
6+
17
# Agent Visualization Skills — Volume 2: Advanced Techniques
28

39
Prerequisite: Volume 1 (SVG diagrams, basic interactive widgets, Chart.js, Mermaid).
@@ -427,7 +433,7 @@ hardcoded hex values.
427433
```
428434
- Height goes on the wrapper div ONLY, never on canvas.
429435
- Always set `responsive: true, maintainAspectRatio: false`.
430-
- For horizontal bar charts: height = (bars × 40) + 80 pixels.
436+
- For horizontal bar charts: height = (bars x 40) + 80 pixels.
431437

432438
### Custom Legend (Always Use This)
433439
Disable Chart.js default legend and build HTML:
@@ -450,7 +456,7 @@ plugins: { legend: { display: false } }
450456
```
451457

452458
### Dashboard Layout
453-
Metric cards on top chart below sendPrompt for drill-down:
459+
Metric cards on top -> chart below -> sendPrompt for drill-down:
454460
```html
455461
<!-- Metric cards grid -->
456462
<div style="display: grid;

apps/agent/skills/master-agent-playbook.txt renamed to apps/agent/skills/master-playbook/SKILL.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
name: "Master Agent Playbook"
3+
description: "Philosophy, decision-making framework, and technical skills for delivering visual, interactive, and educational AI responses."
4+
allowed-tools: []
5+
---
6+
17
# Master Agent Playbook: Making AI Responses Extraordinary
28

39
This playbook teaches an AI coding agent how to go beyond plain text and deliver
@@ -22,20 +28,20 @@ The principle: **Show, don't just tell.** Before writing any response, ask:
2228

2329
```
2430
User asks a question
25-
26-
├─ Is it a quick factual answer? Answer in 1-2 sentences.
27-
28-
├─ Is it conceptual / "how does X work"?
29-
├─ Is it spatial or visual? SVG illustrative diagram
30-
├─ Is it a process/flow? SVG flowchart or HTML stepper
31-
├─ Is it data-driven? Interactive chart (Chart.js / Recharts)
32-
└─ Is it abstract but explorable? Interactive HTML widget with controls
33-
34-
├─ Is it "build me X"? Working code artifact, fully functional
35-
36-
├─ Is it a comparison? Side-by-side table or comparative visual
37-
38-
└─ Is it emotional/personal? Warm text response. No visuals needed.
31+
|
32+
+- Is it a quick factual answer? -> Answer in 1-2 sentences.
33+
|
34+
+- Is it conceptual / "how does X work"?
35+
| +- Is it spatial or visual? -> SVG illustrative diagram
36+
| +- Is it a process/flow? -> SVG flowchart or HTML stepper
37+
| +- Is it data-driven? -> Interactive chart (Chart.js / Recharts)
38+
| +- Is it abstract but explorable? -> Interactive HTML widget with controls
39+
|
40+
+- Is it "build me X"? -> Working code artifact, fully functional
41+
|
42+
+- Is it a comparison? -> Side-by-side table or comparative visual
43+
|
44+
+- Is it emotional/personal? -> Warm text response. No visuals needed.
3945
```
4046

4147
### The 3-Layer Response Pattern
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
name: "SVG Diagram Generation"
3+
description: "Generating rich inline SVG diagrams to visually explain systems, processes, architectures, and abstract concepts."
4+
allowed-tools: []
5+
---
6+
17
# SVG Diagram Generation Skill
28

39
You can generate rich, inline SVG diagrams to visually explain concepts. Use this skill whenever a visual would help the user understand a system, process, architecture, or mechanism better than text alone.
@@ -46,9 +52,9 @@ Always use this template:
4652
- **Sentence case always**. Never Title Case or ALL CAPS.
4753

4854
### Text Width Estimation
49-
At 14px, each character 8px wide. At 12px, each character 7px wide.
50-
- "Load Balancer" (13 chars) at 14px 104px needs rect 140px wide (with padding).
51-
- Always compute: `rect_width = max(title_chars × 8, subtitle_chars × 7) + 48px padding`.
55+
At 14px, each character ~ 8px wide. At 12px, each character ~ 7px wide.
56+
- "Load Balancer" (13 chars) at 14px ~ 104px -> needs rect ~ 140px wide (with padding).
57+
- Always compute: `rect_width = max(title_chars x 8, subtitle_chars x 7) + 48px padding`.
5258

5359
### Colors (Light/Dark Mode Safe)
5460
Use these semantic color sets that work in both modes:
@@ -169,7 +175,7 @@ If you're rendering inside a system that supports CSS variables, prefer:
169175

170176
1. **ViewBox height**: Find your lowest element (max y + height). Set H = that + 40px.
171177
2. **No content past x=640 or below y=(H-40)**.
172-
3. **Text fits in boxes**: `(char_count × 8) + 48 < rect_width` for 14px text.
178+
3. **Text fits in boxes**: `(char_count x 8) + 48 < rect_width` for 14px text.
173179
4. **No arrows through boxes**: Trace every line's path — if it crosses a rect, reroute.
174180
5. **All `<path>` connectors have `fill="none"`**.
175181
6. **All text has appropriate fill color** — never rely on inheritance (SVG defaults to black).
@@ -207,7 +213,7 @@ For complex topics, use multiple smaller SVGs instead of one dense one:
207213
<text x="340" y="56" text-anchor="middle" dominant-baseline="central"
208214
font-size="12" fill="#0F6E56">HTTP POST /api/data</text>
209215

210-
<!-- Arrow 12 -->
216+
<!-- Arrow 1->2 -->
211217
<line x1="340" y1="76" x2="340" y2="100" stroke="#534AB7"
212218
stroke-width="1.5" marker-end="url(#arrow)"/>
213219

@@ -219,7 +225,7 @@ For complex topics, use multiple smaller SVGs instead of one dense one:
219225
<text x="340" y="142" text-anchor="middle" dominant-baseline="central"
220226
font-size="12" fill="#534AB7">Validate and transform</text>
221227

222-
<!-- Arrow 23 -->
228+
<!-- Arrow 2->3 -->
223229
<line x1="340" y1="162" x2="340" y2="186" stroke="#854F0B"
224230
stroke-width="1.5" marker-end="url(#arrow)"/>
225231

apps/app/src/components/generative-ui/widget-renderer.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ window.addEventListener('message', function(e) {
369369
var scriptCloses = (rawHtml.match(/<\\/script>/gi) || []).length;
370370
var allScriptsClosed = scriptOpens <= scriptCloses;
371371
tmp.querySelectorAll('script').forEach(function(s) {
372-
incomingScripts.push({ src: s.src, text: s.textContent });
372+
incomingScripts.push({ src: s.src, text: s.textContent, type: s.type || '' });
373373
s.remove();
374374
});
375375
@@ -414,24 +414,36 @@ window.addEventListener('message', function(e) {
414414
content.innerHTML = tmp.innerHTML;
415415
}
416416
417-
// Execute only new scripts — skip entirely while a <script> tag is still streaming
417+
// Execute only new scripts — skip entirely while a <script> tag is still streaming.
418+
// External scripts (src) are loaded sequentially and we wait for each to finish
419+
// before running subsequent scripts, so inline code can use libraries like Three.js.
418420
if (allScriptsClosed) {
419-
incomingScripts.forEach(function(scriptInfo) {
421+
(function runScripts(scripts, idx) {
422+
if (idx >= scripts.length) return;
423+
var scriptInfo = scripts[idx];
420424
var key = scriptInfo.src || scriptInfo.text;
421-
if (!key || !key.trim()) return;
425+
if (!key || !key.trim()) { runScripts(scripts, idx + 1); return; }
422426
var hash = btoa(unescape(encodeURIComponent(key))).slice(0, 16).replace(/[^a-zA-Z0-9]/g, '');
423-
if (!hash || content.getAttribute('data-exec-' + hash)) return;
427+
if (!hash || content.getAttribute('data-exec-' + hash)) { runScripts(scripts, idx + 1); return; }
424428
content.setAttribute('data-exec-' + hash, '1');
425429
try {
426430
var newScript = document.createElement('script');
431+
if (scriptInfo.type) newScript.type = scriptInfo.type;
427432
if (scriptInfo.src) {
428433
newScript.src = scriptInfo.src;
434+
newScript.onload = function() { runScripts(scripts, idx + 1); };
435+
newScript.onerror = function() { runScripts(scripts, idx + 1); };
436+
content.appendChild(newScript);
429437
} else {
430438
newScript.textContent = scriptInfo.text;
439+
content.appendChild(newScript);
440+
runScripts(scripts, idx + 1);
431441
}
432-
content.appendChild(newScript);
433-
} catch(e) { console.warn('[widget] script exec failed:', e); }
434-
});
442+
} catch(e) {
443+
console.warn('[widget] script exec failed:', e);
444+
runScripts(scripts, idx + 1);
445+
}
446+
})(incomingScripts, 0);
435447
}
436448
reportHeight();
437449
}
@@ -469,7 +481,11 @@ function assembleShell(initialHtml: string = ""): string {
469481
style-src 'unsafe-inline';
470482
img-src 'self' data: blob:;
471483
font-src 'self' data:;
472-
connect-src 'self';
484+
connect-src 'self'
485+
https://cdnjs.cloudflare.com
486+
https://esm.sh
487+
https://cdn.jsdelivr.net
488+
https://unpkg.com;
473489
">
474490
<style>
475491
${THEME_CSS}

0 commit comments

Comments
 (0)