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:
@@ -5,6 +5,7 @@ Fast codebase exploration with read-only tools.
|
||||
Uses sanitized Ollama provider for reliable tool calling.
|
||||
"""
|
||||
import os
|
||||
from collections.abc import AsyncIterator
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
@@ -113,6 +114,34 @@ class ExploreAgentImpl(BaseAgent):
|
||||
raise
|
||||
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
prompt: str,
|
||||
working_dir: str | None = None,
|
||||
allowed_paths: list[str] | None = None,
|
||||
**kwargs: Any
|
||||
) -> AsyncIterator[str]:
|
||||
"""
|
||||
Run the explore agent with streaming output.
|
||||
|
||||
Yields text chunks as they become available.
|
||||
"""
|
||||
ctx = ExploreContext(
|
||||
working_dir=working_dir or os.getcwd(),
|
||||
allowed_paths=allowed_paths or self._settings.effective_allowed_paths,
|
||||
timeout_seconds=self._settings.tool_timeout_seconds,
|
||||
)
|
||||
|
||||
async with trace_span("explore_agent_stream"):
|
||||
try:
|
||||
async with self.agent.run_stream(prompt, deps=ctx) as result:
|
||||
async for chunk in result.stream_text():
|
||||
yield chunk
|
||||
except Exception as e:
|
||||
logger.exception(f"Explore agent stream error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
# Create and register the singleton instance
|
||||
explore_agent = ExploreAgentImpl()
|
||||
register_agent(explore_agent)
|
||||
@@ -125,3 +154,13 @@ async def explore(
|
||||
) -> str:
|
||||
"""Run exploration query."""
|
||||
return await explore_agent.run(prompt, working_dir=working_dir, **kwargs)
|
||||
|
||||
|
||||
async def explore_stream(
|
||||
prompt: str,
|
||||
working_dir: str | None = None,
|
||||
**kwargs: Any
|
||||
) -> AsyncIterator[str]:
|
||||
"""Run exploration query with streaming."""
|
||||
async for chunk in explore_agent.run_stream(prompt, working_dir=working_dir, **kwargs):
|
||||
yield chunk
|
||||
|
||||
Reference in New Issue
Block a user