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:
@@ -3,7 +3,9 @@ Webber API client.
|
||||
|
||||
Communicates with the Webber API backend for agent execution.
|
||||
"""
|
||||
import json
|
||||
import httpx
|
||||
from collections.abc import AsyncIterator
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
@@ -132,6 +134,52 @@ class WebberClient:
|
||||
error=data.get("error"),
|
||||
)
|
||||
|
||||
async def run_agent_stream(
|
||||
self,
|
||||
agent_type: str,
|
||||
prompt: str,
|
||||
working_dir: str = ".",
|
||||
) -> AsyncIterator[str]:
|
||||
"""
|
||||
Run an agent with streaming response.
|
||||
|
||||
Args:
|
||||
agent_type: Type of agent (e.g., "explore")
|
||||
prompt: User prompt/query
|
||||
working_dir: Working directory for the agent
|
||||
|
||||
Yields:
|
||||
Text chunks as they arrive
|
||||
"""
|
||||
# Use a fresh client for streaming with longer timeout
|
||||
async with httpx.AsyncClient(
|
||||
base_url=self.base_url,
|
||||
timeout=httpx.Timeout(300.0, connect=10.0),
|
||||
) as client:
|
||||
async with client.stream(
|
||||
"POST",
|
||||
"/agents/stream",
|
||||
json={
|
||||
"agent_type": agent_type,
|
||||
"prompt": prompt,
|
||||
"working_dir": working_dir,
|
||||
},
|
||||
) as response:
|
||||
response.raise_for_status()
|
||||
async for line in response.aiter_lines():
|
||||
if line.startswith("data: "):
|
||||
try:
|
||||
data = json.loads(line[6:])
|
||||
event = data.get("event")
|
||||
if event == "chunk":
|
||||
yield data.get("data", "")
|
||||
elif event == "error":
|
||||
raise Exception(data.get("data", "Unknown error"))
|
||||
elif event == "done":
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
async def __aenter__(self) -> "WebberClient":
|
||||
"""Async context manager entry."""
|
||||
return self
|
||||
|
||||
Reference in New Issue
Block a user