Skip to content

fix(voice): clamp the user_speaking span end to its start - #2536

Open
rosetta-livekit-bot[bot] wants to merge 3 commits into
mainfrom
fix/user-speaking-span-clamp
Open

rosetta-livekit-bot[bot] wants to merge 3 commits into
mainfrom
fix/user-speaking-span-clamp

Conversation

@rosetta-livekit-bot

@rosetta-livekit-bot rosetta-livekit-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • clamp a backdated user_speaking span end to the recorded span start
  • preserve valid positive span durations
  • add a patch changeset for @livekit/agents

Ports livekit/agents#7332. Fixes livekit/agents#7307.

Testing

  • pnpm test agents/src/voice/audio_recognition_span.test.ts (11 passed)
  • pnpm --filter @livekit/agents typecheck
  • pnpm --filter @livekit/agents build
  • pnpm build (40 workspace packages built)
  • pnpm --filter @livekit/agents lint (passes with existing warnings)
  • pnpm exec prettier --check "agents/src/**/*.{ts,tsx,md,json}"
  • pnpm test agents (2,721 passed, 5 skipped; 3 unrelated agents/src/telemetry/loop_monitor.test.ts failures because this host falls back to process CPU accounting while those tests require thread CPU accounting)
  • cue-cli voice validation attempted; the configured LiveKit endpoint rejected both the worker and driven turn with 401 Unauthorized - invalid API key, so framework-event validation could not complete

Source diff coverage

Classification of every file in livekit/agents#7332
Source file Classification Target coverage
livekit-agents/livekit/agents/voice/agent_session.py Adapted agents/src/voice/agent_session.ts: stores the explicit millisecond start anchor alongside the span and clamps the end against it, keeping the behavior independent of the tracer provider's span implementation.
tests/test_user_speaking_span.py Adapted agents/src/voice/audio_recognition_span.test.ts: ports both source cases into the existing JS span-test counterpart, covering a 550 ms backdated end and an unchanged 1,250 ms positive duration.
tests/trace_schema.py Not applicable This is the Python repository's offline OTLP trace-schema validator. agents-js has no equivalent trace-schema validator or span-record model, and runtime behavior does not depend on it.
tests/test_trace_schema.py Not applicable Its only source change tests the Python-only offline validator above; there is no target counterpart.

No source runtime behavior depends on the Python-only validator omitted above.


Ported from livekit/agents#7332

Original PR description

Problem: The user_speaking span starts on the STT turn anchor and ends on a VAD anchor that is backdated by silence_duration + inference_duration. The two detectors do not share a clock, so on a short burst the end lands before the start, and OpenTelemetry keeps the duration unsigned — the reporter's backend read 18446744073500970us.

Fix: The end is clamped to the span start, so the span is zero-length instead of negative. check_trace in tests/trace_schema.py now reports any span that ends before it starts.

Fixes #7307.

Context for reviewing and coding agents

How to see it

tests/test_user_speaking_span.py::test_backdated_end_is_clamped_to_the_start fails on main. It opens the span on one anchor, closes it on an anchor 0.55 s earlier, and asserts end_time == start_time. The second case in that file closes 1.25 s later and asserts the duration is kept, so the clamp cannot swallow a real one.

Why the end is clamped and not re-anchored

The backdating is deliberate and feeds the end-of-turn delay metrics, so both anchors must stay. The VAD and the STT genuinely disagree here: the VAD places the end of speech before the STT places the start, and a zero-length span is the honest record of that. _end_eou_wait_span already resolves the same conflict the same way at audio_recognition.py:2093.

Blast radius

user_speaking was the only span that ends on an anchor a different detector produced. The other explicit ends cannot go negative: audio_recognition.py:2079 and 2104 end at time.time_ns(), 2124 is the clamp that already exists, and telemetry/loop_monitor.py:462 computes its end as start_ns + duration.

Where the numbers came from

The reporter's OTLP export holds one span with start 1789557796406789376 and end 1789557796198206208, a gap of 208.6 ms. Its speaking -> listening log line is stamped 575.8 ms after that end value, which is Silero's default min_silence_duration of 0.55 s plus inference. python -m tests.trace_schema <export> over that file reports that span and none of the other 144.

@rosetta-livekit-bot
rosetta-livekit-bot Bot requested a review from a team as a code owner September 19, 2026 00:31
@changeset-bot

changeset-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c9835da

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-azure Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-krisp Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-meta Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-protoface Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

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.

Otel span containing incorrectly long duration

0 participants