2e36381576
Phase 1 streaming substrate per PHASE1.md §4.
curl.post_sse(url, body, headers, on_event, timeout_ms)
-> true | (nil, errmsg)
Reuses the Phase 0 WRITEFUNCTION hook. Each chunk delivery accumulates
into a per-request buffer; the buffer is drained for complete events
(\n\n-terminated). Each event's `data: ...` field(s) are joined per the
SSE spec and passed to on_event(data_string) synchronously. `:` comment
lines (keepalives) are filtered.
The `[DONE]` sentinel is passed through to on_event as-is (broker.lua
filters it — this module stays HTTP-layer only, no JSON / OpenAI shape
knowledge).
Two robustness items:
- End-of-stream flush: the final event may lack \n\n if the server
closes-on-EOF immediately after the last data: line (some llama.cpp
builds, plain HTTP/1.0 close-on-EOF feeds). Post-perform, any
remaining buffer is parsed as one last event.
- FAILONERROR: a non-2xx response surfaces as a CURLcode error rather
than silently feeding the error body into the SSE parser.
Smoke:
[1] canned events via nc listener: 3 events parsed in order
[2] chunk-split mid-event ("Hel" + sleep + "lo..."): correctly
reassembled across two WRITEFUNCTION deliveries
[3] LIVE against hossenfelder.fritz.box:8082 fast preset with
stream:true: response "pong" assembled from incremental deltas;
4 raw events (role + 1 content + finish_reason + [DONE])
Next: broker.lua chat_stream that decodes the OpenAI delta shape on
top of this and exposes on_delta(content_string) for renderer streaming.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>