Added
-
Added token usage metrics to
AWSNovaSonicLLMService, which now emitsLLMUsageMetricsDatafrom Nova Sonic'susageEvent. Each event reports its token delta, with speech and text tokens combined intoprompt_tokensandcompletion_tokens, so metrics summed over a session match the session total.
(PR #4783) -
Added an
http_clientparameter toOpenAITTSServiceand Whisper-based STT services (BaseWhisperSTTService,OpenAISTTService,GroqSTTService), so a customhttpx.AsyncClient— e.g. one with a raised request timeout for high-latency endpoints — can be used for API requests. Preferopenai.DefaultAsyncHttpxClient, which retains the OpenAI SDK's connection limits and redirect handling.
(PR #4941) -
Added the ability for users to provide a local image to the LemonSlice transport to be used as the avatar image.
(PR #4977) -
Added a
filter_background_audiosetting toElevenLabsRealtimeSTTService.Settingsso callers can have ElevenLabs suppress background and far-end audio before transcription, which reduces spurious partial transcripts and empty commits on noisy telephony audio. When set, it is forwarded as a connection query parameter regardless of commit strategy. Defaults to unset, which preserves ElevenLabs' default of no filtering.
(PR #5003) -
Added STT usage metrics: every STT service reports usage as
STTUsageMetricsData, carrying the client-measured seconds of audio submitted to the service (audio_seconds). Continuous services emit incrementally per final transcript with a flush on stop/cancel; segmented services emit per transcribed segment. Enabled withenable_usage_metrics=True; usage is forwarded to RTVI clients (stt_usage), logged byMetricsLogObserver, and attached to OpenTelemetrysttspans asmetrics.audio_seconds.
(PR #5055) -
Added inbound SIP DTMF support on the LiveKit transport via
on_dtmf_eventandInputDTMFFrame, soDTMFAggregatorworks with LiveKit SIP/PSTN calls.
(PR #5097) -
Added
numeralssetting toDeepgramFluxSTTSettingsto convert spoken numbers to numeral form in transcripts (e.g. "twenty three" → "23"). Enable withnumerals=Truein the settings; configured at connection time per Deepgram's Flux API.
(PR #5099) -
Added
safety_settingstoGoogleLLMService.Settings(andGoogleVertexLLMService.Settings), exposing Gemini's content safety filters. Previously these could only be set by smuggling them throughextra.```python from google.genai.types import HarmBlockThreshold, HarmCategory,SafetySetting
llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( safety_settings=[ SafetySetting( category=HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold=HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, ), ], ), ) ``` Categories left unspecified keep the Gemini API defaults. The setting isruntime-updatable via
LLMUpdateSettingsFrame, which also accepts plain
dicts.
(PR #5109) -
Added
language_codessetting toAssemblyAISTTSettings, exposing the list-valued name of AssemblyAI's declared-language parameter and the one to prefer over the singularlanguage_code, which stays supported and is ignored when both are set. It takesLanguageenums: a single language (e.g.language_codes=[Language.ES]) pins transcription to that language, while several (e.g.language_codes=[Language.EN, Language.ES]) steer toward that subset while keeping code-switching among them. Steering is prompt-based, so it applies to U3 Pro models only and isn't sent forother models; on U3 Pro a change applies to the live connection instead of reconnecting, and an empty list clears steering back to the model default. At most 10 distinct languages can be declared — regional variants resolve to their base code, so they collapse rather than counting twice. An over-long list raises at construction; an over-long runtime update is dropped with a warning, leaving the current steering in place.
(PR #5129) -
Added
endpointing,keywords, andformattoSmallestSTTService.Settings.endpointingfinalizes transcripts promptly on trailing silence,keywordsboosts recognition of domain-specific words/phrases (e.g."Blackwell:2,NVIDIA:1"), andformatcontrols whether transcripts getpunctuation and capitalization applied.
(PR #5137) -
Added an opt-in
force_localesetting toAzureTTSServiceandAzureHttpTTSService(AzureTTSSettings) that wraps synthesized text in SSML's<lang xml:lang>element, so multilingual voices (e.g.en-US-EmmaMultilingualNeural) speak in the configured locale/accent instead of auto-detecting one per segment. Defaults toFalse, leaving SSML output untouched unless enabled.
(PR #5154) -
Added
reach_inactive_servicestoServiceUpdateSettingsFrame, and so toLLMUpdateSettingsFrame,TTSUpdateSettingsFrameandSTTUpdateSettingsFrame. Set it when a settings update is provider-neutral and needs to survive a service switch, and every service aServiceSwitchermanages applies it rather than the active one alone.```python update = LLMUpdateSettingsFrame( delta=LLMSettings(temperature=0.2), reach_inactive_services=True, ) await worker.queue_frames([update]) ``` It defaults to `False`, which suits values only one provider understands:a Cartesia voice id applied to a Deepgram TTS service would leave it unusable
once it takes over. To configure one specific service, address the update to
it withservice=instead.
(PR #5155) -
Added
keytermtoCartesiaSTTService.SettingsandCartesiaTurnsSTTService.Settings, biasing transcription toward domain-specific words and phrases such as product names and jargon.```python stt = CartesiaTurnsSTTService( api_key=os.environ["CARTESIA_API_KEY"], settings=CartesiaTurnsSTTService.Settings(keyterm=["Pipecat", "Ink2"]),
)
```Cartesia binds keyterms to a connection, so updating them with anSTTUpdateSettingsFramereconnects to apply them. Lists longer than
Cartesia's limit of 100 keyterms or 1200 total characters are truncated with
a warning.CartesiaSTTServicesends keyterms only for ink-2 models, the
only family Cartesia supports them on.
(PR #5168) -
Added
PocketTTSService, a local CPU-only text-to-speech service built on kyutai-labs' pocket-tts streaming model. Supports English, French, German, Italian, Portuguese, and Spanish, predefined voices, and voice cloning from a wav file orhf://voice prompt. Install withpip install "pipecat-ai[pocket-tts]".```python from pipecat.services.pocket_tts.tts import PocketTTSService tts = PocketTTSService(settings=PocketTTSService.Settings(voice="alba")) ```(PR #5170)
Changed
-
SmallestSTTServicenow uses the Waves v4 STT endpoint (/waves/v1/stt/live) and sendsfinalizeper utterance to keep the WebSocket session alive across turns.
(PR #4747) -
The TeXML served by the development runner for Twilio and Telnyx no longer includes a trailing
<Pause length="40"/>.<Connect><Stream>holds the call for the duration of the WebSocket session, so the call now hangs up as soon as the stream ends instead of lingering for another 40 seconds.
(PR #5135) -
SageMakerBidiClientnow relies on the SageMaker Runtime SDK's default auth configuration (SigV4 for the sagemaker service) instead of passing an equivalent explicit configuration, so future SDK auth changes are picked up automatically.
(PR #5144) -
⚠️ TavusParams.audio_out_faster_than_realtimenow defaults toTrue. Bot audio is accumulated into 100ms chunks and sent to Tavus as fast as it is produced, giving the avatar a larger rendering buffer, instead of being paced to real playback time. Pipelines that need bot audio to arrive at roughly real time — for example when anAudioBufferProcessordownstream is recording the conversation — should now setaudio_out_faster_than_realtime=Falseexplicitly.
(PR #5162) -
Websocket-based services now bound the websocket closing handshake at 2 seconds instead of the
websocketsdefault of 10. A service disconnects while handling theEndFrame, before the frame continues downstream, so a peer that never acknowledges the close delayed pipeline shutdown bythat much per service. The bound is configurable per service via the newws_close_timeoutargument onWebsocketService.
(PR #5164) -
CartesiaSTTServicenow connects withCartesia-Version: 2026-03-01, the version that supportskeytermand returns structured JSON errors. This brings it in line withCartesiaTTSServiceandCartesiaTurnsSTTService, which already use that version.
(PR #5168) -
SarvamLLMServicenow defaults tosarvam-105b, the only chat model Sarvam's API still serves.sarvam-30b,sarvam-30b-16kandsarvam-105b-32kare rejected server-side, so they're no longer accepted as themodelsetting.
(PR #5177) -
BasetenLLMServicenow defaults tomoonshotai/Kimi-K2.6. Setsettings=BasetenLLMService.Settings(model=...)to pin a different model.
(PR #5178)
Deprecated
-
Deprecated
XTTSService. Constructing one now emits aDeprecationWarning; it will be removed in 2.0.0. The Coqui XTTS streaming server it connects to has been unmaintained since February 2024 and pins a commit of the discontinuedcoqui-ai/TTSlibrary, and the XTTS-v2 model it serves is licensed for non-commercial use only.
-KokoroTTSServiceandPiperTTSServiceare the maintained local TTS services. To stay on XTTS, the community-maintainedpipecat-xtts-vllmpackage targets a current XTTSv2 serving stack.
(PR #5131) -
Deprecated
LLMSettings.filter_incomplete_user_turnsandLLMSettings.user_turn_completion_config, which will be removed in 2.0.0. Turn completion is configured through the user turn strategy: passuser_turn_strategies=FilterIncompleteUserTurnStrategies()toLLMUserAggregatorParams, withFilterIncompleteUserTurnStrategies(config=...)for a customUserTurnCompletionConfig. Setting either field at construction now emits aDeprecationWarning; it never took effect there.
(PR #5157)
Fixed
-
Fixed cached DTMF audio being returned at the wrong sample rate.
load_dtmf_audio()keyed its in-memory cache by button only, so the first sample rate a button was loaded at was reused for every later request. The cache is now keyed by(button, sample_rate).
(PR #4766) -
Fixed an issue where sending several
STTUpdateSettingsFrames in quick succession toDeepgramFluxSTTService(or the SageMaker transport) could be rejected by Deepgram Flux withConfigure rejected: [unknown] Too many pending Configure messages.. Configure messages are now sent one at a time, waiting for each to be acknowledged (bounded by a 5s timeout) before the next goes out, so bursts of mid-stream settings updates no longer trip the server-side pending-Configure cap.
(PR #4791) -
Fixed
AWSBedrockLLMAdapterraisingUnboundLocalErrorwhen a user message's content list places the image before the text (e.g. aUserImageRawFramefollowed by a prompt).
(PR #4796) -
Fixed
FunctionCallUserMuteStrategyraisingKeyErroron aFunctionCallResultFrame/FunctionCallCancelFramewhose tool call id it was not tracking, which aborted the user aggregator's handling of that frame and surfaced anErrorFrameto the application. Such frames arrive when the LLM invokes the built-incancel_async_tool_call(excluded fromFunctionCallsStartedFrame, but it still emits a result), when an async tool reports intermediate updates (a result frame per update, plus the final one), and when a bus bridge re-delivers a result another worker alreadyhandled. Untracked tool call ids are now ignored.
(PR #4824) -
Fixed
AWSBedrockLLMServicedropping all but the last tool call in streaming responses. Parallel tool calls arrive as separate Bedrock content blocks, but the accumulator overwrote the prior block on eachcontentBlockStart, so only the final call ran. Tool blocks are now keyed bycontentBlockIndexand finalized oncontentBlockStop, so every requested function call is dispatched.
(PR #4918) -
Fixed
expand_currency(used byVoiceFormatter) leaking the 3rd and later fractional digits of a currency amount onto the subunit word.$5.500expanded tofive dollars and fifty cents0and$3.567tothree dollars and fifty-six cents7, so TTS spoke the stray digit. The full fraction is now consumed and read to cent precision (e.g.five dollars and fifty cents), dropping sub-cent precision like a two-digit amount.
(PR #4969) -
Fixed
TurnAnalyzerUserTurnStopStrategyreleasing user turns later than the configured STTttfs_p99_latencybudget. The safety-net timeout is now computed from an absolute deadline — the end of the user's speech plusttfs_p99_latency— so time spent in end-of-turn analysis (e.g. MLturn-analyzer inference, typically 50–120ms) no longer extends the wait.
(PR #5007) -
Fixed
TurnAnalyzerUserTurnStopStrategywaiting out the STT safety-net timer when a transcript finalized while end-of-turn analysis was still running: the trigger conditions are re-checked as soon as the analyzer's verdict lands, releasing the turn immediately. Withwait_for_transcript=False, the turn is likewise released directly on the analyzer'sCOMPLETEverdict.
(PR #5007) -
Fixed
TaskManager.current_tasks()undercounting live tasks, which left the dangling-task warnings logged at shutdown incomplete. The task registry was keyed by task name, and names are not unique — tasks started from the same method on the same object, such as the parallel function-call tasks, all share one name and evicted one another. The registry is now keyed by the task object.
(PR #5025) -
Fixed services that send the system instruction as a separate parameter (e.g.
AnthropicLLMService,AWSBedrockLLMService,GoogleLLMService) permanently rewriting a lonesystemmessage in theLLMContexttouser. When a context's only message is a system message, it's convertedtouserso the provider isn't sent an empty conversation history; that conversion now happens on a copy, so the context keeps itssystemrole and later turns — once more messages have accumulated — still send it as the system instruction.
(PR #5027) -
Fixed
developer-role messages being permanently rewritten touserin theLLMContextby OpenAI-compatible services that don't support the role (e.g.QwenLLMService,OllamaLLMService,TogetherLLMService). These services convertdevelopertouseron the way out to the provider; that conversion now happens on a copy, so the stored conversation history keeps its original roles for logging, persistence, and any other service reading the same context.
(PR #5027) -
Fixed
GeminiLiveLLMServicecrashing with WebSocket error 1007 when seeding a session from a context containing tool calls (e.g. after a multi-agent handoff or a reconnect). Gemini Live only accepts text and media content, so the newGeminiLiveLLMAdaptersummarizes function calls andtheir responses as text.⚠️ GeminiLiveLLMServicenow usesGeminiLiveLLMAdapterrather than
GeminiLLMAdapter, so any hand-craftedLLMSpecificMessages aimed at it
needllm="gemini-live"instead ofllm="google". We recommend you use
LLMService.create_llm_specific_message(), which picks the right value for
you.
(PR #5028) -
Fixed
pipecat initomitting the video avatar provider's extra from the generated project'spipecat-aidependency. Choosing Tavus, HeyGen, or Simli produced abot.pyimporting that service whilepyproject.tomlrequested only the transport and cascade extras, so the scaffolded botfailed to import after a freshuv sync.
(PR #5031) -
Fixed
FilterIncompleteUserTurnStrategiesgoing silent after a tool call when the LLM spoke a✓acknowledgement (e.g. "Let me look that up, one moment.") in the same response as the call. The per-turn one-spoken-completion guard — which stops the acoustic detector's duplicate inferences from voicing the same turn twice — also swallowed the post-tool response. A function call now resets that guard, so the post-tool answer is spoken and the acknowledgement still plays in full.
(PR #5063) -
Fixed
AssemblyAISTTServiceraising aValueErrorat construction whenpromptandkeyterms_promptwere set together on U3 Pro models (u3-rt-pro,universal-3-5-pro). AssemblyAI's server accepts the combination on these models, so both parameters are now sent on the streaming connect URL; the client-side mutual-exclusivity check still applies to older models.
(PR #5084) -
Fixed word-timestamp and streamed-token TTS providers producing incorrect or duplicated text/transcript output when two audio contexts were in flight at the same time (e.g. back-to-back
TTSSpeakFrameutterances on a websocket TTS service). Previously theAggregatedFrameSequencertracked pending-sentence and force-complete state globally, so a word event or force-complete for one context could bleed into another; state is now tracked per context. In streaming (token) mode, sentence-boundary frames were also being emitted twice per sentence; only one is now emitted.
(PR #5098) -
Fixed streamed-token TTS providers with no word-timestamp support (e.g.
DeepgramFluxTTSServicein its defaultTextAggregationMode.TOKENconfiguration) tracking each LLM token as its own spoken unit instead of grouping tokens into sentences, and never sending the RTVI "new segment" bot-output event. Previously theAggregatedFrameSequenceronly grouped streamed tokens into sentences when the TTS service also supported word timestamps; providers without word timestamps now get the same per-sentence grouping, completing each sentence's slot as soon as it is confirmed (there is no word-timestamp signal to complete it progressively).
(PR #5102) -
Fixed
ElevenLabsTTSServicepushing a spuriousErrorFrame(which could e.g. trigger an unwantedServiceSwitcherStrategyFailoverswitch) when the server closed the websocket first during an intentional disconnect.
(PR #5103) -
Fixed TTS services with
pause_frame_processing=True(e.g. ElevenLabs, Deepgram, Azure, Rime, Inworld) permanently hanging the pipeline when the expected resume signal never arrived — for example, a provider reporting a successful completion with zero audio (a quota-exhausted key), or a resume racing ahead of the pause taking effect. The stuck pause blocked the terminalEndFramefrom draining, which in turn blocked pipeline teardown,PipelineRunner.run()/WorkerRunnerreturning, and any cleanup that runs afterward.TTSServicenow starts a watchdog whenever it pauses frame processing and force-resumes (reporting a non-fatalErrorFrame) if noBotStartedSpeakingFrameconfirms audio within the newpause_watchdog_timeout_sparameter (default 3.0 seconds).
(PR #5106) -
Fixed
LLMFullResponseEndFramebeing silently dropped for every turn after the first when two turns' audio contexts were in flight concurrently. The end frame is now looked up per audio context instead of being gated by a single shared "response started" flag, which only ever fired forwhichever turn's audio happened to finish first.
(PR #5108) -
Fixed
LLMFullResponseStartFrame/LLMFullResponseEndFramearriving out of order at downstream processors (e.g.LLMAssistantAggregator) when a new LLM turn started while the previous turn's audio was still playing — reachable on TTS services that don't pause frame processing during synthesis (e.g.CartesiaTTSService), where a second, overlapping turn is possible.LLMFullResponseStartFrameis now routed through the same serialization queue as the rest of a turn's frames, so it can no longer be pushed ahead of an earlier turn's still-draining audio context.
(PR #5108) -
Fixed
SmallWebRTCConnection's outgoing app-message buffer never being flushed for data channels created by the remote peer. aiortc surfaces such channels already open, so the "open" event the flush was wired to never fired and buffered messages were stuck until disconnect; the buffer now also flushes when a channel arrives already open.
(PR #5112) -
Fixed
SmallWebRTCTransportsilently dropping app messages sent before the peer connection was established — e.g. the RTVIuser-mute-startedemitted byMuteUntilFirstBotCompleteUserMuteStrategyat pipeline start, which left clients that connect slower than the pipeline starts (realnetworks; loopback usually wins the race) never showing the greeting-hold mute. Messages sent before the data channel is open are now buffered and delivered, in order, once it opens; messages sent while the connection is closing are discarded with a debug log.
(PR #5112) -
Fixed
DeepgramSTTServiceretrying forever with no error notification when the WebSocket connection kept failing (e.g. an invalid API key). A 4xx error from Deepgram now stops the retry loop immediately, and any other connection failure now reports apush_erroron every attempt and gives up after 3 consecutive failures within 5 seconds of connecting, backing off exponentially between attempts.
(PR #5113) -
Fixed
SmallWebRTCTransportinput audio ignoring a configuredaudio_in_channels=2: the resampler was hardcoded to downmix to mono regardless of the setting, so frames were labeled as stereo while containing mono data. The resampler now targets the configured channel layout.
(PR #5115) -
Fixed
SmallWebRTCTransportinput audio being read as half-speed garbage whenaudio_in_sample_ratematches the wire rate (48000): stereo frames bypassed the mono-downmixing resampler and their interleaved bytes were labeled mono.
(PR #5115) -
Fixed a Pipecat CLI issue where one broken plugin took down the whole CLI. Plugins are imported on every invocation, so a bad install (missing transitive dependency, version conflict, stale wheel) made every command —
pipecat initincluded — exit with a message saying thecliextra wasn't installed, which was not the problem. Plugins now load in isolation: a failure is reported on stderr and skipped, and the rest of the CLI carries on. A plugin that isn't atyper.Typeris rejected the same way instead of failing later with an opaqueAttributeError.
(PR #5121) -
Fixed a Pipecat CLI issue where the hint for enabling an optional sub-CLI silently uninstalled other plugins.
uv tool install --withreplaces the tool environment rather than adding to it, so a hint naming only the missing plugin dropped every other one. The hint now repeats every installed plugin package.
(PR #5121) -
Fixed
SmallWebRTCTransportPATCH/api/offerrequests crashing with anAssertionErrorwhen the browser sends an RFC 8840 end-of-candidates marker (an empty candidate string) for a media line, as Firefox does on every connection. The marker is now forwarded to aiortc asNoneinstead of being parsed as a regular candidate.
(PR #5126) -
Fixed word-by-word context attribution breaking after punctuation for TTS services that report word timestamps (e.g.
InworldTTSService), which duplicated the affected sentence in the assistant context. When a provider's word-timestamp events don't repeat punctuation attached to the previous word — reporting"Yeah"then"I"for"Yeah, I can do that."— every word after the punctuation failed to match its slot, was emitted as an untracked passthrough frame without its original-text attribution, and the unmatched remainder was re-emitted when the audio context ended.
(PR #5128) -
Fixed punctuation being duplicated in the assistant context when a TTS service reports it with the following word (
", I") rather than the preceding one ("Yeah,"), which produced"Yeah, , I can do that.". Punctuation trailing a word is attributed to that word, so the repeat is now dropped from the following word instead of discarding that word's original-text attribution.
(PR #5128) -
Fixed
AzureTTSServiceandAzureHttpTTSServicecrashing at startup whenSettings(language=None)was used: the synthesis language is no longer assigned to the Azure SDK (which rejectsNone), letting the service default apply.
(PR #5144) -
Fixed
DailyTransportClient.add_custom_video_track()crashing with anAttributeError: the video track factory was invoked withoutawait, so the SDK received a coroutine instead of a track. Joining with an already-released client now reports a join error instead of raising aTypeError.
(PR #5144) -
Fixed an
InvalidSsmlExceptionerror inAWSPollyTTSServicewhen the text to synthesize contained XML-reserved characters such as&or<. The text is now escaped before being embedded in the SSML request.
(PR #5144) -
Fixed
NvidiaSegmentedSTTServicefailing with gRPCNOT_FOUNDagainst NVIDIA's hosted endpoint: the default NVCFfunction_idforcanary-1b-asrnow points at NVIDIA's current deployment.
(PR #5144) -
Fixed responses to
LLMMessagesAppendFrame(..., run_llm=True)being silently dropped whenfilter_incomplete_user_turnsis enabled and the bot had already responded in the current user turn. The response was generated but never spoken, with nothing logged — most visibly breaking user-idle re-prompts, where an escalation ladder ended the session with an unspoken goodbye.
(PR #5146) -
Fixed
ServiceSwitcher(and thereforeLLMSwitcher) deliveringServiceUpdateSettingsFrameonly to the active service, so an update addressed to a specific service —LLMUpdateSettingsFrame(service=...),TTSUpdateSettingsFrame(service=...)— never reached its target unless that target happened to be active. An addressed update now reaches its service whether or not it is the active one. Updates with no target still apply to the active service alone, since settings values are often specific to one provider; mark onereach_inactive_servicesto send it to every service the switcher manages.
(PR #5155) -
Fixed
LLMTurnCompletionUserTurnStopStrategyconfiguring only the LLM active at pipeline start when its LLMs sit behind anLLMSwitcher. Once a switch made another LLM active, that LLM emitted none of the turn-completion markers the strategy waits on, and every user turn was closed bytheuser_turn_stop_timeoutwatchdog instead. The startup update now reaches every LLM the switcher manages.
(PR #5155) -
Fixed
TurnAnalyzerUserTurnStopStrategyending the user turn on every finalized transcript when the turn was started by a transcript rather than by a VAD frame — the case forMinWordsUserTurnStartStrategy, and forTranscriptionUserTurnStartStrategyin the default strategy set. One utterance split by the STT endpointer into several finalized transcripts produced an LLM inference per fragment, each landing as its ownrole: "user"message, so the bot replied to partial phrases.
(PR #5159) -
Fixed
total_tokensomitting cached input tokens on the Anthropic and AWS Bedrock LLM services. Both report their input count net of the prompt cache, so cache reads and cache writes were left out of the total — a large undercount for a voice agent, where the cached prefix comes to dominate the input within a few turns.total_tokensis now the gross figure on both, matching what the OpenAI and Google services already reported, and on Bedrock a usage report carrying only cache activity is no longer skipped. The per-field breakdown is unchanged; readprompt_tokensforthe net input figure.
(PR #5163) -
Fixed
DTMFAggregatoremitting flushedTranscriptionFrames withfinalized=False, which stalled user-turn stop strategies waiting for a final transcription.
(PR #5172) -
Fixed
UserIdleControllerignoringUserIdleTimeoutUpdateFrameuntil the next re-arm: a running idle timer now restarts with the new duration, and enabling a positive timeout while waiting for the user to speak arms the timer immediately.
(PR #5173) -
Fixed
TTSServiceleaving frame processing permanently paused when a text filter strips the text to empty andpause_frame_processingis enabled.
(PR #5174) -
Fixed
NovitaLLMServiceemitting a token-usageMetricsFrame(and a debug log line) for every streamed chunk instead of one per completion, which over-counted a single turn dozens of times over for anything aggregating those metrics. Novita reports a cumulative usage snapshot on each chunk; the service now collapses them into one total reported when the completion finishes, including when it is interrupted mid-stream.
(PR #5180) -
Fixed
PerplexityLLMServiceandNvidiaLLMServicediscarding the cache-read and reasoning token counts their providers report. Both now pass the whole usage snapshot through, socache_read_input_tokensandreasoning_tokensreach the token-usageMetricsFramealongside the prompt and completion counts.
(PR #5180) -
Fixed a
KeyError: 'role'crash inAnthropicLLMServicewhen using a model that thinks by default, such as Claude Sonnet 5 or Claude Opus 5. The first tool call failed, and so did every later inference on that context. These models return thinking blocks whose text is empty and whose signature carries the reasoning; such blocks are now preserved and passed back to Anthropic, as required within a tool-use turn. A thought carrying no signature has no valid thinking-block form and is skipped.
(PR #5183)