-
Notifications
You must be signed in to change notification settings - Fork 2.7k
fix streaming tts retries #4410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix streaming tts retries #4410
Conversation
| ctx.waiter.set_exception( | ||
| APIError("11labs stream ended without audio", retryable=True) | ||
| ) | ||
| self.mark_non_current() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why mark_non_current is needed when one of the generation failed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just ensures the next attempt gets a fresh connection
|
|
||
| if data.get("isFinal"): | ||
| if not ctx.received_audio and not ctx.waiter.done(): | ||
| # ElevenLabs sometimes returns `isFinal` with an empty `audio` payload. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a valid case that elevenlabs returns final without audio, like when the pushed text is empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, empty/whitespace input returns isFinal with audio: null. I added a sent_text guard so we only error when real text was sent
livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py
Outdated
Show resolved
Hide resolved
📝 WalkthroughWalkthroughImplements replayable streaming input and per-attempt reset for TTS streaming retries; adds Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant TTS_Agent as TTS Agent\n(tts.py)
participant InputBuffer as Input Buffer\n(_replay_events)
participant Plugin as TTS Plugin\n(_run)
participant Output as AudioEmitter
Client->>TTS_Agent: start synthesize_stream(request)
TTS_Agent->>InputBuffer: push_text / flush / end_input (record events)
TTS_Agent->>Plugin: start _run with input channel
Plugin->>Output: produce audio (attempt `#1`)
alt attempt fails and should_retry
Plugin-->>TTS_Agent: emit recoverable error
TTS_Agent->>TTS_Agent: wait, increment attempt
TTS_Agent->>Plugin: call SynthesizeStream._reset_for_retry()
TTS_Agent->>InputBuffer: replay events into new input channel
TTS_Agent->>Plugin: restart _run with reconstructed channel (attempt `#2`)
Plugin->>Output: produce audio (attempt `#2`)
else non-retryable or retries exhausted
Plugin-->>TTS_Agent: emit non-recoverable error / raise
end
alt Output.has_pushed_audio() is true
Output-->>Client: deliver audio stream
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (1)**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-01-22T03:28:16.289ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
🔇 Additional comments (10)
✏️ Tip: You can disable this entire section by setting Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (2)
483-484: Missingend_segment()call on early return.When
text_partsis empty, the function returns early at line 484, butstart_segment()was already called at line 471. This leaves the segment in an inconsistent state without a correspondingend_segment()call.Proposed fix
if not text_parts: + output_emitter.end_segment() return
511-511: Incorrect method call:end_input()should beend_segment().Other TTS plugins (Deepgram, Neuphonic, Gradium) call
output_emitter.end_segment()to finalize a segment. Usingend_input()here appears to be a bug that could cause segment tracking issues.Proposed fix
- output_emitter.end_input() + output_emitter.end_segment()
♻️ Duplicate comments (1)
livekit-agents/livekit/agents/tts/tts.py (1)
572-585: Close the previous input channel when swapping.
Leaving the old channel open can leak resources or leave readers hanging after a failed attempt.🔧 Suggested fix
- ch = aio.Chan[Union[str, SynthesizeStream._FlushSentinel]]() + old_ch = self._input_ch + ch = aio.Chan[Union[str, SynthesizeStream._FlushSentinel]]() for ev in self._replay_events: ch.send_nowait(ev) if self._input_ended: ch.close() self._input_ch = ch + old_ch.close()
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
livekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-deepgram/livekit/plugins/deepgram/tts.pylivekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.pylivekit-plugins/livekit-plugins-google/livekit/plugins/google/tts.pylivekit-plugins/livekit-plugins-gradium/livekit/plugins/gradium/tts.pylivekit-plugins/livekit-plugins-neuphonic/livekit/plugins/neuphonic/tts.pylivekit-plugins/livekit-plugins-nvidia/livekit/plugins/nvidia/tts.pylivekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.pylivekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-plugins/livekit-plugins-nvidia/livekit/plugins/nvidia/tts.pylivekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.pylivekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.pylivekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-google/livekit/plugins/google/tts.pylivekit-plugins/livekit-plugins-neuphonic/livekit/plugins/neuphonic/tts.pylivekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.pylivekit-plugins/livekit-plugins-deepgram/livekit/plugins/deepgram/tts.pylivekit-plugins/livekit-plugins-gradium/livekit/plugins/gradium/tts.py
🧠 Learnings (1)
📚 Learning: 2026-01-22T03:28:16.289Z
Learnt from: longcw
Repo: livekit/agents PR: 4563
File: livekit-agents/livekit/agents/beta/tools/end_call.py:65-65
Timestamp: 2026-01-22T03:28:16.289Z
Learning: In code paths that check capabilities or behavior of the LLM processing the current interaction, prefer using the activity's LLM obtained via ctx.session.current_agent._get_activity_or_raise().llm instead of ctx.session.llm. The session-level LLM may be a fallback and not reflect the actual agent handling the interaction. Use the activity LLM to determine capabilities and to make capability checks or feature toggles relevant to the current processing agent.
Applied to files:
livekit-agents/livekit/agents/tts/tts.py
🧬 Code graph analysis (6)
livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.py (2)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
SentenceStream(32-64)
livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (5)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
SentenceStream(32-64)livekit-plugins/livekit-plugins-nvidia/livekit/plugins/nvidia/tts.py (1)
_process_segments(149-152)livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.py (1)
_process_segments(277-279)livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (1)
_process_segments(445-448)
livekit-agents/livekit/agents/tts/tts.py (2)
livekit-agents/livekit/agents/utils/aio/channel.py (6)
send_nowait(32-32)send_nowait(90-98)close(34-34)close(42-42)close(133-146)Chan(49-178)livekit-agents/livekit/agents/tokenize/token_stream.py (1)
flush(70-91)
livekit-plugins/livekit-plugins-neuphonic/livekit/plugins/neuphonic/tts.py (3)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
SentenceStream(32-64)livekit-agents/livekit/agents/tokenize/token_stream.py (2)
push_text(35-67)end_input(93-95)
livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (2)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (2)
WordStream(80-108)SentenceStream(32-64)
livekit-plugins/livekit-plugins-gradium/livekit/plugins/gradium/tts.py (2)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
WordStream(80-108)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.9)
- GitHub Check: unit-tests
🔇 Additional comments (29)
livekit-plugins/livekit-plugins-gradium/livekit/plugins/gradium/tts.py (1)
269-296: LGTM - Local channel enables proper retry behavior.Moving
segments_chfrom an instance attribute to a local variable ensures a fresh channel is created for each_runinvocation, correctly fixing the retry issue where the old channel would be exhausted.Minor nit: the comment on line 279 still references
_segments_chbut the code now usessegments_ch.livekit-plugins/livekit-plugins-google/livekit/plugins/google/tts.py (1)
326-380: LGTM - Consistent with the retry fix pattern.The local
segments_chscoped to_runensures a fresh channel for each retry attempt. The implementation correctly handles the tokenization → segmentation → synthesis pipeline.livekit-plugins/livekit-plugins-deepgram/livekit/plugins/deepgram/tts.py (1)
257-284: LGTM - Local channel enables proper retry behavior.The refactor correctly scopes
segments_chto the_runmethod, ensuring retries get a fresh channel.Minor nit: the comment on line 267 still references
_segments_chbut the code now usessegments_ch.livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (1)
413-448: LGTM - Local channel enables proper retry behavior.The refactor correctly scopes
segments_chto the_runmethod. The Union typetokenize.WordStream | tokenize.SentenceStreamappropriately handles both tokenizer types.livekit-plugins/livekit-plugins-neuphonic/livekit/plugins/neuphonic/tts.py (1)
307-353: LGTM - Consistent with the retry fix pattern.The local
segments_chscoped to_runensures a fresh channel for each retry attempt. The implementation is clean and follows the established pattern across all TTS plugins.livekit-plugins/livekit-plugins-nvidia/livekit/plugins/nvidia/tts.py (4)
126-139: LGTM - Proper per-run state initialization.All state (
context_id,sent_tokenizer_stream,token_q,event_loop,done_fut) is now scoped locally to_run, enabling correct retry behavior. The capturedevent_loopis correctly used forcall_soon_threadsafecalls from the worker thread.
141-152: LGTM - Input processing pipeline.The tokenization flow correctly uses the local
sent_tokenizer_streamand queues tokens for the worker thread. The sentinelNoneproperly signals end-of-input.
154-178: LGTM - Worker thread implementation.The worker correctly pulls from the local
token_q, calls the Riva service synchronously, and usescall_soon_threadsafeto safely push audio to the output emitter. Thedone_fut.set_resultin the finally block ensures proper completion signaling.
192-198: LGTM - Cleanup logic.The defensive
token_q.put(None)in the finally block ensures the worker thread exits even if_process_segmentsis cancelled. Awaitingdone_futbeforeend_segment()ensures all audio is emitted. Closingsent_tokenizer_streamviaaclose()properly releases resources.livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (3)
458-458: Per-run_segments_chaddresses the retry issue.The change from a persistent instance-level
_segments_chto an optional field initialized per-run is the correct fix. This ensures that on retries, a fresh channel is created instead of reusing a consumed/closed one.Regarding the past review comment asking why
self._segments_chis reserved: it's kept as an instance field (rather than purely local) so thataclose()can properly close it during cleanup if the stream is terminated mid-run.
473-474: LGTM!Creating the segments channel at the start of
_runand storing a reference inself._segments_chcorrectly implements the per-run lifecycle pattern, enabling retries to work with fresh channels.
827-840: LGTM!The dynamic channel list construction correctly handles the optional
_segments_chlifecycle, preventing cleanup errors whenaclose()is called before or after_run()completes.livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.py (7)
374-378: LGTM!Resetting
_context_id,_text_buffer, and alignment lists at the start of_runensures clean state for retries.
389-389: LGTM!Creating
sent_tokenizer_streamlocally per-run (instead of per-instance) is the core fix that enables retries to work correctly.
542-544: LGTM!The
is_openproperty provides a proper tri-condition liveness check that prevents using stale WebSocket connections.
575-578: LGTM!Only marking
sent_text = Truewhen actual non-empty text is sent correctly distinguishes between empty flushes and real content, which is essential for the retry detection logic.
732-745: Addresses the retry detection correctly.The logic properly handles the edge case raised in the past review comment: empty input legitimately returns
isFinalwithout audio, so thectx.sent_textcheck ensures we only treat it as a retryable failure when text was actually sent but no audio was received.Regarding the past comment about
mark_non_current: this is appropriate because a connection that fails to produce audio for sent text may be in a degraded state, so marking it non-current ensures subsequent streams use a fresh connection.
463-464: LGTM!Expanding the exception handling to include
APIError,APIConnectionError, andAPITimeoutErrorensures the new retryable error fromisFinalhandling is properly propagated.
469-469: LGTM!Closing
sent_tokenizer_streamin thefinallyblock ensures proper cleanup and prevents resource leaks on both success and failure paths.livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.py (2)
248-248: LGTM!Creating
segments_chlocally in_runcorrectly implements the per-run channel lifecycle for retry support. Unlike the sarvam plugin (which keeps an optional instance field for cleanup inaclose), the resemble implementation uses a purely local channel which is appropriate since the channel's lifetime is fully contained within_run.
257-279: LGTM!The tokenization and segment processing logic correctly uses the local
segments_chthroughout, consistent with the pattern applied across other plugins (upliftai, nvidia, etc.).livekit-agents/livekit/agents/tts/tts.py (8)
280-299: Retry gating looks correct and safer.
The addedhas_pushed_audio()check prevents retrying after partial output.
360-362: Replay state initialization looks good.
397-402: Retry reset hook is appropriately placed.
430-445: Streaming retry gating is consistent with non-streaming.
542-549: Buffering text for replay and lazy metrics start are solid.
562-565: Flush sentinel replay is correct.
569-570: End-of-input state tracking looks right.
663-665: Helper method is clear and useful for retry logic.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
livekit-agents/livekit/agents/tts/tts.py (1)
526-570: Potential token loss during retry reset.
push_text()returns when the input channel is closed, and_reset_for_retry()closes the channel before swapping it, so tokens pushed during the retry window can be dropped and never replayed. Consider serializingpush_text/flush/end_inputwith_reset_for_retry, or buffering events during reset to preserve order.Also applies to: 572-587
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
livekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py
🧠 Learnings (1)
📚 Learning: 2026-01-22T03:28:16.289Z
Learnt from: longcw
Repo: livekit/agents PR: 4563
File: livekit-agents/livekit/agents/beta/tools/end_call.py:65-65
Timestamp: 2026-01-22T03:28:16.289Z
Learning: In code paths that check capabilities or behavior of the LLM processing the current interaction, prefer using the activity's LLM obtained via ctx.session.current_agent._get_activity_or_raise().llm instead of ctx.session.llm. The session-level LLM may be a fallback and not reflect the actual agent handling the interaction. Use the activity LLM to determine capabilities and to make capability checks or feature toggles relevant to the current processing agent.
Applied to files:
livekit-agents/livekit/agents/tts/tts.py
🧬 Code graph analysis (2)
livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.py (3)
livekit-agents/livekit/agents/tokenize/token_stream.py (3)
flush(70-91)push_text(35-67)end_input(93-95)livekit-agents/livekit/agents/_exceptions.py (2)
APIStatusError(45-81)APIError(14-42)livekit-agents/livekit/agents/utils/aio/channel.py (1)
closed(152-153)
livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (3)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
SentenceStream(32-64)livekit-agents/livekit/agents/vad.py (2)
_FlushSentinel(99-100)end_input(160-163)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.9)
- GitHub Check: unit-tests
🔇 Additional comments (9)
livekit-agents/livekit/agents/tts/tts.py (4)
281-296: Retry gating avoids duplicating partial audio.
Thehas_pushed_audio()guard makes retry decisions safer once any audio has been emitted.
360-363: Replay buffer + input-end tracking is a solid setup.
This lays the groundwork for deterministic input replay across attempts.
397-446: Retry reset flow aligns streaming with chunked behavior.
Resetting per-attempt state and gating retries on “no audio” keeps retries consistent.
665-667: Helper for retry gating is clear and pragmatic.
Duration-based checks are easy to reason about and fit the retry logic.livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (2)
472-513: Per-run segments channel lifecycle is cleaner.
Localizing the channel and closing it infinallyavoids stale state across retries.
826-833: Cleanup matches the new per-run channel ownership.
Limiting close operations to the input channel avoids touching per-run locals.livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.py (3)
260-264: Connection reuse now guarded by open-state.
This prevents handing out a closed websocket as the “current” connection.Also applies to: 542-545
375-410: Per-run tokenizer stream reset is solid.
Resetting buffers and scoping the tokenizer stream per run prevents cross-attempt leakage, and cleanup infinallyis tidy.Also applies to: 419-469
508-515: Stream context state tracking looks robust.
Tracking per-context “sent/received” state keeps finalization and retry paths well-defined.Also applies to: 575-579, 725-746
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@livekit-agents/livekit/agents/tts/tts.py`:
- Around line 361-363: Replace the asyncio.Lock instance used for synchronous
context manager usage with a threading.Lock: change the attribute
self._input_lock to use threading.Lock() instead of asyncio.Lock() so that the
sync methods push_text(), flush(), end_input(), and _reset_for_retry() can use
"with self._input_lock:" without raising AttributeError; ensure you import
threading at top of the module and leave async usages (if any) unaffected by
this change.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
livekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.pylivekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-agents/livekit/agents/tts/tts.pylivekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.pylivekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py
🧠 Learnings (1)
📚 Learning: 2026-01-22T03:28:16.289Z
Learnt from: longcw
Repo: livekit/agents PR: 4563
File: livekit-agents/livekit/agents/beta/tools/end_call.py:65-65
Timestamp: 2026-01-22T03:28:16.289Z
Learning: In code paths that check capabilities or behavior of the LLM processing the current interaction, prefer using the activity's LLM obtained via ctx.session.current_agent._get_activity_or_raise().llm instead of ctx.session.llm. The session-level LLM may be a fallback and not reflect the actual agent handling the interaction. Use the activity LLM to determine capabilities and to make capability checks or feature toggles relevant to the current processing agent.
Applied to files:
livekit-agents/livekit/agents/tts/tts.py
🧬 Code graph analysis (3)
livekit-agents/livekit/agents/tts/tts.py (2)
livekit-agents/livekit/agents/stt/stt.py (2)
_FlushSentinel(251-254)flush(407-416)livekit-agents/livekit/agents/utils/aio/channel.py (7)
closed(152-153)send_nowait(32-32)send_nowait(90-98)close(34-34)close(42-42)close(133-146)Chan(49-178)
livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (2)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (2)
WordStream(80-108)SentenceStream(32-64)
livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (3)
livekit-agents/livekit/agents/utils/aio/channel.py (1)
Chan(49-178)livekit-agents/livekit/agents/tokenize/tokenizer.py (1)
SentenceStream(32-64)livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/tts.py (1)
_process_segments(277-279)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: type-check (3.9)
- GitHub Check: type-check (3.13)
- GitHub Check: unit-tests
🔇 Additional comments (10)
livekit-plugins/livekit-plugins-sarvam/livekit/plugins/sarvam/tts.py (2)
470-510: Good per-run channel scoping for retry safety.Localizing
segments_chto_runand routing tokenization/processing through it keeps channel lifecycle per-attempt and avoids stale state reuse.
825-827: Cleanup aligns with new channel lifecycle.Closing only
_input_chhere is consistent now thatsegments_chis local to_run.livekit-plugins/livekit-plugins-upliftai/livekit/plugins/upliftai/tts.py (5)
410-414: Per-run channel isolation is a solid fix.Localizing
segments_chensures retries don’t reuse stale channels and aligns with replayable input.
425-433: Tokenization now correctly routes through the per-run channel.Enqueuing the initial
word_streaminto the local channel and closing it at the end makes segment processing deterministic per attempt.Also applies to: 440-443
445-448: Segment processing correctly consumes the local channel.Iterating over
segments_chensures only this run’s streams are processed.
483-485: Empty-text segments are cleanly finalized.Ending the segment before returning prevents dangling segment state when no tokens are produced.
499-512: Segment lifecycle now ends withend_segment().This aligns with streaming semantics and keeps segment boundaries consistent.
livekit-agents/livekit/agents/tts/tts.py (3)
281-296: Retry gating on emitted audio looks solid.This should prevent retries after partial audio emission, which aligns with the retry safety goals.
399-403: Replay + retry decision flow looks consistent.Resetting per-attempt state and gating retries on
has_pushed_audio()make the retry behavior predictable.Also applies to: 431-447
675-676: Nice helper for retry decisions.
has_pushed_audio()makes the retry policy explicit and easy to reason about.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
fixes: #4409
Summary by CodeRabbit
Bug Fixes
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.