Server: AGUI
Deploy agents as AG-UI protocol servers for CopilotKit integration
Deploy agents as AG-UI servers for CopilotKit and other AG-UI compatible frontends.
CLI
pip install "praisonaiagents[os]"
export OPENAI_API_KEY="your-key"
# Start AGUI/A2U server
praisonai serve a2u --port 8002| Option | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Host to bind to |
--port | 8002 | Server port |
--file | agents.yaml | Agents YAML file |
Python
from praisonaiagents import Agent
from praisonaiagents import AGUI
from fastapi import FastAPI
import uvicorn
agent = Agent(
name="Assistant",
role="Helpful AI Assistant",
goal="Help users with their questions",
llm="gpt-4o-mini"
)
agui = AGUI(agent=agent)
app = FastAPI()
app.include_router(agui.get_router())
uvicorn.run(app, host="0.0.0.0", port=8000)Expected Output:
INFO: Started server process
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000Verify:
curl http://localhost:8000/statusResponse:
{"status": "available"}Verify Routes
After include_router, app.routes won’t list /agui and /status as flat paths — Starlette nests them under an internal _IncludedRouter. The endpoints work at runtime; verify them with TestClient.
from fastapi import FastAPI
from fastapi.testclient import TestClient
from praisonaiagents import Agent, AGUI
agent = Agent(instructions="You are a helpful assistant")
agui = AGUI(agent=agent)
app = FastAPI()
app.include_router(agui.get_router())
client = TestClient(app)
# Health check
assert client.get("/status").status_code == 200
# AG-UI endpoint reachable (will stream SSE on real input)
assert client.post("/agui", json={"message": "hi"}).status_code != 404agents.yaml
AGUI server is Python-only. No agents.yaml support.
AGUI Endpoints
| Endpoint | Method | Description |
|---|---|---|
/agui | POST | Run agent with SSE streaming |
/status | GET | Check agent availability |
AGUI Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
agent | Agent | – | PraisonAI Agent instance |
agents | Agents | – | Multi-agent workflow |
name | str | agent.name | AGUI endpoint name |
description | str | agent.role | Agent description |
prefix | str | "" | URL prefix |
Concurrency & Thread Isolation
One AGUI(agent=agent) instance safely serves many concurrent clients — each run’s streamed tokens stay in its own SSE stream.
A shared Agent still means a shared chat_history. The request’s threadId does not create a separate conversation history. For per-thread memory, give each thread its own Agent.
| Behaviour | Shared Agent | Agent per thread |
|---|---|---|
| Streamed tokens isolated per run | Yes | Yes |
tool_call_* events isolated per run | Yes | Yes |
Custom praisonai.* events isolated per run | Yes | Yes |
Conversation history isolated per threadId | No | Yes |
Agent per thread — mount one AGUI router per thread (or tenant) with prefix:
from praisonaiagents import Agent, AGUI
from fastapi import FastAPI
app = FastAPI()
for thread in ["alice", "bob"]:
agent = Agent(instructions="You are a helpful assistant")
app.include_router(AGUI(agent=agent, prefix=f"/{thread}").get_router())
# Client A → POST /alice/agui, Client B → POST /bob/aguiCopilotKit Integration
import { CopilotKit } from "@copilotkit/react-core";
function App() {
return (
<CopilotKit runtimeUrl="http://localhost:8000/agui">
<YourApp />
</CopilotKit>
);
}Send AGUI Request
curl -X POST http://localhost:8000/agui \
-H "Content-Type: application/json" \
-d '{
"threadId": "thread-123",
"runId": "run-456",
"state": {
"messages": [
{"id": "msg-1", "role": "user", "content": "Hello!"}
]
}
}'Response (SSE Stream):
event: run_started
data: {"type": "run_started", "threadId": "thread-123", "runId": "run-456"}
event: text_message_start
data: {"type": "text_message_start", "messageId": "msg-2"}
event: text_message_content
data: {"type": "text_message_content", "messageId": "msg-2", "delta": "Hello! How can I help?"}
event: text_message_end
data: {"type": "text_message_end", "messageId": "msg-2"}
event: run_finished
data: {"type": "run_finished", "threadId": "thread-123", "runId": "run-456"}Event Types
| Event | Description |
|---|---|
run_started | Agent run has started |
run_finished | Agent run completed |
run_error | Terminal error — includes mid-stream provider failures |
text_message_start | New text message started |
text_message_content | Text content delta |
text_message_end | Text message completed |
tool_call_start | Tool call started |
tool_call_args | Tool call arguments |
tool_call_end | Tool call completed |
custom (praisonai.*) | PraisonAI-specific events for recoverable failures and progress — see below |
A terminal error reaches the client as run_error, now including mid-stream provider failures that were previously silently dropped. Recoverable conditions (retry, model fallback, streaming fallback) arrive as custom praisonai.* events while the run continues.
PraisonAI custom events
AG-UI has no first-class frames for recoverable failures or user-visible progress. PraisonAI carries these as custom events with a praisonai.* name. AG-UI clients are expected to ignore custom event names they do not recognise, so these are backwards-compatible.
| Custom event name | When it fires | value shape |
|---|---|---|
praisonai.retry | LLM call is being retried after a transient failure | { message, metadata } |
praisonai.model_fallback | Provider swapped to a fallback model | { message, metadata } |
praisonai.stream_unavailable | Streaming path failed; falling back to non-streaming | { message, metadata } |
praisonai.tool_progress | A running tool emitted progress | { content, tool_call, metadata } |
praisonai.todo_updated | The agent’s todo list changed | { content, tool_call, metadata } |
See the AGUI API reference for the SSE payload and a JavaScript handler.
JavaScript Client
const response = await fetch('http://localhost:8000/agui', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
threadId: 'thread-123',
runId: 'run-456',
state: {
messages: [{ id: 'msg-1', role: 'user', content: 'Hello!' }]
}
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const event = JSON.parse(line.slice(6));
console.log('Event:', event);
}
}
}Troubleshooting
| Issue | Fix |
|---|---|
| Port in use | lsof -i :8000 |
| Missing deps | pip install "praisonaiagents[os]" |
| No API key | export OPENAI_API_KEY="your-key" |
| CORS errors | Add CORS middleware to FastAPI app |
/agui and /status missing from app.routes | Expected — Starlette nests included routers under _IncludedRouter. Verify with TestClient (see Verify Routes) or inspect agui.get_router().routes directly. Endpoints work at runtime. |
Related
- AGUI API Reference – Full AGUI endpoint documentation
- A2A Server – Deploy as A2A server
- Agents Server – Deploy as HTTP server