fix(inference): finalize AvatarSession Rosetta port#2103
Conversation
Rollback base avatar lifecycle state when gateway provisioning fails so callers can retry without duplicating a successfully created provider session. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 13645af The changes in this PR will be included in the next version bump. This PR includes changesets to release 39 packages
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 |
Reuse one create idempotency key across start retries so ambiguous gateway failures cannot provision duplicate paid sessions. Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid accumulating shutdown callbacks when a failed avatar provisioning attempt is retried in the same job context. Co-authored-by: Cursor <cursoragent@cursor.com>
Roll back only local lifecycle bindings after ambiguous create failures so idempotent retries do not evict a provider participant that may already exist. Co-authored-by: Cursor <cursoragent@cursor.com>
tinalenguyen
left a comment
There was a problem hiding this comment.
i noticed there was a lip sync issue when audioEnabled is true in outputOptions, here is what claude said for the fix (pre existing this PR but maybe we can bundle it):
Where: room_io.ts start(), output-setup block.
What: two small changes so RoomIO wraps a pre-installed audio output (the avatar's DataStreamAudioOutput) instead of clobbering it:
const externalAudioOutput = this.agentSession.output.audio ?? undefined;
// don't publish a competing track when the avatar already owns audio
if (this.outputOptions.audioEnabled && !externalAudioOutput) {
this.participantAudioOutput = new ParticipantAudioOutput(...);
}
// sync against our own output, else the avatar's
const audioOutput = this.participantAudioOutput ?? externalAudioOutput;
Why it works: the synchronizer now wraps the avatar output, so frames flow SyncedAudioOutput → DataStreamAudioOutput → avatar while driving the transcript clock. Nothing else changes — the audioOutput getter and line 578 already assign the wrapped output to session.output.audio.
Result: leave audio enabled (drop audioEnabled: false) → avatar works and transcript is synced.
Also need: confirm close() doesn't double-close the avatar output, reword the stale "ignoring" warning, add a room_io.test.ts case, changeset.
Keep avatar-provided audio routing intact while allowing transcription synchronization without transferring output ownership to RoomIO. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Implemented the RoomIO/avatar integration feedback on exact head
Also fixed stale shutdown callbacks from an earlier job context closing a later successful retry. Verification: 56 focused tests, 1,464 full agents tests, 40/40 build tasks, green exact-head CI, and Cue session |
Refresh gateway authentication per retry and recover missing dispatch room SIDs from connected rooms. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Closed the two remaining Python-parity gaps on exact head
Verification: strict red-green regressions; 57 focused tests; full agents suite 1,465 passed/5 skipped; build 40/40; exact-head CI green; independent Fresh Cue session |
Summary
inference.AvatarSession.start()retry when provisioning fails before a create response is parsedstart()retriesaclose()participant removal, and retryable termination state@livekit/agentspatch changeset for the follow-up fixesParity revalidation
Compared the actual patches for #2101 and livekit/agents#6492 against merged JS main and current Python. Revalidated request shaping, per-attempt authentication, retries/timeouts, nullable room SID fallback,
waitForJoin()ordering, lifecycle and partial-start failure, cleanup/termination retry, double-start, audio rebinding, nullable IDs, examples, exports, and tests.The follow-up fixes JS-specific integration gaps around failed-start rollback, cross-start idempotency, shutdown callback ownership, ambiguous-create participant preservation, RoomIO handling of an avatar-installed audio output, connected-room SID resolution, and authentication refresh across long retry delays.
The JS whole-request timeout and retrying/escalating termination behavior remain deliberate, tested adaptations from Python's separate total/connect timeouts and best-effort single termination attempt.
Tests
pnpm build— 40/40 tasks passed on exact headgit diff --check— cleanSpec compliance: APPROVEDandCode quality: APPROVED, with no Critical, Important, or Minor findingsCue voice E2E
13645af1c1299266de334880f355b3435a523acdsid_4411b9daf480(--mode voice, ended cleanly){debug_message(.payload.phase="request_parity_verified",.payload.head="13645af1c1299266de334880f355b3435a523acd",.payload.createVerified=true,.payload.authRefreshed=true,.payload.idempotencyStable=true,.payload.roomIoVerified=true) -> function_tools_executed(.function_calls[0].name="close_avatar") -> debug_message(.payload.phase="terminate_completed",.payload.terminateVerified=true)} and agent_state_changed(.new_state="AS_SPEAKING") and conversation_item_added(.item.message.role="ASSISTANT")DataStreamAudioOutput -> SyncedAudioOutputforwarded one frame; termination was verified; and the agent spoke an assistant completion.~/.cue-cli/sessions/sid_4411b9daf480/controlled-gateway-redacted.jsonlcontains two create requests around a controlled one-second 503 retry and one terminate request. It persists only URL/method, provider header,Bearer JWTshape, non-secret room/provider session IDs, and SHA-256 token/idempotency representations. No raw bearer, worker, or terminate secret is persisted.09c896...then4fcff5...) while the idempotency-key hash remains296eff...; terminate used its own freshly minted Authorization hash with the expected provider session and hashed token evidence.nova-3STT, LiveKit turn detector, LiveKit OpenAIgpt-4.1-miniLLM, and LiveKit Cartesiasonic-3TTS.recording.wavis 4,063,596 bytes, stereo 48 kHz, 21.164 s; commandrecording.wavis 1,785,932 bytes, stereo 48 kHz, 9.302 s. Both channels in both files contain substantial non-zero samples.Made with Cursor