SSE en formato Claude Code CLI via ?format=claude

Nuevo ClaudeFormatEmitter traduce eventos nativos al formato exacto
que produce Claude Code CLI: content_block_start/delta/stop, tool_result,
assistant snapshots, result con usage/cost, done.

- streaming/claude_format.py: ClaudeFormatEmitter + DualEmitter
- base.py: enriquecer eventos con tool_call_id, raw_output, tool_arguments
- engine.py: usage/cost en EXECUTION_COMPLETED
- routes.py: ?format=claude en /sessions/{id}/stream
- main.py: DualEmitter wiring (emite a ambos formatos)

El frontend puede consumir el stream sin cambios — mismos event types
que Claude Code CLI. El formato nativo sigue disponible para el dashboard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jordan Diaz
2026-04-03 18:48:07 +00:00
parent 6978764540
commit df7dfbc280
5 changed files with 382 additions and 24 deletions

View File

@@ -68,13 +68,15 @@ def set_dependencies(
context_engine: Any,
memory_store: Any,
sse_emitter: Any,
mcp_registry: Any,
claude_emitter: Any = None,
mcp_registry: Any = None,
) -> None:
_deps["storage"] = storage
_deps["model_adapter"] = model_adapter
_deps["context_engine"] = context_engine
_deps["memory_store"] = memory_store
_deps["sse"] = sse_emitter
_deps["claude_sse"] = claude_emitter
_deps["mcp_registry"] = mcp_registry
@@ -207,22 +209,33 @@ async def _execute_and_persist(orchestrator, storage, session, message) -> dict[
# ------------------------------------------------------------------
@router.get("/sessions/{session_id}/stream")
async def stream_session(session_id: str) -> StreamingResponse:
async def stream_session(session_id: str, format: str = "native") -> StreamingResponse:
storage = _get_storage()
session = await storage.get_session(session_id)
if not session:
raise HTTPException(status_code=404, detail="Session not found")
sse = _get_sse()
headers = {
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
}
if format == "claude":
claude_sse = _deps.get("claude_sse")
if not claude_sse:
raise HTTPException(status_code=501, detail="Claude format emitter not available")
return StreamingResponse(
claude_sse.subscribe(session_id),
media_type="text/event-stream",
headers=headers,
)
sse = _get_sse()
return StreamingResponse(
sse.subscribe(session_id),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
},
headers=headers,
)