Skip to content

Commit 96ca7c0

Browse files
committed
Merge branch 'main' of https://github.com/ag2ai/artifacts
2 parents 5a131b0 + 77771f8 commit 96ca7c0

File tree

25 files changed

+1021
-1
lines changed

25 files changed

+1021
-1
lines changed

.github/workflows/generate-registry.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ jobs:
1313
contents: write
1414
steps:
1515
- uses: actions/checkout@v4
16+
with:
17+
token: ${{secrets.REGISTRY_PAT}}
1618

1719
- uses: actions/setup-python@v5
1820
with:

registry.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"version": "1",
3-
"updated": "2026-03-19T04:23:44Z",
3+
"updated": "2026-03-19T04:42:42Z",
44
"artifacts": [
55
{
66
"name": "ag2",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "fullstack-multi-agent",
3+
"type": "template",
4+
"display_name": "Full-Stack Multi-Agent App",
5+
"description": "A full-stack application with a FastAPI backend orchestrating an AG2 multi-agent team and a React + Vite frontend with real-time chat UI",
6+
"version": "1.0.0",
7+
"authors": ["ag2ai"],
8+
"license": "Apache-2.0",
9+
"tags": ["fullstack", "react", "fastapi", "multi-agent", "websocket", "chat"],
10+
"template": {
11+
"scaffold": "scaffold/",
12+
"variables": {
13+
"project_name": {
14+
"prompt": "Project name",
15+
"default": "my-agent-app",
16+
"transform": "slug"
17+
},
18+
"description": {
19+
"prompt": "Project description",
20+
"default": "A full-stack multi-agent application"
21+
}
22+
},
23+
"ignore": ["__pycache__", "*.pyc", ".git", "node_modules"],
24+
"post_install": []
25+
},
26+
"skills": {
27+
"dir": "skills/",
28+
"auto_install": true
29+
}
30+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
__pycache__/
2+
*.py[cod]
3+
.env
4+
.venv/
5+
dist/
6+
*.egg-info/
7+
.pytest_cache/
8+
node_modules/
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# {{ project_name }}
2+
3+
{{ description }}
4+
5+
A full-stack application with an AG2 multi-agent backend (FastAPI + WebSocket) and a React frontend.
6+
7+
## Architecture
8+
9+
```
10+
{{ project_name }}/
11+
├── backend/ # FastAPI + AG2 multi-agent team
12+
│ ├── app/
13+
│ │ ├── main.py # FastAPI app, REST + WebSocket endpoints
14+
│ │ ├── agents.py # AG2 agent team: planner, coder, reviewer
15+
│ │ └── schemas.py # Pydantic request/response models
16+
│ └── tests/
17+
├── frontend/ # React + Vite + TypeScript
18+
│ └── src/
19+
│ ├── App.tsx
20+
│ └── components/
21+
│ └── Chat.tsx # WebSocket chat interface
22+
```
23+
24+
## Quick start
25+
26+
```bash
27+
# Backend
28+
cd backend
29+
uv sync
30+
cp .env.example .env # add your API keys
31+
uv run uvicorn app.main:app --reload
32+
33+
# Frontend (separate terminal)
34+
cd frontend
35+
npm install
36+
npm run dev
37+
```
38+
39+
Open http://localhost:5173 — the chat UI connects to the backend WebSocket at ws://localhost:8000/ws/chat.
40+
41+
## How it works
42+
43+
1. User sends a message through the React chat UI
44+
2. The frontend opens a WebSocket connection to the backend
45+
3. FastAPI hands the message to an AG2 multi-agent team:
46+
- **Planner** — breaks the request into steps
47+
- **Coder** — implements the solution
48+
- **Reviewer** — checks for correctness and suggests improvements
49+
4. Each agent response is streamed back to the frontend in real time
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# LLM provider API keys — uncomment the one you use
2+
# OPENAI_API_KEY=sk-...
3+
# ANTHROPIC_API_KEY=sk-ant-...
4+
# GEMINI_API_KEY=...

templates/fullstack-multi-agent/scaffold/backend/app/__init__.py

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""AG2 multi-agent team: planner, coder, reviewer.
2+
3+
The team uses a RoundRobin pattern so each agent contributes in sequence.
4+
Agent responses are collected via a callback and returned to the caller.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from autogen import AssistantAgent, LLMConfig
10+
from autogen.agentchat.group import run_group_chat
11+
from autogen.agentchat.group.patterns.pattern import RoundRobinPattern
12+
13+
config = LLMConfig(api_type="openai", model="gpt-4o")
14+
15+
with config:
16+
planner = AssistantAgent(
17+
name="planner",
18+
system_message=(
19+
"You are a planning agent. Given a user request, break it into clear, "
20+
"numbered steps. Be specific about what needs to happen at each step. "
21+
"Do not write code — only produce the plan."
22+
),
23+
)
24+
25+
coder = AssistantAgent(
26+
name="coder",
27+
system_message=(
28+
"You are a coding agent. Given a plan from the planner, implement "
29+
"the solution step by step. Write clean, well-documented code. "
30+
"If something is ambiguous, state your assumption and proceed."
31+
),
32+
)
33+
34+
reviewer = AssistantAgent(
35+
name="reviewer",
36+
system_message=(
37+
"You are a code review agent. Review the coder's implementation for "
38+
"correctness, edge cases, security issues, and clarity. Provide "
39+
"specific, actionable feedback. If the code is good, say so briefly."
40+
),
41+
)
42+
43+
AGENTS = [planner, coder, reviewer]
44+
AGENT_NAMES = [a.name for a in AGENTS]
45+
46+
pattern = RoundRobinPattern(
47+
initial_agent=planner,
48+
agents=AGENTS,
49+
)
50+
51+
52+
async def run_team(message: str) -> list[dict[str, str]]:
53+
"""Run the multi-agent team on a user message.
54+
55+
Returns a list of {"agent": name, "content": text} dicts.
56+
"""
57+
result = await run_group_chat(
58+
pattern=pattern,
59+
messages=message,
60+
max_rounds=6,
61+
)
62+
63+
messages: list[dict[str, str]] = []
64+
for msg in result.messages:
65+
if hasattr(msg, "source") and hasattr(msg, "content") and msg.content:
66+
messages.append({"agent": msg.source, "content": msg.content})
67+
68+
return messages
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""FastAPI application with REST and WebSocket endpoints.
2+
3+
Run with:
4+
uvicorn app.main:app --reload
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import json
10+
import traceback
11+
12+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
13+
from fastapi.middleware.cors import CORSMiddleware
14+
15+
from .agents import AGENT_NAMES, run_team
16+
from .schemas import AgentMessage, HealthResponse
17+
18+
app = FastAPI(title="{{ project_name }}", description="{{ description }}")
19+
20+
app.add_middleware(
21+
CORSMiddleware,
22+
allow_origins=["http://localhost:5173"],
23+
allow_credentials=True,
24+
allow_methods=["*"],
25+
allow_headers=["*"],
26+
)
27+
28+
29+
@app.get("/api/health", response_model=HealthResponse)
30+
async def health() -> HealthResponse:
31+
"""Health check — returns status and available agent names."""
32+
return HealthResponse(status="ok", agents=AGENT_NAMES)
33+
34+
35+
@app.websocket("/ws/chat")
36+
async def chat_ws(websocket: WebSocket) -> None:
37+
"""WebSocket endpoint for real-time multi-agent chat.
38+
39+
Protocol:
40+
Client sends: {"message": "user text"}
41+
Server sends: {"agent": "name", "content": "...", "type": "agent_message"}
42+
Server sends: {"agent": "", "content": "", "type": "done"} when finished
43+
"""
44+
await websocket.accept()
45+
46+
try:
47+
while True:
48+
data = await websocket.receive_text()
49+
payload = json.loads(data)
50+
user_message = payload.get("message", "")
51+
52+
if not user_message:
53+
await websocket.send_text(
54+
AgentMessage(agent="system", content="Empty message", type="error").model_dump_json()
55+
)
56+
continue
57+
58+
# Notify client that processing has started
59+
await websocket.send_text(
60+
AgentMessage(agent="system", content="Agents are working...", type="status").model_dump_json()
61+
)
62+
63+
# Run the agent team
64+
try:
65+
responses = await run_team(user_message)
66+
except Exception:
67+
await websocket.send_text(
68+
AgentMessage(
69+
agent="system",
70+
content=f"Agent error: {traceback.format_exc()}",
71+
type="error",
72+
).model_dump_json()
73+
)
74+
continue
75+
76+
# Stream each agent response
77+
for resp in responses:
78+
msg = AgentMessage(agent=resp["agent"], content=resp["content"])
79+
await websocket.send_text(msg.model_dump_json())
80+
81+
# Signal completion
82+
await websocket.send_text(
83+
AgentMessage(agent="", content="", type="done").model_dump_json()
84+
)
85+
86+
except WebSocketDisconnect:
87+
pass
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Pydantic models for API request/response types."""
2+
3+
from __future__ import annotations
4+
5+
from pydantic import BaseModel
6+
7+
8+
class ChatRequest(BaseModel):
9+
"""Incoming chat message from the frontend."""
10+
11+
message: str
12+
13+
14+
class AgentMessage(BaseModel):
15+
"""A single message from an agent, sent over WebSocket."""
16+
17+
agent: str
18+
content: str
19+
type: str = "agent_message" # agent_message | status | error | done
20+
21+
22+
class HealthResponse(BaseModel):
23+
status: str = "ok"
24+
agents: list[str] = []

0 commit comments

Comments
 (0)