FastAPI + OpenAI Agents SDK
A complete example with a FastAPI backend using the OpenAI Agents SDK and a React frontend consuming the stream with @devscalelabs/react-sse-chat.
Backend (Python)
Section titled “Backend (Python)”import json
from agents import Agent, RawResponsesStreamEvent, RunItemStreamEvent, Runnerfrom agents.extensions.memory import SQLAlchemySessionfrom fastapi import APIRouter, Dependsfrom fastapi.responses import StreamingResponsefrom openai.types.responses import ResponseFunctionToolCall, ResponseTextDeltaEventfrom sqlmodel import Session
from app.models.engine import engine, get_db
chat_router = APIRouter(prefix="/chat")
@chat_router.post("/")async def generate_answer(request: ChatRequest, db_session: Session = Depends(get_db)): session = SQLAlchemySession( session_id=request.session_id, engine=engine, create_tables=True, )
agent = Agent( "Assistant", instructions="You are a helpful assistant.", model="gpt-4o-mini", tools=[], # add your tools here ) runner = Runner.run_streamed(agent, input=request.message, session=session)
async def event_generator(): async for event in runner.stream_events(): if event.type == "raw_response_event" and isinstance( event, RawResponsesStreamEvent ): if ( isinstance(event.data, ResponseTextDeltaEvent) and event.data.delta != "" ): yield f"data: {json.dumps({'type': 'text_delta', 'delta': event.data.delta})}\n\n"
elif ( isinstance(event, RunItemStreamEvent) and event.name == "tool_called" ): if isinstance(event.item.raw_item, ResponseFunctionToolCall): yield f"data: {json.dumps({'type': 'tool_call', 'tool_name': event.item.raw_item.name, 'argument': event.item.raw_item.arguments})}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")Frontend (React)
Section titled “Frontend (React)”Since this backend emits both text_delta and tool_call events, use onEvent to handle them:
import { useChat } from "@devscalelabs/react-sse-chat";
function Chat() { const { messages, isLoading, sendMessage, stop } = useChat({ api: "http://localhost:8000/chat/", body: { session_id: "3" }, onEvent: (event, helpers) => { switch (event.type) { case "text_delta": helpers.appendText(event.delta); break; case "tool_call": helpers.appendPart({ type: "tool_call", tool_name: event.tool_name, argument: event.argument, }); break; } }, });
return ( <div> {messages.map((msg) => ( <div key={msg.id}> <strong>{msg.role}:</strong> {msg.parts.map((part, i) => { switch (part.type) { case "text": return <span key={i} style={{ whiteSpace: "pre-wrap" }}>{part.text}</span>; case "tool_call": return ( <pre key={i} style={{ display: "block", opacity: 0.7, fontSize: "0.85em" }}> [Tool: {part.tool_name}]{"\n"} {JSON.stringify(JSON.parse(part.argument), null, 2)} </pre> ); default: return null; } })} </div> ))}
{isLoading && <button onClick={stop}>Stop</button>}
<form onSubmit={(e) => { e.preventDefault(); const input = e.currentTarget.elements.namedItem("message") as HTMLInputElement; sendMessage(input.value); input.value = ""; }} > <input name="message" placeholder="Type a message..." /> <button type="submit" disabled={isLoading}>Send</button> </form> </div> );}Run the Example
Section titled “Run the Example”The full runnable example is in the cookbook/fastapi-agents directory:
git clone https://github.com/devscalelabs/react-sse-chat.gitcd react-sse-chatpnpm installpnpm --filter cookbook-fastapi-agents dev