e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
169 lines
5.3 KiB
Python
169 lines
5.3 KiB
Python
"""
|
|
Simple AG-UI Protocol Agent for testing assistant-ui integration.
|
|
|
|
Usage:
|
|
pip install fastapi uvicorn openai python-dotenv
|
|
python server/agent.py
|
|
|
|
Set OPENAI_API_KEY in .env.local or environment.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import os
|
|
import uuid
|
|
from typing import AsyncGenerator
|
|
|
|
from dotenv import load_dotenv
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
# Load environment variables from .env.local
|
|
load_dotenv(".env.local")
|
|
load_dotenv(".env")
|
|
|
|
app = FastAPI(title="AG-UI Example Agent")
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
def create_event(event_type: str, **kwargs) -> str:
|
|
"""Create an SSE event in AG-UI format."""
|
|
data = {"type": event_type, **kwargs}
|
|
return f"data: {json.dumps(data)}\n\n"
|
|
|
|
|
|
async def echo_agent(messages: list, run_id: str, thread_id: str) -> AsyncGenerator[str, None]:
|
|
"""Simple echo agent that responds to user messages."""
|
|
yield create_event("RUN_STARTED", runId=run_id, threadId=thread_id)
|
|
|
|
# Get the last user message
|
|
last_message = ""
|
|
for msg in reversed(messages):
|
|
if msg.get("role") == "user":
|
|
content = msg.get("content", "")
|
|
if isinstance(content, str):
|
|
last_message = content
|
|
elif isinstance(content, list):
|
|
for part in content:
|
|
if part.get("type") == "text":
|
|
last_message = part.get("text", "")
|
|
break
|
|
break
|
|
|
|
message_id = str(uuid.uuid4())
|
|
yield create_event("TEXT_MESSAGE_START", messageId=message_id)
|
|
|
|
# Generate response
|
|
response = f"You said: {last_message}\n\nThis is a simple echo agent. To use a real LLM, set OPENAI_API_KEY in your environment."
|
|
|
|
# Stream the response character by character for demo
|
|
for char in response:
|
|
yield create_event("TEXT_MESSAGE_CONTENT", messageId=message_id, delta=char)
|
|
await asyncio.sleep(0.01)
|
|
|
|
yield create_event("TEXT_MESSAGE_END", messageId=message_id)
|
|
yield create_event("RUN_FINISHED", runId=run_id, threadId=thread_id)
|
|
|
|
|
|
async def openai_agent(messages: list, run_id: str, thread_id: str) -> AsyncGenerator[str, None]:
|
|
"""OpenAI-powered agent with streaming."""
|
|
from openai import AsyncOpenAI
|
|
|
|
client = AsyncOpenAI()
|
|
|
|
yield create_event("RUN_STARTED", runId=run_id, threadId=thread_id)
|
|
|
|
# Convert AG-UI messages to OpenAI format
|
|
openai_messages = []
|
|
for msg in messages:
|
|
role = msg.get("role", "user")
|
|
content = msg.get("content", "")
|
|
|
|
if isinstance(content, list):
|
|
# Handle multimodal content
|
|
text_parts = [p.get("text", "") for p in content if p.get("type") == "text"]
|
|
content = " ".join(text_parts)
|
|
|
|
if role in ("user", "assistant", "system"):
|
|
openai_messages.append({"role": role, "content": content})
|
|
|
|
if not openai_messages:
|
|
openai_messages = [{"role": "user", "content": "Hello"}]
|
|
|
|
message_id = str(uuid.uuid4())
|
|
yield create_event("TEXT_MESSAGE_START", messageId=message_id)
|
|
|
|
try:
|
|
stream = await client.chat.completions.create(
|
|
model="gpt-5.4-nano",
|
|
messages=openai_messages,
|
|
stream=True,
|
|
)
|
|
|
|
async for chunk in stream:
|
|
if chunk.choices and chunk.choices[0].delta.content:
|
|
delta = chunk.choices[0].delta.content
|
|
yield create_event("TEXT_MESSAGE_CONTENT", messageId=message_id, delta=delta)
|
|
|
|
yield create_event("TEXT_MESSAGE_END", messageId=message_id)
|
|
yield create_event("RUN_FINISHED", runId=run_id, threadId=thread_id)
|
|
|
|
except Exception as e:
|
|
yield create_event("TEXT_MESSAGE_CONTENT", messageId=message_id, delta=f"Error: {e}")
|
|
yield create_event("TEXT_MESSAGE_END", messageId=message_id)
|
|
yield create_event("RUN_ERROR", message=str(e), threadId=thread_id)
|
|
|
|
|
|
@app.post("/agent")
|
|
async def agent_endpoint(request: Request):
|
|
"""AG-UI agent endpoint."""
|
|
body = await request.json()
|
|
|
|
run_id = body.get("runId", str(uuid.uuid4()))
|
|
messages = body.get("messages", [])
|
|
thread_id = body.get("threadId", "default")
|
|
|
|
print(f"[agent] threadId={thread_id}, runId={run_id}, messages={len(messages)}")
|
|
|
|
# Choose agent based on API key availability
|
|
if os.getenv("OPENAI_API_KEY"):
|
|
generator = openai_agent(messages, run_id, thread_id)
|
|
else:
|
|
generator = echo_agent(messages, run_id, thread_id)
|
|
|
|
return StreamingResponse(
|
|
generator,
|
|
media_type="text/event-stream",
|
|
headers={
|
|
"Cache-Control": "no-cache",
|
|
"Connection": "keep-alive",
|
|
"X-Accel-Buffering": "no",
|
|
},
|
|
)
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
"""Health check endpoint."""
|
|
return {"status": "ok", "openai_configured": bool(os.getenv("OPENAI_API_KEY"))}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
print("Starting AG-UI Agent on http://localhost:8000")
|
|
print(f"OpenAI API Key: {'configured' if os.getenv('OPENAI_API_KEY') else 'not set (using echo mode)'}")
|
|
print("\nEndpoints:")
|
|
print(" POST /agent - AG-UI agent endpoint")
|
|
print(" GET /health - Health check")
|
|
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|