Skip to content

feat(eot): add agent context, the mini model, and turn length - #8113

Open
chenghao-mou wants to merge 16 commits into
mainfrom
chenghao/feat/AGT-3736-mini-2s-eot-model
Open

chenghao-mou wants to merge 16 commits into
mainfrom
chenghao/feat/AGT-3736-mini-2s-eot-model

Conversation

@chenghao-mou

@chenghao-mou chenghao-mou commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

This combines #7787 into the end-of-turn changes for v1 (cloud) and v1-mini (local), so they ship and get reviewed together.

Agent context (v1): before each cloud prediction, the stream sends the last assistant message as EotInputChatContext. Empty context clears prior text. Local inference stays audio-only.

Mini model (v1-mini): the next local model (livekit-local-inference 0.2.8) reads the last 2.0 s of audio and takes the turn length as a feature. The per-language thresholds are refit for it on eot-bench.

Turn length (both): AudioRecognition reports the user turn start once per turn through on_start_of_turn. The local transport passes the turn length to the model. The cloud transport sends it as turn_samples on inference_start (livekit-protocol 1.1.31, livekit/protocol#1937). predict() now queues the request behind the audio, as flush and the turn start already are, so the count covers every frame captured before the call and the gateway needs no wait. A request replaced or cancelled before it reaches the transport is not sent.

Companions: livekit/agent-gateway#1571 forwards both fields, and livekit/infserve#145 serves the model that reads them.

Warning

Blocked on the livekit-local-inference 0.2.8 release (livekit/agents-private#551). uv.lock still resolves 0.2.7, so CI fails until it ships.

Addresses AGT-3730
Addresses AGT-3736
Addresses AGT-3872

Initial prompt and agent context

Model: Claude Opus 5.5

can you update the agents client too for this new model?

Then, on the thresholds:

I think there is an update to how we should choose the thresholds:

  • run the FP32 model on the eot-bench, and find the best latency for a given interruption rate and interruption rate for a given latency
  • run the int8 version and find the thresholds that matches those stats closely
    […]

The 10% cutoff budget under the default endpointing delays was chosen as the anchor. Then: "yes, open the PR and open the node js PR too."

Later, adding the turn length to the cloud path (AGT-3872):

but that's the input samples, not turn samples

why not report turn samples from the client?

okay, it is shipped, we need to update both clients, gateway, and the infserve PR

Later, combining the client PRs:

Can we also merge the client PRs so I need to ask for review once? the python and node js v1 and v1 mini changes can go into one PR in each repo.

Then, on the snapshot point:

shouldn't we queue inference start and audio frames in the same queue?

yes please, but do not push yet.

Send the last assistant message through EotInputChatContext before each cloud prediction. Clear prior text when the chat context has no assistant text.

Addresses AGT-3730
@chenghao-mou chenghao-mou added turn-detector Issues and requests related to LiveKit's turn detector review_effort:high Needs broad context and substantial validation labels Oct 24, 2026
livekit-local-inference 0.2.8 ships v1.1-mini, which reads the last 2.0 s
of audio and takes the turn length, counted from the user's first speech,
as a feature. The local transport now buffers EOT_MAX_SAMPLES and passes
turn_samples; AudioRecognition marks the turn start on VAD start of speech.

The per-language thresholds follow the int8 refit rule used for the cloud
model: on eot-bench, under the default audio-EOT endpointing (0.3 s, 2.5 s),
the fp32 model's best latency at a 10% cutoff budget sets each language's
target, and the shipped quantized model takes the fastest threshold whose
cutoff rate stays within the fp32 one.
Rename mark_speech_start(lookback) to on_start_of_turn(started_at), after
the endpointing peer on_start_of_speech(started_at). AudioRecognition now
calls it once per turn, where it sets _user_turn_start, so the transport
no longer filters repeated starts and a turn that starts from STT gets a
start too. The stream turns the start time into a duration when called,
so the channel delay can't stretch the turn.
Both on_start_of_turn methods now take started_at; the local transport turns it into a sample position when it reads the sentinel. The drain lag this adds is sub-millisecond on an idle loop, and a long stall already delays inference.
@chenghao-mou
chenghao-mou force-pushed the chenghao/feat/AGT-3736-mini-2s-eot-model branch from cf289f1 to 95b87ba Compare October 27, 2026 10:34
The local transport read the clock when it drained the start-of-turn
sentinel. During a cloud reconnect the audio channel backs up, so after
a fallback to local the turn length was too long by the backlog. The
stream now converts the start time to samples when it queues the
sentinel, and the transport takes that sample count.

- Keep one turn counter in the local transport, and send at least the
  audio the model reads.
- Tell the turn detector only a known speech start. A turn opened at
  end of speech keeps the count from the last flush.
- Correct the memory figure to the measured ~450 MB.
@chenghao-mou
chenghao-mou force-pushed the chenghao/feat/AGT-3736-mini-2s-eot-model branch from 2d7017d to c12b0b5 Compare October 27, 2026 12:01
The sentinel carries the elapsed time measured when it is queued, and
the local transport converts it to samples. The name no longer collides
with the turn_samples passed to predict, which counts up to inference.
The cloud end-of-turn model reads the turn duration too. The cloud transport
counts the turn the way the local transport does, from the turn start since
the last flush, and sends it as turn_samples on inference_start
(livekit-protocol 1.1.31). The gateway adds the audio that reaches it later.

Addresses AGT-3872
…-context' into chenghao/feat/AGT-3736-mini-2s-eot-model

# Conflicts:
#	livekit-agents/livekit/agents/inference/eot/transports.py
…6-mini-2s-eot-model

# Conflicts:
#	livekit-agents/pyproject.toml
@chenghao-mou chenghao-mou changed the title feat(eot): feed the local mini model its 2.0 s window and turn length feat(eot): add agent context, the mini model, and turn length Nov 5, 2026
predict() called the transport directly, so frames captured before it but
still queued went out after inference_start: the turn length left them out,
and the gateway had to wait for later audio to collect them. predict() now
queues the request behind the audio, as flush and the turn start already are.
The transport runs it after every earlier frame, so turn_samples covers all
audio captured before the call, and a request replaced before it runs is not
sent. The request carries a snapshot of the chat context items.

Addresses AGT-3872
@chenghao-mou
chenghao-mou marked this pull request as ready for review November 5, 2026 13:34
@chenghao-mou
chenghao-mou requested a review from a team as a code owner November 5, 2026 13:34

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Devin Review

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review_effort:high Needs broad context and substantial validation turn-detector Issues and requests related to LiveKit's turn detector

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant