Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions livekit-agents/livekit/agents/llm/fallback_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ def model(self) -> str:
def provider(self) -> str:
return "livekit"

def prewarm(self, *, loop: asyncio.AbstractEventLoop | None = None) -> None:
"""Pre-warm connections for all contained LLM instances.

Every LLM in the fallback chain is warmed so that a fallback provider has
an already-established connection (DNS resolved, TLS handshaked) and does
not add latency precisely when the primary has just failed.
"""
for llm_instance in self._llm_instances:
llm_instance.prewarm(loop=loop)

def chat(
self,
*,
Expand Down
10 changes: 10 additions & 0 deletions livekit-agents/livekit/agents/stt/fallback_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ def model(self) -> str:
def provider(self) -> str:
return "livekit"

def prewarm(self) -> None:
"""Pre-warm connections for all contained STT instances.

Every STT in the fallback chain is warmed so that a fallback provider has
an already-established connection (DNS resolved, TLS handshaked) and does
not add latency precisely when the primary has just failed.
"""
for stt_instance in self._stt_instances:
stt_instance.prewarm()

def _update_session_keyterms(self, keyterms: list[str]) -> None:
# forward to every underlying STT; unsupported ones warn-and-skip internally
for stt_instance in self._stt_instances:
Expand Down
10 changes: 8 additions & 2 deletions livekit-agents/livekit/agents/tts/fallback_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,14 @@ def stream(
return FallbackSynthesizeStream(tts=self, conn_options=conn_options)

def prewarm(self) -> None:
if self._tts_instances:
self._tts_instances[0].prewarm()
"""Pre-warm connections for all contained TTS instances.

Every TTS in the fallback chain is warmed so that a fallback provider has
an already-established connection (DNS resolved, TLS handshaked) and does
not add latency precisely when the primary has just failed.
"""
for tts_instance in self._tts_instances:
tts_instance.prewarm()

def _on_metrics_collected(self, *args: Any, **kwargs: Any) -> None:
self.emit("metrics_collected", *args, **kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,6 @@ def send_endpoint_transcript() -> None:
if token["is_final"]:
if is_end_token(token):
send_endpoint_transcript()
self._report_processed_audio_duration(
total_audio_proc_ms,
)
else:
final.update(token)
else:
Expand Down Expand Up @@ -602,7 +599,13 @@ def send_endpoint_transcript() -> None:
# 3) on error or finish, flush any remaining final tokens.
if content.get("finished") or has_error:
send_endpoint_transcript()
self._report_processed_audio_duration(total_audio_proc_ms)

# Report processed audio duration on every message. The helper tracks
# `_reported_duration_ms` and only emits a RECOGNITION_USAGE event for
# the delta since the last report, so calling this unconditionally is
# safe and ensures usage is never under-reported when the stream ends
# without a clean endpoint token (e.g. disconnection or error).
self._report_processed_audio_duration(total_audio_proc_ms)

if has_error:
err_code = content.get("error_code")
Expand Down