--- name: ag2-ag-ui description: Expose an AG2 beta `Agent` over the AG-UI protocol so a frontend (CopilotKit, custom React/Next.js, or any AG-UI client) can stream responses, render tool calls, sync shared state, and surface human-input checkpoints. Wraps the agent with `AGUIStream(agent)` and mounts it in FastAPI via `stream.dispatch(...)` or `stream.build_asgi()`. Use when the user wants a web frontend in front of an AG2 agent rather than a CLI / script. license: Apache-2.0 --- # AG-UI integration ## When to use - The user is building a web UI (React / Next.js / anything HTTP+SSE) that should talk to an AG2 beta agent. - They want streaming text, tool-call rendering, shared state sync, or HITL checkpoints surfaced to the frontend with a standard protocol — not a custom REST/WebSocket contract. - They're using or considering CopilotKit (the recommended React client). For a custom narrow-purpose API where you own the contract end-to-end, skip AG-UI and write a plain endpoint instead. ## Supported AG-UI features | Feature | Status | |---|---| | Streaming text events (`TEXT_MESSAGE_*`, `TEXT_MESSAGE_CHUNK`) | ✓ | | Backend tool lifecycle (`TOOL_CALL_START` / `_ARGS` / `_RESULT` / `_END`) | ✓ | | Frontend-tool dispatch (`TOOL_CALL_CHUNK` for client tools in `RunAgentInput.tools`) | ✓ | | Shared-state snapshots (`STATE_SNAPSHOT`) | ✓ | | Human input checkpoints (surfaced as user-visible message events) | ✓ | ## Installation ```bash pip install "ag2[ag-ui]" ``` ## 60-second recipe — FastAPI server ```python title="run_ag_ui.py" from fastapi import FastAPI, Header from fastapi.responses import StreamingResponse from autogen.beta import Agent from autogen.beta.ag_ui import AGUIStream, RunAgentInput from autogen.beta.config import OpenAIConfig agent = Agent( name="support_bot", prompt="You help users with billing questions.", config=OpenAIConfig(model="gpt-4o-mini"), ) stream = AGUIStream(agent) app = FastAPI() @app.post("/chat") async def run_agent(message: RunAgentInput, accept: str | None = Header(None)) -> StreamingResponse: return StreamingResponse( stream.dispatch(message, accept=accept), media_type=accept or "text/event-stream", ) ``` ```bash uvicorn run_ag_ui:app --reload --port 8000 ``` Test: ```bash curl -N -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{"thread_id":"t1","run_id":"r1","messages":[{"id":"m1","role":"user","content":"Hello"}],"state":{},"context":[],"tools":[]}' ``` ## Lower-friction alternative — `build_asgi()` If you don't need custom auth / middleware, mount the ASGI endpoint directly: ```python from autogen.beta.ag_ui import AGUIStream from fastapi import FastAPI app = FastAPI() stream = AGUIStream(agent) app.mount("/chat", stream.build_asgi()) ``` ## CopilotKit frontend (recommended for React / Next.js) Two ways to start: ```bash # Option A — CopilotKit bootstrap npx copilotkit@latest create -f ag2 # Option B — clone the reference starter # https://github.com/ag2ai/ag2-copilotkit-starter ``` The starter layout: ``` ag2-copilotkit-starter/ ├── agent-py/ # Python backend (AG2 + AG-UI) └── ui-react/ # React + CopilotKit frontend ``` Backend serves `/chat` on port 8008 by default. The Next.js route at `ui-react/app/api/copilotkit/route.ts` bridges the UI to the backend: ```tsx import { HttpAgent } from "@ag-ui/client"; import { CopilotRuntime, ExperimentalEmptyAdapter, copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime"; import { NextRequest } from "next/server"; const agent = new HttpAgent({ url: "http://localhost:8008/chat" }); const runtime = new CopilotRuntime({ agents: { weather_agent: agent } }); export async function POST(req: NextRequest) { const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ runtime, serviceAdapter: new ExperimentalEmptyAdapter(), endpoint: "/api/copilotkit", }); return handleRequest(req); } ``` Wrap your app: ```tsx import { CopilotKit } from "@copilotkit/react-core"; import "@copilotkit/react-ui/styles.css"; export default function RootLayout({ children }: { children: React.ReactNode }) { return (