feat(capi): ABI v5 - surface <EOU> vs <EOB> distinction across the C boundary - #24
Merged
Merged
Conversation
…boundary
The streaming C API conflated end-of-utterance and backchannel: the feed
eou_out flag and the JSON "eou" field were 1 for either token, so a voice
agent could not tell "the user yielded the turn" (<EOU>, respond) from
"the user acknowledged while listening" (<EOB>, do not treat as a turn).
- parakeet_capi_stream_feed's *eou_out is now an event bitmask
(PARAKEET_EVENT_EOU | PARAKEET_EVENT_EOB) instead of an any-event 0/1
- the streaming JSON documents carry separate "eou"/"eob" 0/1 flags plus
an "events" array ({"type":"eou"|"eob","frame","t"}) with per-event
timestamps
- new parakeet_capi_stream_drain_events / parakeet_capi_free_events
expose typed per-event records (token, is_eob, encoder_frame,
time_sec) on the flat API; the queue is shared with the JSON entry
points
- StreamingSession gains a non-draining events() peek; the C layer
watermarks the queue to attribute events to one feed pass (a feed can
span several encoder chunks, which last_chunk_had_eou() cannot express)
- decoder reset-on-<EOU>/<EOB> is deliberately unchanged (matches NeMo's
reference driver); the distinction is a downstream turn-taking signal
- tests: both C-API stream tests gain a two-utterance-clip phase (shared
builder in tests/stream_clips.hpp, same construction as
gen_stream_reset_baseline.py) asserting the typed mid-stream <EOU> and
exact flag/mask agreement with the drained queue
Verified against mudler/parakeet-cpp-gguf realtime_eou_120m-v1-f16: all
61 ctest targets pass; the two-utterance clip yields the mid-stream
<EOU> at t=7.84s with mask=PARAKEET_EVENT_EOU.
Assisted-by: Claude:claude-fable-5 [Claude Code]
mudler
approved these changes
Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
EOU and EOB are treated as the same thing, but they are very different.
The consumer may want to ignore messages marked as EOB altogether.
So this separates them in the C API.
The streaming C API conflated end-of-utterance and backchannel: the feed
eou_out flag and the JSON "eou" field were 1 for either token, so a voice
agent could not tell "the user yielded the turn" (, respond) from
"the user acknowledged while listening" (, do not treat as a turn).
(PARAKEET_EVENT_EOU | PARAKEET_EVENT_EOB) instead of an any-event 0/1
an "events" array ({"type":"eou"|"eob","frame","t"}) with per-event
timestamps
expose typed per-event records (token, is_eob, encoder_frame,
time_sec) on the flat API; the queue is shared with the JSON entry
points
watermarks the queue to attribute events to one feed pass (a feed can
span several encoder chunks, which last_chunk_had_eou() cannot express)
reference driver); the distinction is a downstream turn-taking signal
builder in tests/stream_clips.hpp, same construction as
gen_stream_reset_baseline.py) asserting the typed mid-stream and
exact flag/mask agreement with the drained queue
Verified against mudler/parakeet-cpp-gguf realtime_eou_120m-v1-f16: all
61 ctest targets pass; the two-utterance clip yields the mid-stream
at t=7.84s with mask=PARAKEET_EVENT_EOU.
Assisted-by: Claude:claude-fable-5 [Claude Code]