From 1cb7ac9574d28f1092df7065dff03b8698bb0f9c Mon Sep 17 00:00:00 2001 From: jerelvelarde Date: Wed, 25 Mar 2026 00:43:37 -0700 Subject: [PATCH 1/4] feat: upgrade to LangChain deep agents with skills support Replace `create_agent` from langchain with `create_deep_agent` from the deepagents package. This enables proper skills management with progressive disclosure instead of injecting ~47KB of skill text into the system prompt. - Convert 3 skill .txt files to SKILL.md format in subdirectories - Remove old skills/__init__.py loader (deepagents handles discovery) - Add `skills=` parameter pointing to skills directory - Add deepagents dependency to pyproject.toml Closes #47 --- apps/agent/main.py | 16 +++------ apps/agent/pyproject.toml | 1 + apps/agent/skills/__init__.py | 26 -------------- .../SKILL.md} | 10 ++++-- .../SKILL.md} | 34 +++++++++++-------- .../SKILL.md} | 18 ++++++---- 6 files changed, 46 insertions(+), 59 deletions(-) delete mode 100644 apps/agent/skills/__init__.py rename apps/agent/skills/{agent-skills-vol2.txt => advanced-visualization/SKILL.md} (98%) rename apps/agent/skills/{master-agent-playbook.txt => master-playbook/SKILL.md} (93%) rename apps/agent/skills/{svg-diagram-skill.txt => svg-diagrams/SKILL.md} (94%) diff --git a/apps/agent/main.py b/apps/agent/main.py index 9d558e6..8c5b548 100644 --- a/apps/agent/main.py +++ b/apps/agent/main.py @@ -4,26 +4,24 @@ """ import os +from pathlib import Path from copilotkit import CopilotKitMiddleware -from langchain.agents import create_agent +from deepagents import create_deep_agent from langchain_openai import ChatOpenAI from src.query import query_data from src.todos import AgentState, todo_tools from src.form import generate_form from src.templates import template_tools -from skills import load_all_skills -# Load all visualization skills -_skills_text = load_all_skills() - -agent = create_agent( +agent = create_deep_agent( model=ChatOpenAI(model=os.environ.get("LLM_MODEL", "gpt-5.4-2026-03-05")), tools=[query_data, *todo_tools, generate_form, *template_tools], middleware=[CopilotKitMiddleware()], state_schema=AgentState, - system_prompt=f""" + skills=[str(Path(__file__).parent / "skills")], + system_prompt=""" You are a helpful assistant that helps users understand CopilotKit and LangGraph used together. Be brief in your explanations of CopilotKit and LangGraph, 1 to 2 sentences. @@ -47,10 +45,6 @@ - Pre-styled form elements (buttons, inputs, sliders look native automatically) - Pre-built SVG CSS classes for color ramps (.c-purple, .c-teal, .c-blue, etc.) - Follow the skills below for how to produce high-quality visuals: - - {_skills_text} - ## UI Templates Users can save generated UIs as reusable templates and apply them later. diff --git a/apps/agent/pyproject.toml b/apps/agent/pyproject.toml index 5bb21d3..26ca9e5 100644 --- a/apps/agent/pyproject.toml +++ b/apps/agent/pyproject.toml @@ -16,4 +16,5 @@ dependencies = [ "copilotkit>=0.1.77", "langgraph-api>=0.7.16", "langchain-mcp-adapters>=0.2.1", + "deepagents>=0.1.0", ] diff --git a/apps/agent/skills/__init__.py b/apps/agent/skills/__init__.py deleted file mode 100644 index a871edf..0000000 --- a/apps/agent/skills/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Skill loader — reads .txt skill files from this directory -and exposes them for injection into the agent system prompt. -""" - -from pathlib import Path - - -_SKILLS_DIR = Path(__file__).parent - - -def load_skill(name: str) -> str: - """Load a single skill file by name (without extension).""" - path = _SKILLS_DIR / f"{name}.txt" - if not path.exists(): - raise FileNotFoundError(f"Skill file not found: {path}") - return path.read_text() - - -def load_all_skills() -> str: - """Load and concatenate all .txt skill files in this directory.""" - parts: list[str] = [] - for path in sorted(_SKILLS_DIR.glob("*.txt")): - parts.append(f"\n\n{'='*60}\n# SKILL: {path.stem}\n{'='*60}\n\n") - parts.append(path.read_text()) - return "".join(parts) diff --git a/apps/agent/skills/agent-skills-vol2.txt b/apps/agent/skills/advanced-visualization/SKILL.md similarity index 98% rename from apps/agent/skills/agent-skills-vol2.txt rename to apps/agent/skills/advanced-visualization/SKILL.md index a92d5e4..f40735e 100644 --- a/apps/agent/skills/agent-skills-vol2.txt +++ b/apps/agent/skills/advanced-visualization/SKILL.md @@ -1,3 +1,9 @@ +--- +name: "Advanced Visualization Techniques" +description: "UI mockups, dashboards, advanced interactivity, generative art, simulations, math visualizations, and design system rules for producing rich HTML widget output." +allowed-tools: [] +--- + # Agent Visualization Skills — Volume 2: Advanced Techniques Prerequisite: Volume 1 (SVG diagrams, basic interactive widgets, Chart.js, Mermaid). @@ -427,7 +433,7 @@ hardcoded hex values. ``` - Height goes on the wrapper div ONLY, never on canvas. - Always set `responsive: true, maintainAspectRatio: false`. -- For horizontal bar charts: height = (bars × 40) + 80 pixels. +- For horizontal bar charts: height = (bars x 40) + 80 pixels. ### Custom Legend (Always Use This) Disable Chart.js default legend and build HTML: @@ -450,7 +456,7 @@ plugins: { legend: { display: false } } ``` ### Dashboard Layout -Metric cards on top → chart below → sendPrompt for drill-down: +Metric cards on top -> chart below -> sendPrompt for drill-down: ```html
Answer in 1-2 sentences. + | + +- Is it conceptual / "how does X work"? + | +- Is it spatial or visual? -> SVG illustrative diagram + | +- Is it a process/flow? -> SVG flowchart or HTML stepper + | +- Is it data-driven? -> Interactive chart (Chart.js / Recharts) + | +- Is it abstract but explorable? -> Interactive HTML widget with controls + | + +- Is it "build me X"? -> Working code artifact, fully functional + | + +- Is it a comparison? -> Side-by-side table or comparative visual + | + +- Is it emotional/personal? -> Warm text response. No visuals needed. ``` ### The 3-Layer Response Pattern diff --git a/apps/agent/skills/svg-diagram-skill.txt b/apps/agent/skills/svg-diagrams/SKILL.md similarity index 94% rename from apps/agent/skills/svg-diagram-skill.txt rename to apps/agent/skills/svg-diagrams/SKILL.md index 2a28385..fedb655 100644 --- a/apps/agent/skills/svg-diagram-skill.txt +++ b/apps/agent/skills/svg-diagrams/SKILL.md @@ -1,3 +1,9 @@ +--- +name: "SVG Diagram Generation" +description: "Generating rich inline SVG diagrams to visually explain systems, processes, architectures, and abstract concepts." +allowed-tools: [] +--- + # SVG Diagram Generation Skill 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: - **Sentence case always**. Never Title Case or ALL CAPS. ### Text Width Estimation -At 14px, each character ≈ 8px wide. At 12px, each character ≈ 7px wide. -- "Load Balancer" (13 chars) at 14px ≈ 104px → needs rect ≈ 140px wide (with padding). -- Always compute: `rect_width = max(title_chars × 8, subtitle_chars × 7) + 48px padding`. +At 14px, each character ~ 8px wide. At 12px, each character ~ 7px wide. +- "Load Balancer" (13 chars) at 14px ~ 104px -> needs rect ~ 140px wide (with padding). +- Always compute: `rect_width = max(title_chars x 8, subtitle_chars x 7) + 48px padding`. ### Colors (Light/Dark Mode Safe) 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: 1. **ViewBox height**: Find your lowest element (max y + height). Set H = that + 40px. 2. **No content past x=640 or below y=(H-40)**. -3. **Text fits in boxes**: `(char_count × 8) + 48 < rect_width` for 14px text. +3. **Text fits in boxes**: `(char_count x 8) + 48 < rect_width` for 14px text. 4. **No arrows through boxes**: Trace every line's path — if it crosses a rect, reroute. 5. **All `` connectors have `fill="none"`**. 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: HTTP POST /api/data - + @@ -219,7 +225,7 @@ For complex topics, use multiple smaller SVGs instead of one dense one: Validate and transform - + From 98c8c9b2b1b484abf92c6b95a354c648a411ccd9 Mon Sep 17 00:00:00 2001 From: jerelvelarde Date: Wed, 25 Mar 2026 01:34:08 -0700 Subject: [PATCH 2/4] fix: use context_schema instead of state_schema for create_deep_agent The deepagents API uses `context_schema` parameter instead of `state_schema` from the old create_agent API. --- apps/agent/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agent/main.py b/apps/agent/main.py index 8c5b548..4c0c17c 100644 --- a/apps/agent/main.py +++ b/apps/agent/main.py @@ -19,7 +19,7 @@ model=ChatOpenAI(model=os.environ.get("LLM_MODEL", "gpt-5.4-2026-03-05")), tools=[query_data, *todo_tools, generate_form, *template_tools], middleware=[CopilotKitMiddleware()], - state_schema=AgentState, + context_schema=AgentState, skills=[str(Path(__file__).parent / "skills")], system_prompt=""" You are a helpful assistant that helps users understand CopilotKit and LangGraph used together. From 131c4d784d2890d9afe0417f16abc3d0b3de8f1a Mon Sep 17 00:00:00 2001 From: jerelvelarde Date: Wed, 25 Mar 2026 03:40:30 -0700 Subject: [PATCH 3/4] fix: load CDN scripts sequentially and preserve script type in widget iframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit External scripts (e.g. Three.js from CDN) were appended via DOM and loaded asynchronously, so inline scripts that depend on them would execute before the library was available. Now scripts are chained sequentially — each external script waits for onload before the next script runs. Also preserves the script `type` attribute (needed for `type="module"`) and adds CDN origins to CSP `connect-src` so dynamic imports and fetch from allowed CDNs work in the sandboxed iframe. --- .../generative-ui/widget-renderer.tsx | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/app/src/components/generative-ui/widget-renderer.tsx b/apps/app/src/components/generative-ui/widget-renderer.tsx index 44ed82e..6a64a1a 100644 --- a/apps/app/src/components/generative-ui/widget-renderer.tsx +++ b/apps/app/src/components/generative-ui/widget-renderer.tsx @@ -369,7 +369,7 @@ window.addEventListener('message', function(e) { var scriptCloses = (rawHtml.match(/<\\/script>/gi) || []).length; var allScriptsClosed = scriptOpens <= scriptCloses; tmp.querySelectorAll('script').forEach(function(s) { - incomingScripts.push({ src: s.src, text: s.textContent }); + incomingScripts.push({ src: s.src, text: s.textContent, type: s.type || '' }); s.remove(); }); @@ -414,24 +414,36 @@ window.addEventListener('message', function(e) { content.innerHTML = tmp.innerHTML; } - // Execute only new scripts — skip entirely while a