feat: streaming del subproceso claude-code con --output-format stream-json

Implementa la Fase 1 del issue 0036: soporte de streaming en tiempo real
para el provider claude-code.

- Tipos puros de streaming en pkg/llm/types.go: StreamEventKind,
  StreamEvent, StreamFunc (pure core, sin side effects)
- Refactor de shell/llm/claudecode.go: nuevo code path executeStreaming
  que usa cmd.StdoutPipe + bufio.Scanner para leer linea a linea
- Parser parseStreamLine que mapea eventos JSON del CLI (system, assistant,
  result) a StreamEvent del dominio
- buildClaudeArgs ahora selecciona --output-format stream-json cuando
  streaming esta habilitado y StreamFunc presente
- Campos Streaming y ShowToolProgress en ClaudeCodeCfg (config schema)
- Backward compatible: streaming=false (default) no cambia comportamiento
- 40 tests (20 existentes + 20 nuevos) pasan sin errores

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 22:53:41 +00:00
parent 0933099365
commit 1bdf9344a2
4 changed files with 738 additions and 39 deletions
+38 -6
View File
@@ -42,13 +42,14 @@ type ToolSpec struct {
}
type CompletionRequest struct {
Model string
Messages []Message
Tools []ToolSpec
MaxTokens int
Temperature float64
Stream bool
Model string
Messages []Message
Tools []ToolSpec
MaxTokens int
Temperature float64
Stream bool
SystemPrompt string
StreamFunc StreamFunc // optional: if set, streaming events are emitted during execution
}
type TokenUsage struct {
@@ -67,3 +68,34 @@ type CompletionResponse struct {
// CompleteFunc is the single contract for LLM providers.
// Implementations live in shell/llm/.
type CompleteFunc func(ctx context.Context, req CompletionRequest) (CompletionResponse, error)
// ── Streaming types (pure) ───────────────────────────────────────────────
// StreamEventKind identifies the kind of streaming event emitted by
// a claude-code subprocess running with --output-format stream-json.
type StreamEventKind string
const (
StreamInit StreamEventKind = "init"
StreamToolUse StreamEventKind = "tool_use"
StreamToolResult StreamEventKind = "tool_result"
StreamText StreamEventKind = "text"
StreamResult StreamEventKind = "result"
StreamError StreamEventKind = "error"
)
// StreamEvent carries a single streaming event from the claude subprocess.
// Fields are populated based on Kind; not all fields are valid for all kinds.
type StreamEvent struct {
Kind StreamEventKind
ToolName string // tool_use: name of the tool being invoked
ToolInput string // tool_use: truncated input description
Content string // text/result: textual content
IsError bool // result: whether the result indicates an error
Error error // error: the error that occurred
}
// StreamFunc is the callback invoked for each streaming event.
// Implementations must be safe for concurrent use (typically not needed
// since the streaming loop calls sequentially).
type StreamFunc func(event StreamEvent)