feat: add streaming responses to API and CLI
Add real-time streaming support for agent responses using Server-Sent Events (SSE). Responses now appear as they're generated instead of waiting for completion. - Add run_stream method to BaseAgent and ExploreAgentImpl - Add /agents/stream SSE endpoint to API router - Add run_agent_stream method to CLI client - Add --stream flag to chat and explore commands (enabled by default) - Use --no-stream for batch mode with spinner Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
REST API routes for agents.
|
||||
"""
|
||||
import json
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from src.domains.agents.base import get_agent, list_agents
|
||||
|
||||
@@ -68,6 +70,53 @@ async def run_agent(request: AgentRunRequest) -> AgentRunResponse:
|
||||
)
|
||||
|
||||
|
||||
@router.post("/stream")
|
||||
@logged()
|
||||
async def stream_agent(request: AgentRunRequest) -> StreamingResponse:
|
||||
"""
|
||||
Run an agent with streaming response.
|
||||
|
||||
Returns Server-Sent Events (SSE) with text chunks.
|
||||
Event types:
|
||||
- "chunk": Text chunk from the agent
|
||||
- "done": Stream complete
|
||||
- "error": Error occurred
|
||||
"""
|
||||
agent = get_agent(request.agent_type)
|
||||
if not agent:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Unknown agent type: {request.agent_type}"
|
||||
)
|
||||
|
||||
async def generate():
|
||||
try:
|
||||
async for chunk in agent.run_stream(
|
||||
request.prompt,
|
||||
working_dir=request.working_dir,
|
||||
):
|
||||
# SSE format: data: {json}\n\n
|
||||
event = {"event": "chunk", "data": chunk}
|
||||
yield f"data: {json.dumps(event)}\n\n"
|
||||
|
||||
# Signal completion
|
||||
yield f"data: {json.dumps({'event': 'done'})}\n\n"
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Stream error: {e}")
|
||||
error_event = {"event": "error", "data": str(e)}
|
||||
yield f"data: {json.dumps(error_event)}\n\n"
|
||||
|
||||
return StreamingResponse(
|
||||
generate(),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{agent_type}", response_model=AgentInfo)
|
||||
async def get_agent_info(agent_type: str) -> AgentInfo:
|
||||
"""Get information about a specific agent."""
|
||||
|
||||
Reference in New Issue
Block a user