Repository navigation
Decisions through LiveKit Inference
You can evaluate named decisions with LiveKit credentials instead of a provider key. inference.DecisionModel batches evaluations and reports refusals per decision without discarding the other answers. Access requires a project feature flag.
model = inference.DecisionModel("openai/gpt-6-luna-decisions")
response = await model.evaluate(
input=chat_ctx,
decisions={"handoff": llm.Probability("The caller requests a human.")},
)Custom LLM request attributes
You can attach prompt metadata directly to the provider’s llm_request span from your custom llm_node, without a custom OpenTelemetry processor. Attributes stay scoped to that node’s calls.
async def llm_node(self, chat_ctx, tools, model_settings):
gen_ai.set_llm_request_attributes(
{"langfuse.observation.prompt.name": "support-agent"}
)
async for chunk in Agent.default.llm_node(
self, chat_ctx, tools, model_settings
):
yield chunkSilent tool progress
You can record background-tool progress without prompting a spoken response. Final background-tool results can now trigger follow-up tool calls.
await ctx.update("checking the fare rules", silent=True)New providers and models
- ThunderPhone: new realtime speech-to-speech plugin for saved-agent and inline sessions, including
bolt. - OpenAI: Decisions API support directly and through Inference with
openai/gpt-6-luna-decisions. - TypeSafe Jev and LiveKit Clef Omni: decision evaluation through LiveKit Inference.
- Microsoft-Decision-1: batched decisions through Microsoft Foundry.
- AssemblyAI
assemblyai-tts-1: streaming TTS with word timestamps and six languages. - Keyframe
persona-2.0-live: speech-timed avatar expressions. - Fish Audio
drama-3: Inference TTS with free-form expressive steering. - Speechify
simba-3.5: streaming synthesis with timestamps. - Google
gemini-3.8-flash: added to supported chat models and selected by default.
Security
- Tool errors: raw arguments and validation details no longer bypass PII filtering through log message bodies.
- Simli: startup error logs no longer expose the session token.
- RoomIO: configured participant identity now restricts chat before linking and during reconnection, preventing another participant from starting a turn.
Breaking changes
- Rime: WebSocket v1 requires explicit
model; add it if you relied on URL-path detection. - Gradium: passing both
voiceandvoice_idnow raisesValueError; choose one. - Inworld:
max_connectionsandidle_connection_timeoutare deprecated and ignored; TTS now uses one WebSocket per instance.
Behavior changes
- Adaptive interruptions: errors now fall back to VAD without reconnecting by default.
- STT flushing: supported engines now flush at manual and detected turn boundaries; buffering custom nodes must disable
manual_flush. - Gradium: the default voice is now Everly; explicit voice selections are honored.
- Anthropic: forced tool choices become
autofor Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1, so tool use is not guaranteed. - Inworld:
inworld-tts-2uses server-side sentence splitting by default; client-side splitting defaults to 250 characters. - Dependencies: OpenAI requires SDK 3.28+, Phonic requires
phonic>=0.32.36, and the framework requireslivekit-protocol>=1.1.28.
This release also improves reliability across realtime history, tool handoffs, task shutdown, TTS retries, avatar interruptions, worker prewarming, and session reporting.
What's Changed
- fix(workflows): support turn_handling in warm transfers by @JonasDaWi in #7847
- feat(room_io): warn when agent and client both apply noise cancellation by @lukasIO in #7848
- docs: stop flagging LiveKit identifiers as PII in Devin review by @chenghao-mou in #7849
- fix(inference): send keyterms to speechmatics/linden-1 by @shawnfeldman in #7853
- feat(inference): add DecisionModel for the gateway's /v1/decisions by @shawnfeldman in #7855
- docs(inference): Deepgram mip_opt_out is always true on the gateway by @shawnfeldman in #7860
- fix(elevenlabs): offset text-to-dialogue alignment by chunk start by @satheeshk50 in #7784
- prewarm Resource.create to avoid blocking runloop during start by @davidzhao in #7872
- fix(voice): skip audio turn handling warnings in text simulations by @u9g in #7878
- fix(gradium): identify the STT plugin to the API with x-api-source: livekit-plugin by @hhugo-gradium in #7873
- docs: add Google realtime models to inference SUPPORTED REALTIME MODELS list by @detail-app[bot] in #7861
- docs: add the shared LiveKit sections to AGENTS.md by @JustinScitech in #7885
- fix(anthropic): fall back to auto tool_choice for Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1 by @omChauhanDev in #7511
- feat(deepgram): support speed parameter in Flux TTS by @73bruno in #7483
- fix(llm): drop nonexistent name from all by @BlueX888 in #7894
- Clarify what "item" is in logged warning by @kath0la in #7934
- Send
lk.agent.sessionmessages with one-shot send_bytes instead of stream_bytes by @1egoman in #7751 - fix(cli): apply connect credentials to the agent server by @BlueX888 in #7929
- feat(telemetry): let llm_node set attributes on the llm_request span by @rrfunde in #7827
- fix(simli): retry session start and stop the error handler from crashing by @sidxh in #7498
- feat(soniox): report output tokens in RECOGNITION_USAGE for token-based billing by @Panmax in #6435
- fix(cartesia): expand TTS language support and add WebSocket timestamp params by @Namit1867 in #7754
- fix(voice): keep ToolError detail out of log message bodies by @Li-john1021 in #7804
- fix(llm): keep a nullable enum's null out of the shared $defs entry by @ANDROIDHASSAN in #7760
- fix(llm): render missing {} and formatted template placeholders as empty by @ANDROIDHASSAN in #7783
- fix(tts): don't repeat the reply when a FallbackAdapter stream retries by @RachelWanggg in #7840
- fix(ipc): drop exited idle processes from the warmed queue by @RGB-loop in #7864
- fix(bey): use the current session endpoint and send 16 kHz audio by @niqodea in #7877
- fix(bey): don't retry 4xx when starting a session, and keep the provider's error by @sidxh in #7496
- fix: do not reconnect the adaptive interruption stream by @chenghao-mou in #7923
- chore: remove unused BoundedDict.set_or_update method by @detail-app[bot] in #7568
- feat(openai): add DecisionModel for OpenAI's Decisions API by @tinalenguyen in #7882
- fix(proto): stringify map values in encode_by_name by @u9g in #7962
- fix(rime): require explicit v1 models and anonymize endpoints by @naszzz in #7964
- fix(meta): don't delay Muse STT audio by the websocket connect time by @DEVJHAWAR11 in #7890
- fix(voice): stop wait_for_idle from spinning through a speech delay by @longcw in #7965
- fix(voice): preserve tool outputs across concurrent realtime context updates by @Epsilon006 in #7905
- fix: honor participant identity for chat before linking by @chenghao-mou in #7435
- fix(tokenize): don't hold a CJK reply until the end in word streams by @dev-S-t in #7669
- fix(hume, simli): make every all entry resolve by @omChauhanDev in #7981
- fix(inference): emit every parallel tool call sent in one chunk by @pcbeingused333 in #7887
- fix(realtime): treat credit_balance_exhausted as a fatal OpenAI error by @jipeng6036-del in #7819
- fix(gradium): identify the TTS plugin to the API with x-api-source (1/4) by @hhugo-gradium in #7815
- fix(voice): add say() text to the realtime chat context when it is spoken by @xiaoyuCR7 in #7947
- fix(gradium): stop voice_id default from overriding an explicit voice (1/2) by @hhugo-gradium in #7813
- fix(room_io): size trailing silence by num_channels by @Hugo-DDT in #7968
- fix: prevent stale avatar clear timeouts from finishing new replies by @chenghao-mou in #7980
- feat(microsoft-ai): add DecisionModel for Microsoft-Decision-1 by @tinalenguyen in #7883
- fix: keep sarvam stt streams open across flushes by @chenghao-mou in #7990
- fix: flush supported stt at manual and detected turn boundaries by @chenghao-mou in #7285
- feat: enable manual flushing in elevenlabs stt by @chenghao-mou in #7287
- feat(keyframe): sync avatar emotions to speech for persona-2.0-live by @kradkfl in #7846
- fix(elevenlabs): pad short commits so Scribe v2 keeps the session by @u9g in #7992
- ci: run the data-capture simulation with
lk agent simulate textby @u9g in #7982 - feat: add ThunderPhone realtime (speech-to-speech) plugin by @kolchinski in #7202
- fix(voice): don't cut an AgentTask's speech on shutdown() by @RachelWanggg in #7839
- fix(cartesia): accept an integer speed for sonic-3 models by @ShivamSharma43 in #7945
- fix(voice): reset realtime agent state without audio output by @Luke-salinero in #7869
- fix(gradium): report the configured model name from TTS.model and STT.model by @hhugo-gradium in #7814
- Add audio_sample_rate and model args for LemonSlice avatars by @jp-lemon in #8005
- fix(utils): do not let a blank or non-numeric LK_* debug flag break package import by @Lesereingrape in #7470
- feat(gradium): default the TTS voice to Everly by @hhugo-gradium in #7874
- fix(inference): Drop the sampling params gpt-6 rejects while it reasons by @adrian-cowham in #8001
- feat(inworld): cancel the TTS context when a stream is abandoned by @ianbbqzy in #8006
- feat(inworld): let the server split streamed text on inworld-tts-2 by @ianbbqzy in #8000
- fix(phonic, baseten): register plugin loggers so debug/info logs show up by @omChauhanDev in #8007
- fix(soniox): emit END_OF_SPEECH when an endpoint follows a fully retracted segment by @iamdgarcia in #7897
- fix(speechify): list_voices() iterates the SDK's async pager by @vitalragaz in #8034
- feat(speechify): stream simba-3.5 and any later model without a plugin update by @vitalragaz in #8029
- docs(inworld): correct buffer_char_threshold default in README example by @detail-app[bot] in #8036
- fix(minimax): validate options in update_options like the constructor by @ShivamSharma43 in #8009
- feat(hotel_receptionist): publish suggested replies by @ShayneP in #7345
- chore: remove unused GladiaLanguages type alias by @detail-app[bot] in #7863
- fix(google): leave TTS ssml_gender unspecified when no gender is given by @sarankumar1325 in #7954
- fix(neuphonic): create segments channel per-run to fix retry after connection error by @muntazir868 in #7622
- fix: don't bind names inside assert statements by @bercedev in #8046
- fix: update certifi, uv and Go (eMudhra intermediate CA compromise) by @kyle-livekit in #7998
- fix(gradium): surface TTS server errors, close codes and transport errors (1/2) by @hhugo-gradium in #7672
- fix(voice): stop the preemptive generation when an interim moves past the preflight by @Sakuta014 in #7949
- feat(fishaudio): free-form expressive steering for drama-3 by @cshape in #8049
- fix(google): use role=system for Vertex update_instructions by @Li-john1021 in #7802
- fix(voice): end the call after the turn in EndCallTool when no reply is asked for by @TANGBUDU in #8011
- docs(examples): fix broken drive-thru README links by @kun654 in #8052
- fix(openai): report reasoning tokens for Responses API usage by @pcbeingused333 in #8033
- feat(anam): use lk.playback_started to track turn state by @sebvanleuven in #8042
- chore(deps): update dependency aiohttp to v3.14.4 [security] by @renovate[bot] in #7961
- feat(phonic): add external_id option to RealtimeModel by @arunwpm-work in #8070
- fix(agents): set SessionReport.duration when audio recording is off by @JustinScitech in #8083
- docs(soniox): fix moved STT/TTS documentation links by @kun654 in #8084
- fix(voice): wait for a paused activity before delivering a tool reply by @longcw in #7685
- fix(voice): arm the user-away timer while an AgentTask runs by @longcw in #8056
- feat(voice): add silent to ctx.update() and allow tools in the final reply by @longcw in #7966
- fix(anthropic): send parallel_tool_calls when no tool_choice is set by @sarankumar1325 in #8082
- fix(tts): keep StreamPacer buffered tail after end_input by @Hugo-DDT in #7816
- chore: ignore google coroutine to avoid tripping up fail_on_leaked_tasks by @davidzhao in #8087
- feat(assemblyai): add Streaming TTS (assemblyai-tts-1) by @dlange-aai in #8040
- feat(plugins-tavus): add AvatarSession.set_emotion() by @ggarber in #8025
- include code tracking & agent name in logs by @andrewnitu in #8096
- refactor(inworld): use a single TTS websocket instead of a connection pool by @ianbbqzy in #8038
- feat(beta): add TranslationSession for speech-to-speech translation m… by @tinalenguyen in #7776
- telemetry: send a session summary in the recording header by @jmcclanahan in #8069
- fix(google): default the LLM to gemini-3.8-flash by @pcbeingused333 in #8081
- feat(inference): claim aligned transcripts for AssemblyAI TTS by @adrian-cowham in #8101
- feat(keyframe): [tag] emotion markers for ElevenLabs v3/v4 voices by @kradkfl in #8104
- feat(azure): pronunciation assessment on the streaming STT by @Bilnaa in #8102
- livekit-agents@1.8.9 by @github-actions[bot] in #8100
New Contributors
- @JonasDaWi made their first contribution in #7847
- @satheeshk50 made their first contribution in #7784
- @hhugo-gradium made their first contribution in #7873
- @Li-john1021 made their first contribution in #7804
- @RachelWanggg made their first contribution in #7840
- @niqodea made their first contribution in #7877
- @DEVJHAWAR11 made their first contribution in #7890
- @Epsilon006 made their first contribution in #7905
- @dev-S-t made their first contribution in #7669
- @pcbeingused333 made their first contribution in #7887
- @jipeng6036-del made their first contribution in #7819
- @xiaoyuCR7 made their first contribution in #7947
- @Hugo-DDT made their first contribution in #7968
- @kolchinski made their first contribution in #7202
- @ShivamSharma43 made their first contribution in #7945
- @iamdgarcia made their first contribution in #7897
- @sarankumar1325 made their first contribution in #7954
- @muntazir868 made their first contribution in #7622
- @bercedev made their first contribution in #8046
- @Sakuta014 made their first contribution in #7949
- @TANGBUDU made their first contribution in #8011
- @kun654 made their first contribution in #8052
- @sebvanleuven made their first contribution in #8042
- @ggarber made their first contribution in #8025
- @Bilnaa made their first contribution in #8102
Full Changelog: https://github.com/livekit/agents/compare/livekit-agents@1.8.8...livekit-agents@1.8.9