deepseek streams LLM output token-by-token over Server-Sent Events
(SSE) by default. Both OpenAI-compatible (/chat/completions) and
Anthropic-compatible (/messages) base URLs are supported.
When stdout is a TTY, the renderer applies ANSI:
- Cyan: assistant text (streamed live)
- Yellow
🛠 name(args): tool call (rendered after args fully assembled) - Green
✓ name: tool succeeded - Red
✗ name: tool failed - Dim
─── step N ───: step divider
Pipe stdout to a file (deepseek run "..." > out.txt) and ANSI is
suppressed automatically — is_terminal() detects the redirection.
curl -sS -N --max-time 60 is spawned per LLM call. The -N
(no-buffer) flag is critical — without it curl batches output by
buffer-fill rather than by SSE frame boundary. Frames are read by
util::sse::read_frame from a BufRead over child stdout. Per
protocol:
- OpenAI:
delta.contentchunks dispatch toon_text_deltalive.delta.tool_calls[]accumulates across frames into a singleOpenAiToolAssemblyand is rendered oncefinish_reason == "tool_calls". Finalusageframe (requested viastream_options.include_usage) carries token counts.[DONE]closes the stream. - Anthropic:
event: text_deltachunks dispatch live.event: input_json_deltaaccumulatespartial_jsonfor tool-use blocks, parsed oncecontent_block_stoparrives.message_deltacarries output tokens;message_stopcloses the stream.
- Curl exit non-zero →
tool_failurewith stderr tail (last 64 KB) - HTTP 4xx/5xx errors emit an
errorframe →tool_failure(api error) - Malformed SSE →
tool_failure(malformed sse frame: ...) - Tool args JSON unparseable after assembly →
tool_failure(...)
Already-streamed tokens are not rolled back on error — the red ✗
mark appears after them, matching Claude Code / Codex behavior. The
parser still calls on_assistant_done exactly once even on error,
preserving the StreamEvents trait contract so the renderer can
close cyan ANSI cleanly.
When DEEPSEEK_API_KEY is unset, the offline planner runs locally
and still drives StreamEvents, so the renderer paints the same
colors. Tokens arrive in one block rather than progressively.
- Ctrl+C does not interrupt an in-flight stream
up/downarrow history not implemented (userlwrap deepseek)- Patch / shell tool output not streamed (rendered in one block)
- Syntax highlight not applied
- Color theme not user-configurable