Skip to content

feat(capi): ABI v5 - surface <EOU> vs <EOB> distinction across the C boundary - #24

Merged
mudler merged 1 commit into
mudler:masterfrom
richiejp:capi-eou-eob-events
Jun 12, 2026
Merged

feat(capi): ABI v5 - surface <EOU> vs <EOB> distinction across the C boundary#24
mudler merged 1 commit into
mudler:masterfrom
richiejp:capi-eou-eob-events

Conversation

@richiejp

Copy link
Copy Markdown
Contributor

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_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-/ 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 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]

…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
mudler merged commit 503d7ec into mudler:master Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants