chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:18 +08:00
commit 6d5d58c1a9
18293 changed files with 3502153 additions and 0 deletions
@@ -0,0 +1,30 @@
"""Claude Agent SDK (Python) starter — AG-UI server.
Serves the agent (defined in ``src/agent.py``) over AG-UI using the official
adapter's FastAPI helper: ``POST /`` streams the run, ``GET /health`` reports
status. Runs on uvicorn (port 8000).
"""
from __future__ import annotations
import os
from fastapi import FastAPI
from ag_ui_claude_sdk import add_claude_fastapi_endpoint
from src.agent import adapter
app = FastAPI(title="Claude Agent SDK (Python) Starter")
# The adapter's helper mounts POST / (streams the run) — the runtime connects here.
add_claude_fastapi_endpoint(app, adapter)
@app.get("/health")
async def health() -> dict[str, str]:
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=int(os.getenv("AGENT_PORT", "8000")))
@@ -0,0 +1,18 @@
[project]
name = "claude-sdk-python-starter-agent"
version = "0.1.0"
description = "Claude Agent SDK integration server for AG-UI"
requires-python = ">=3.12,<3.14"
dependencies = [
"anthropic==0.111.0",
"claude-agent-sdk==0.2.104",
"ag-ui-claude-sdk==0.1.5",
"ag-ui-protocol==0.1.19",
"copilotkit==0.1.94",
"fastapi>=0.115.0",
"uvicorn>=0.34.0",
"python-dotenv>=1.0.1",
]
[tool.uv]
package = false
@@ -0,0 +1,37 @@
[
{
"id": "root",
"component": "Row",
"children": {
"componentId": "flight-card",
"path": "/flights"
},
"gap": 16
},
{
"id": "flight-card",
"component": "FlightCard",
"airline": { "path": "airline" },
"airlineLogo": { "path": "airlineLogo" },
"flightNumber": { "path": "flightNumber" },
"origin": { "path": "origin" },
"destination": { "path": "destination" },
"date": { "path": "date" },
"departureTime": { "path": "departureTime" },
"arrivalTime": { "path": "arrivalTime" },
"duration": { "path": "duration" },
"status": { "path": "status" },
"price": { "path": "price" },
"action": {
"event": {
"name": "book_flight",
"context": {
"flightNumber": { "path": "flightNumber" },
"origin": { "path": "origin" },
"destination": { "path": "destination" },
"price": { "path": "price" }
}
}
}
}
]
@@ -0,0 +1,106 @@
"""Dynamic A2UI tool — an LLM-designed dashboard.
A secondary Anthropic call designs a v0.9 A2UI surface via a structured
``render_a2ui`` tool call; the result is wrapped as ``a2ui_operations`` for the
frontend. The handler runs in this process (not the CLI subprocess), so it is
free to make its own Anthropic request.
"""
from __future__ import annotations
import json
from typing import Any
import anthropic
from claude_agent_sdk import tool
from copilotkit import a2ui
from src.model import resolve_model
CATALOG_ID = "copilotkit://app-dashboard-catalog"
# Structured-output schema handed to the secondary LLM to force one design call.
_RENDER_A2UI_TOOL: dict[str, Any] = {
"name": "render_a2ui",
"description": (
"Render a dynamic A2UI v0.9 surface. Provide a components array (flat "
"v0.9 format; the root component must have id 'root') and an optional "
"initial data model."
),
"input_schema": {
"type": "object",
"properties": {
"surfaceId": {"type": "string", "description": "Unique surface id."},
"catalogId": {
"type": "string",
"description": f"Catalog id (use '{CATALOG_ID}').",
},
"components": {
"type": "array",
"items": {"type": "object"},
"description": "A2UI v0.9 component array (flat format).",
},
"data": {
"type": "object",
"description": "Optional initial data model for the surface.",
},
},
"required": ["surfaceId", "catalogId", "components"],
},
}
@tool(
"generate_a2ui",
"Generate a dynamic A2UI dashboard (metrics, charts, tables, cards) based on "
"the conversation. A secondary LLM designs the UI; it renders automatically.",
{"context": str},
)
async def generate_a2ui(args: dict[str, Any]) -> dict[str, Any]:
# Construct the client per call so it picks up ANTHROPIC_API_KEY /
# ANTHROPIC_BASE_URL after the environment is loaded.
client = anthropic.AsyncAnthropic()
try:
response = await client.messages.create(
model=resolve_model(),
max_tokens=4096,
system=args.get("context") or "Generate a useful dashboard UI.",
messages=[
{
"role": "user",
"content": "Generate a dynamic A2UI dashboard based on the conversation.",
}
],
tools=[_RENDER_A2UI_TOOL],
tool_choice={"type": "tool", "name": "render_a2ui"},
)
except Exception:
return {
"content": [
{
"type": "text",
"text": json.dumps({"error": "Failed to generate A2UI dashboard"}),
}
]
}
for block in response.content:
if getattr(block, "type", None) == "tool_use" and block.name == "render_a2ui":
spec = dict(block.input)
surface_id = spec.get("surfaceId", "dynamic-surface")
ops = [
a2ui.create_surface(
surface_id, catalog_id=spec.get("catalogId", CATALOG_ID)
),
a2ui.update_components(surface_id, spec.get("components", []) or []),
]
if spec.get("data"):
ops.append(a2ui.update_data_model(surface_id, spec["data"]))
return {"content": [{"type": "text", "text": a2ui.render(operations=ops)}]}
return {
"content": [
{
"type": "text",
"text": json.dumps({"error": "LLM did not call render_a2ui"}),
}
]
}
@@ -0,0 +1,64 @@
"""Fixed-schema A2UI tool — flight search results.
The A2UI component schema is loaded from JSON; only the flight data changes per
call. The tool result carries ``a2ui_operations``, which the frontend renders.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any, TypedDict
from claude_agent_sdk import tool
from copilotkit import a2ui
CATALOG_ID = "copilotkit://app-dashboard-catalog"
FLIGHT_SURFACE_ID = "flight-search-results"
FLIGHT_SCHEMA = a2ui.load_schema(
Path(__file__).parent / "a2ui" / "schemas" / "flight_schema.json"
)
class Flight(TypedDict):
id: str
airline: str
airlineLogo: str
flightNumber: str
origin: str
destination: str
date: str
departureTime: str
arrivalTime: str
duration: str
status: str
statusIcon: str
price: str
@tool(
"search_flights",
"Search for flights and display the results as rich cards. Return exactly 2 "
"flights. Each flight must have: id, airline, airlineLogo (Google favicon API "
"URL for the airline domain), flightNumber, origin, destination, date (e.g. "
'"Tue, Mar 18" — use near-future dates), departureTime, arrivalTime, duration '
'(e.g. "4h 25m"), status (e.g. "On Time" or "Delayed"), statusIcon (colored '
"dot URL: https://placehold.co/12/22c55e/22c55e.png for On Time, "
'https://placehold.co/12/eab308/eab308.png for Delayed), and price (e.g. "$289").',
{"flights": list[Flight]},
)
async def search_flights(args: dict[str, Any]) -> dict[str, Any]:
flights = args.get("flights", [])
return {
"content": [
{
"type": "text",
"text": a2ui.render(
operations=[
a2ui.create_surface(FLIGHT_SURFACE_ID, catalog_id=CATALOG_ID),
a2ui.update_components(FLIGHT_SURFACE_ID, FLIGHT_SCHEMA),
a2ui.update_data_model(FLIGHT_SURFACE_ID, {"flights": flights}),
]
),
}
]
}
@@ -0,0 +1,67 @@
"""The Claude Agent SDK agent — backend tools + the official AG-UI adapter.
Three backend tools live in their own modules (``src/query.py``,
``src/a2ui_fixed_schema.py``, ``src/a2ui_dynamic_schema.py``). The official
``ClaudeAgentAdapter`` does everything else: it drives Claude via the Claude
Agent SDK, bridges CopilotKit frontend tools + human-in-the-loop, and manages
the shared ``todos`` state via its built-in ``ag_ui_update_state`` tool.
"""
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
from dotenv import load_dotenv
from ag_ui_claude_sdk import ClaudeAgentAdapter
from claude_agent_sdk import create_sdk_mcp_server
from src.model import resolve_model
from src.query import query_data
from src.a2ui_fixed_schema import search_flights
from src.a2ui_dynamic_schema import generate_a2ui
# Load .env from the starter root before building the adapter (which reads the
# model from the environment); fall back to the current working directory.
for _env in (Path(__file__).resolve().parents[2] / ".env", Path(".env")):
if _env.is_file():
load_dotenv(_env)
break
else:
load_dotenv()
SYSTEM_PROMPT = dedent(
"""
You are a polished, professional demo assistant. Keep responses to 1-2 sentences.
- Flights: call search_flights to show flight cards.
- Dashboards: call generate_a2ui to build a rich dashboard UI; it renders itself.
- Charts: call query_data first, then render with the chart component.
- Todos: the todo board is shared state under `todos`; call ag_ui_update_state
with the COMPLETE list to add or change todos.
"""
).strip()
# The Claude Agent SDK exposes custom tools through an in-process MCP server
# (create_sdk_mcp_server). The model calls them as mcp__<server>__<tool>, and
# `allowed_tools` pre-approves those names so they run without a permission prompt.
# (`tools` is a different field — Claude Code's BUILT-IN toolset; `[]` disables it
# so the model only uses ours + the AG-UI protocol tools.)
SERVER_NAME = "copilotkit"
BACKEND_TOOLS = [query_data, search_flights, generate_a2ui]
adapter = ClaudeAgentAdapter(
name="claude-sdk-python",
description="CopilotKit × Claude Agent SDK (Python) starter",
options={
"model": resolve_model(),
"system_prompt": SYSTEM_PROMPT,
"mcp_servers": {
SERVER_NAME: create_sdk_mcp_server(
SERVER_NAME, "1.0.0", tools=BACKEND_TOOLS
),
},
"allowed_tools": [f"mcp__{SERVER_NAME}__{tool.name}" for tool in BACKEND_TOOLS],
"tools": [],
},
)
@@ -0,0 +1,41 @@
date,category,subcategory,amount,type,notes
2026-01-05,Revenue,Enterprise Subscriptions,28000,income,3 new enterprise customers (Acme Corp, TechFlow, DataViz Inc)
2026-01-05,Revenue,Pro Tier Upgrades,18000,income,24 users upgraded from free to pro
2026-01-08,Revenue,API Usage Overages,9500,income,High API usage from top 5 customers
2026-01-10,Expenses,Engineering Salaries,42000,expense,7 engineers + 2 contractors
2026-01-10,Expenses,Product Team,18000,expense,PM and 2 designers
2026-01-12,Expenses,AWS Infrastructure,8200,expense,Increased compute for new AI features
2026-01-15,Expenses,Marketing - Paid Ads,12000,expense,Google Ads and LinkedIn campaigns
2026-01-18,Revenue,Consulting Services,14500,income,Custom integration for Acme Corp
2026-01-20,Expenses,Customer Success,15000,expense,3 CSMs + support tools (Intercom)
2026-01-22,Expenses,AI Model Costs,4200,expense,OpenAI API usage for product features
2026-01-25,Revenue,Marketplace Sales,12800,income,Template and plugin sales
2026-01-28,Expenses,Office & Equipment,3500,expense,New laptops and coworking spaces
2026-02-03,Revenue,Enterprise Subscriptions,31000,income,2 new customers + expansion from TechFlow
2026-02-03,Revenue,Pro Tier Upgrades,22500,income,31 upgrades + reduced churn
2026-02-05,Revenue,API Usage Overages,11800,income,DataViz Inc heavy API usage spike
2026-02-07,Expenses,Engineering Salaries,42000,expense,Same headcount as January
2026-02-07,Expenses,Product Team,18000,expense,No changes to product team
2026-02-10,Expenses,AWS Infrastructure,9500,expense,Traffic spike from viral social post
2026-02-12,Expenses,Marketing - Paid Ads,15000,expense,Increased ad spend for Q1 push
2026-02-14,Revenue,Consulting Services,18000,income,2 custom projects (TechFlow + new client)
2026-02-18,Expenses,Customer Success,16500,expense,Hired 1 additional CSM
2026-02-20,Expenses,AI Model Costs,5800,expense,Increased usage from new AI features launch
2026-02-22,Revenue,Marketplace Sales,14200,income,Top template hit featured list
2026-02-25,Expenses,Conference & Travel,4500,expense,Team attended SaaS Conference 2026
2026-02-27,Revenue,Partnership Revenue,11500,income,Referral fees from integration partners
2026-03-02,Revenue,Enterprise Subscriptions,35000,income,Major win: Fortune 500 customer signed
2026-03-02,Revenue,Pro Tier Upgrades,26000,income,42 upgrades - best month yet
2026-03-05,Revenue,API Usage Overages,13200,income,Consistent high usage across top tier
2026-03-08,Expenses,Engineering Salaries,48000,expense,Hired 1 senior engineer for AI team
2026-03-08,Expenses,Product Team,21000,expense,Promoted designer to senior level
2026-03-10,Expenses,AWS Infrastructure,11000,expense,Scaled infrastructure for enterprise client
2026-03-12,Expenses,Marketing - Paid Ads,18000,expense,Doubled down on successful campaigns
2026-03-14,Revenue,Consulting Services,21500,income,Fortune 500 onboarding + 2 other projects
2026-03-16,Expenses,Customer Success,19500,expense,Hired dedicated enterprise CSM
2026-03-18,Expenses,AI Model Costs,7200,expense,Fortune 500 client heavy AI usage
2026-03-20,Revenue,Marketplace Sales,15800,income,3 new templates in top 10
2026-03-22,Expenses,Sales & BD,12000,expense,Hired first sales rep for enterprise
2026-03-24,Revenue,Partnership Revenue,14200,income,New integration partnerships launched
2026-03-26,Expenses,Security & Compliance,6500,expense,SOC 2 audit and security tools
2026-03-28,Revenue,Training & Workshops,10200,income,Conducted 2 customer training sessions
1 date,category,subcategory,amount,type,notes
2 2026-01-05,Revenue,Enterprise Subscriptions,28000,income,3 new enterprise customers (Acme Corp, TechFlow, DataViz Inc)
3 2026-01-05,Revenue,Pro Tier Upgrades,18000,income,24 users upgraded from free to pro
4 2026-01-08,Revenue,API Usage Overages,9500,income,High API usage from top 5 customers
5 2026-01-10,Expenses,Engineering Salaries,42000,expense,7 engineers + 2 contractors
6 2026-01-10,Expenses,Product Team,18000,expense,PM and 2 designers
7 2026-01-12,Expenses,AWS Infrastructure,8200,expense,Increased compute for new AI features
8 2026-01-15,Expenses,Marketing - Paid Ads,12000,expense,Google Ads and LinkedIn campaigns
9 2026-01-18,Revenue,Consulting Services,14500,income,Custom integration for Acme Corp
10 2026-01-20,Expenses,Customer Success,15000,expense,3 CSMs + support tools (Intercom)
11 2026-01-22,Expenses,AI Model Costs,4200,expense,OpenAI API usage for product features
12 2026-01-25,Revenue,Marketplace Sales,12800,income,Template and plugin sales
13 2026-01-28,Expenses,Office & Equipment,3500,expense,New laptops and coworking spaces
14 2026-02-03,Revenue,Enterprise Subscriptions,31000,income,2 new customers + expansion from TechFlow
15 2026-02-03,Revenue,Pro Tier Upgrades,22500,income,31 upgrades + reduced churn
16 2026-02-05,Revenue,API Usage Overages,11800,income,DataViz Inc heavy API usage spike
17 2026-02-07,Expenses,Engineering Salaries,42000,expense,Same headcount as January
18 2026-02-07,Expenses,Product Team,18000,expense,No changes to product team
19 2026-02-10,Expenses,AWS Infrastructure,9500,expense,Traffic spike from viral social post
20 2026-02-12,Expenses,Marketing - Paid Ads,15000,expense,Increased ad spend for Q1 push
21 2026-02-14,Revenue,Consulting Services,18000,income,2 custom projects (TechFlow + new client)
22 2026-02-18,Expenses,Customer Success,16500,expense,Hired 1 additional CSM
23 2026-02-20,Expenses,AI Model Costs,5800,expense,Increased usage from new AI features launch
24 2026-02-22,Revenue,Marketplace Sales,14200,income,Top template hit featured list
25 2026-02-25,Expenses,Conference & Travel,4500,expense,Team attended SaaS Conference 2026
26 2026-02-27,Revenue,Partnership Revenue,11500,income,Referral fees from integration partners
27 2026-03-02,Revenue,Enterprise Subscriptions,35000,income,Major win: Fortune 500 customer signed
28 2026-03-02,Revenue,Pro Tier Upgrades,26000,income,42 upgrades - best month yet
29 2026-03-05,Revenue,API Usage Overages,13200,income,Consistent high usage across top tier
30 2026-03-08,Expenses,Engineering Salaries,48000,expense,Hired 1 senior engineer for AI team
31 2026-03-08,Expenses,Product Team,21000,expense,Promoted designer to senior level
32 2026-03-10,Expenses,AWS Infrastructure,11000,expense,Scaled infrastructure for enterprise client
33 2026-03-12,Expenses,Marketing - Paid Ads,18000,expense,Doubled down on successful campaigns
34 2026-03-14,Revenue,Consulting Services,21500,income,Fortune 500 onboarding + 2 other projects
35 2026-03-16,Expenses,Customer Success,19500,expense,Hired dedicated enterprise CSM
36 2026-03-18,Expenses,AI Model Costs,7200,expense,Fortune 500 client heavy AI usage
37 2026-03-20,Revenue,Marketplace Sales,15800,income,3 new templates in top 10
38 2026-03-22,Expenses,Sales & BD,12000,expense,Hired first sales rep for enterprise
39 2026-03-24,Revenue,Partnership Revenue,14200,income,New integration partnerships launched
40 2026-03-26,Expenses,Security & Compliance,6500,expense,SOC 2 audit and security tools
41 2026-03-28,Revenue,Training & Workshops,10200,income,Conducted 2 customer training sessions
@@ -0,0 +1,22 @@
"""Model selection shared across the agent's tools and the adapter."""
from __future__ import annotations
import os
DEFAULT_CLAUDE_MODEL = "claude-sonnet-5"
def resolve_model() -> str:
"""Resolve the Claude model id from the environment.
Prefers ``CLAUDE_MODEL``, then ``ANTHROPIC_MODEL``, then the default. A
dotted marketing name (e.g. ``claude-sonnet-4.5``) is normalized to the API
id (``claude-sonnet-4-5``).
"""
raw = (
os.getenv("CLAUDE_MODEL")
or os.getenv("ANTHROPIC_MODEL")
or DEFAULT_CLAUDE_MODEL
)
return raw.replace(".", "-")
@@ -0,0 +1,36 @@
"""query_data tool — returns rows from the sample financial database."""
from __future__ import annotations
import csv
import json
from pathlib import Path
from typing import Any
from claude_agent_sdk import tool
# Read the CSV once at import. The notes column can contain unquoted commas, so
# keep the first N-1 fields and join the remainder into the last column.
_CSV_PATH = Path(__file__).parent / "db.csv"
with open(_CSV_PATH, newline="") as _f:
_reader = csv.reader(_f)
_header = next(_reader)
_last = len(_header) - 1
_CACHED_DATA = [
{
**{_header[i]: (row[i] if i < len(row) else "") for i in range(_last)},
_header[_last]: ",".join(row[_last:]),
}
for row in _reader
if row
]
@tool(
"query_data",
"Query the financial database with a natural-language query. Always call "
"this before rendering a chart so the UI has data to plot.",
{"query": str},
)
async def query_data(args: dict[str, Any]) -> dict[str, Any]:
return {"content": [{"type": "text", "text": json.dumps(_CACHED_DATA)}]}
File diff suppressed because it is too large Load Diff