Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
4a9a1dd
fix(realtime): support externally transcribed user turns
TonyG-FWE Aug 11, 2026
5c4c1ac
test(realtime): initialize recognition fixture state
TonyG-FWE Aug 11, 2026
aec93d6
fix(realtime): isolate external input turn state
TonyG-FWE Aug 11, 2026
90f92f3
fix(realtime): validate external STT input paths
TonyG-FWE Aug 11, 2026
f0d30d3
Merge upstream main into fix/issue-5408-google-realtime-external-input
TonyG-FWE Aug 14, 2026
9dcf6eb
fix(google): preserve realtime input ownership
TonyG-FWE Aug 15, 2026
8c249b8
fix(google): preserve complete deferred audio turns
TonyG-FWE Aug 16, 2026
83b947e
fix(realtime): preserve external turn lifecycle
TonyG-FWE Aug 16, 2026
b7cfbc2
fix(realtime): preserve runtime detector warnings
TonyG-FWE Aug 16, 2026
cc91cab
fix(realtime): retain externally owned turn input
TonyG-FWE Aug 16, 2026
3a8daa9
fix(google): defer video with manual input turns
TonyG-FWE Aug 16, 2026
6079459
Update livekit-plugins/livekit-plugins-google/livekit/plugins/google/…
TonyG-FWE Aug 16, 2026
95b3367
Update livekit-agents/livekit/agents/voice/audio_recognition.py
TonyG-FWE Aug 16, 2026
547969f
test(realtime): capture turn ownership regressions
TonyG-FWE Aug 17, 2026
ecdd26d
Merge remote-tracking branch 'upstream/main' into fix/issue-5408-goog…
TonyG-FWE Aug 17, 2026
71f386f
refactor(realtime): centralize turn policy and ownership
TonyG-FWE Aug 17, 2026
04655c5
fix(realtime): make finalized input synchronization explicit
TonyG-FWE Aug 17, 2026
0a073d2
fix(google): preserve owned input across session restarts
TonyG-FWE Aug 17, 2026
91c96ff
test(realtime): capture fresh ownership regressions
TonyG-FWE Aug 17, 2026
bbbbbe4
fix(realtime): preserve detector policy ownership
TonyG-FWE Aug 17, 2026
c478332
fix(google): quarantine deferred video input
TonyG-FWE Aug 17, 2026
fd55fd7
test(realtime): capture final review regressions
TonyG-FWE Aug 17, 2026
8168644
fix(realtime): preserve finalized turn lifecycle
TonyG-FWE Aug 17, 2026
2caee51
fix(realtime): preserve exact fallback synchronization
TonyG-FWE Aug 17, 2026
3f8db7a
fix(google): preserve owned input across restart
TonyG-FWE Aug 17, 2026
c1fd28e
fix(openai): bound speech timestamp tracking
TonyG-FWE Aug 17, 2026
1cac6b4
test(realtime): capture latest review regressions
TonyG-FWE Aug 17, 2026
658ff6d
fix(realtime): preserve turn update atomicity
TonyG-FWE Aug 17, 2026
f96f437
fix(realtime): keep skipped fallback turns local
TonyG-FWE Aug 17, 2026
3b03c90
test(realtime): capture fallback and shutdown regressions
TonyG-FWE Aug 18, 2026
9a0abc9
fix(realtime): break fallback swap synchronization cycle
TonyG-FWE Aug 18, 2026
99f48a4
fix(realtime): bound shutdown turn settlement
TonyG-FWE Aug 18, 2026
3af88ef
fix(realtime): validate prospective turn policy
TonyG-FWE Aug 18, 2026
204c6df
fix(realtime): preserve message commit compatibility
TonyG-FWE Aug 18, 2026
744b937
test(realtime): make lifecycle races event-driven
TonyG-FWE Aug 18, 2026
4dfcadf
test(realtime): capture empty shutdown turn regression
TonyG-FWE Aug 18, 2026
a533276
fix(realtime): skip empty bounded shutdown turns
TonyG-FWE Aug 18, 2026
6ade6f5
test(realtime): capture final Devin regressions
TonyG-FWE Aug 18, 2026
479d228
fix(realtime): bound fallback model attempts
TonyG-FWE Aug 18, 2026
e890e9d
fix(realtime): preserve provider-owned close history
TonyG-FWE Aug 18, 2026
7ec384a
test(realtime): capture final ownership boundary regressions
TonyG-FWE Aug 18, 2026
1245db9
fix(realtime): preserve explicit provider controls
TonyG-FWE Aug 18, 2026
946be98
fix(realtime): observe background lifecycle failures
TonyG-FWE Aug 18, 2026
ae8a441
test(realtime): capture empty-timeout interruption regression
TonyG-FWE Aug 18, 2026
8db3063
fix(realtime): settle empty audio turns after STT timeout
TonyG-FWE Aug 18, 2026
cba7c0b
Merge remote-tracking branch 'refs/remotes/upstream/main' into fix/is…
TonyG-FWE Aug 19, 2026
349f426
test(voice): capture audio close cancellation regression
TonyG-FWE Aug 19, 2026
d6713e3
fix(voice): preserve audio close cancellation
TonyG-FWE Aug 19, 2026
24c8097
test(realtime): capture close transcript deduplication regression
TonyG-FWE Aug 19, 2026
6e91168
fix(realtime): deduplicate bounded close transcripts
TonyG-FWE Aug 19, 2026
cd2abdd
Merge upstream main into fix/issue-5408-google-realtime-external-input
TonyG-FWE Aug 20, 2026
568feba
Merge upstream main into fix/issue-5408-google-realtime-external-input
TonyG-FWE Aug 22, 2026
685de91
Merge upstream main into fix/issue-5408-google-realtime-external-input
TonyG-FWE Aug 24, 2026
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
4 changes: 4 additions & 0 deletions livekit-agents/livekit/agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
APITimeoutError,
AssignmentTimeoutError,
UnexpectedModelBehavior,
UserTurnCommitAbortedError,
create_api_error_from_http,
)
from .job import (
Expand Down Expand Up @@ -142,6 +143,7 @@
EndpointingOptions,
InterruptionOptions,
PreemptiveGenerationOptions,
RealtimeInputMode,
TurnHandlingOptions,
UserTurnLimitOptions,
)
Expand Down Expand Up @@ -236,6 +238,7 @@ def __getattr__(name: str) -> typing.Any:
"Agent",
"AgentTask",
"AssignmentTimeoutError",
"UserTurnCommitAbortedError",
"UnexpectedModelBehavior",
"APIConnectionError",
"APIError",
Expand Down Expand Up @@ -280,6 +283,7 @@ def __getattr__(name: str) -> typing.Any:
"AMD",
"AMDCategory",
"AMDPredictionEvent",
"RealtimeInputMode",
"TurnHandlingOptions",
"EndpointingOptions",
"InterruptionOptions",
Expand Down
12 changes: 12 additions & 0 deletions livekit-agents/livekit/agents/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ class AssignmentTimeoutError(Exception):
pass


class UserTurnCommitAbortedError(RuntimeError):
"""Raised when a manually committed user turn is abandoned before STT settles.

``partial_transcript`` contains all final and interim text accumulated for that turn at
the abandonment boundary.
"""

def __init__(self, partial_transcript: str) -> None:
super().__init__("the manually committed user turn was abandoned before completion")
self.partial_transcript = partial_transcript


# errors used by our plugins


Expand Down
30 changes: 30 additions & 0 deletions livekit-agents/livekit/agents/llm/realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from abc import ABC, abstractmethod
from collections.abc import AsyncIterable, Awaitable
from dataclasses import dataclass
from enum import Enum
from types import TracebackType
from typing import Generic, Literal, TypeVar

Expand Down Expand Up @@ -104,6 +105,18 @@ def __init__(self, message: str, *, code: str | None = None) -> None:
self.code = code


class _UserMessageSyncStatus(str, Enum):
ACCEPTED = "accepted"
REJECTED = "rejected"
UNKNOWN = "unknown"


@dataclass(frozen=True)
class _UserMessageSyncResult:
status: _UserMessageSyncStatus
error: RealtimeError | None = None


class RealtimeModel:
def __init__(self, *, capabilities: RealtimeCapabilities) -> None:
self._capabilities = capabilities
Expand Down Expand Up @@ -250,6 +263,23 @@ async def update_chat_ctx(
self, chat_ctx: ChatContext
) -> None: ... # can raise RealtimeError on Timeout

async def _sync_user_message(
self, chat_ctx: ChatContext, message_id: str
) -> _UserMessageSyncResult:
"""Synchronize one finalized user item without changing the public provider API."""

del message_id # the compatibility implementation updates the complete context
try:
await self.update_chat_ctx(chat_ctx)
Comment thread
TonyG-FWE marked this conversation as resolved.
except RealtimeError as error:
return _UserMessageSyncResult(_UserMessageSyncStatus.UNKNOWN, error)
return _UserMessageSyncResult(_UserMessageSyncStatus.ACCEPTED)

def _exclude_chat_ctx_item_from_replay(self, item_id: str) -> None:
"""Keep a locally observable item out of adapter-driven provider replay."""

del item_id # only session wrappers that rebuild provider state need to track this

@abstractmethod
async def update_tools(self, tools: list[Tool]) -> None: ...

Expand Down
Loading