feat: streaming incremental (--stream) parseando snapshots de la TUI
Fase 2. Anade modo --stream que emite la respuesta de claude como NDJSON (eventos text_delta + result final), re-parseando snapshots del render. Compone dos funciones nuevas del registry: - pty_capture_stream_go_infra: captura snapshots acumulativos del PTY por canal. - text_prefix_delta_go_core: delta por prefijo comun entre snapshots sucesivos. Por cada snapshot: vt_render -> parse_claude_tui -> delta del Answer. Solo emite text_delta cuando el answer extiende limpiamente al anterior (HasPrefix); los frames no monotonos se reconcilian en el result final. Heuristico y documentado. e2e_check smoke_fake_stream verifica el flujo con el fake TUI (sin gastar claude).
This commit is contained in:
@@ -7,8 +7,10 @@ description: "Alternativa a 'claude -p' que obtiene la respuesta de claude como
|
||||
tags: [cli, claude, terminal, pty, tui, parser]
|
||||
uses_functions:
|
||||
- pty_capture_idle_go_infra
|
||||
- pty_capture_stream_go_infra
|
||||
- vt_render_go_tui
|
||||
- parse_claude_tui_go_tui
|
||||
- text_prefix_delta_go_core
|
||||
uses_types:
|
||||
- claude_tui_parse_go_tui
|
||||
- claude_turn_go_tui
|
||||
@@ -30,6 +32,10 @@ e2e_checks:
|
||||
cmd: "./claude_pipe --bin ./tests/fake_claude.sh --warmup 300ms --step-delay 100ms --idle 700ms --max 5s --format json test"
|
||||
expect_stdout_contains: "\"result\":\"RESPUESTA_FAKE_OK\""
|
||||
timeout_s: 15
|
||||
- id: smoke_fake_stream
|
||||
cmd: "./claude_pipe --stream --bin ./tests/fake_claude.sh --warmup 300ms --step-delay 100ms --snapshot-interval 120ms --idle 700ms --max 5s test"
|
||||
expect_stdout_contains: "\"type\":\"text_delta\""
|
||||
timeout_s: 15
|
||||
---
|
||||
|
||||
# claude_pipe
|
||||
@@ -97,6 +103,27 @@ echo "explica este error" | ./claude_pipe --cwd /home/enmanuel/fn_registry
|
||||
| `--step-delay` | `600ms` | Espera entre teclear el prompt y pulsar Enter. |
|
||||
| `--idle` | `4s` | Corta la captura tras este silencio (respuesta terminada de renderizar). |
|
||||
| `--max` | `120s` | Timeout duro de toda la captura. |
|
||||
| `--stream` | false | Emite la respuesta incrementalmente como NDJSON (`text_delta` por snapshot) y un `result` final. |
|
||||
| `--snapshot-interval` | `150ms` | En `--stream`, cada cuánto se captura y re-parsea la TUI. |
|
||||
|
||||
## Streaming (`--stream`)
|
||||
|
||||
Con `--stream`, la app usa `pty_capture_stream_go_infra` para tomar snapshots del render cada
|
||||
`--snapshot-interval`, re-parsea cada snapshot con `parse_claude_tui_go_tui`, y emite el delta del
|
||||
`Answer` (via `text_prefix_delta_go_core`) como NDJSON, terminando con un `result`:
|
||||
|
||||
```bash
|
||||
./claude_pipe --stream --cwd /home/enmanuel/fn_registry "explica Go en 3 frases"
|
||||
# {"type":"text_delta","text":"Go es un lenguaje..."}
|
||||
# {"type":"text_delta","text":" compilado y concurrente..."}
|
||||
# {"type":"result","subtype":"success","result":"Go es un lenguaje... compilado y concurrente..."}
|
||||
```
|
||||
|
||||
Un programa externo Go (u otro) lo lanza como subprocess y lee líneas. **Es heurístico** (ver
|
||||
Gotchas): la TUI re-renderiza el frame entero, así que solo se emite `text_delta` cuando el nuevo
|
||||
`Answer` extiende limpiamente al anterior; los frames no monótonos (reflow) se reconcilian en el
|
||||
`result` final, cuyo `result` lleva siempre la respuesta completa. Para streaming limpio y
|
||||
monótono nativo sin tocar la TUI, `claude_stream_go_core` (stream-json) es superior.
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
@@ -122,6 +149,9 @@ Si no necesitas pasar por la TUI, usa `claude_stream_go_core` (stream-json) —
|
||||
- **Latencia**: anade `warmup` + `idle` (por defecto ~8s de overhead) sobre el tiempo de respuesta
|
||||
de claude. `claude -p` no tiene ese overhead. Es el precio de ir por la TUI.
|
||||
- **Linux/Unix only**: hereda el PTY POSIX de `pty_capture_idle_go_infra`.
|
||||
- **Streaming**: esta version es one-shot (espera la respuesta completa y luego parsea). El
|
||||
streaming incremental de la TUI esta planificado como fase 2 (requiere capturar snapshots
|
||||
durante el render).
|
||||
- **Streaming heurístico**: `--stream` emite deltas re-parseando snapshots del render. Como la TUI
|
||||
re-renderiza el frame entero, el `Answer` parseado puede no ser monótono (reflow al crecer la
|
||||
respuesta, spinner). Solo se emite `text_delta` cuando el nuevo answer extiende limpiamente al
|
||||
anterior; el `result` final siempre lleva la respuesta completa. Posibles artefactos: deltas que
|
||||
reaparecen, o fragmentos perdidos en un frame intermedio. Si necesitas streaming exacto, usa
|
||||
`claude_stream_go_core` (stream-json).
|
||||
|
||||
Reference in New Issue
Block a user