Repository navigation
Added
-
Added support for clients sending files and images to the bot via the new RTVI
send-filemessage, incorporating them into the LLM context.- New frames:
FileRawFrame,InputFileRawFrame, andUserFileRawFrame, carrying a file payload as inline base64 bytes or as a URL. - New
LLMContextmethods:create_file_message(),create_file_url_message(), andadd_file_frame_message(), producing universalfile_base64(base64 data URL) andfile_urlcontent items translated per adapter (Anthropic, AWS Bedrock, Gemini, OpenAI Chat, OpenAI Responses). Unsupported MIME types raise during conversion rather than being silently dropped, so the invalid message can be cleaned up automatically (see below). AWS Bedrock document names are sanitized to satisfy Bedrock's character restrictions. - File URLs are resolved at the LLM service, right before each completion, because the right resolution depends on the provider: each adapter declares which URLs its provider can consume directly (public
http(s)per supported MIME type,s3://via Bedrock's IAM,gs://via Vertex Gemini's IAM, Files API URIs for Gemini), and those are passed through for the provider to fetch itself. Any other URL is downloaded by the service's newFileResolver(pipecat.utils.file_resolver) and inlined at conversion time — the context itself stays canonical, holding only the URL, while the downloaded bytes live in the resolver's in-memory cache, keyed by URL. Each file is fetched and decoded once, not per turn, and services sharing one resolver instance (e.g. services switched between mid-session) reuse each other's downloads while still making their own per-provider pass-through decisions. A URL is assumed to identify immutable content (uploads mint a fresh URL each time);FileResolver.forget(url)forces a re-fetch when an application knows a URL's content changed. FileResolveris configured per LLM service via the newfile_resolverparameter. It downloadshttp(s)URLs only when the resolved address is publicly routable or falls within itsallowed_url_networksCIDR ranges (defaulting to thePIPECAT_ALLOWED_FILE_URL_NETWORKSenv var; SSRF guard; redirects refused, 50 MB cap), and delegates any other scheme to itsFileStorage. Subclass it to fetch from credentialed backends.- New
FileStorageinterface (pipecat.utils.file_storage) with aLocalFileStoragedefault implementation.save()returns a URL the backend can later resolve (pipecat:<id>for local storage); a custom backend can mint e.g.gs://ors3://URLs, which the LLM provider may then fetch itself without the bot in the loop. A backend whose URLs are all uploads it owns can setdelete_after_load(asLocalFileStoragedoes) to have a file removed once its bytes are cached by the resolver, which then serves every later use of the file; it stays off for backends whose URLs may point at shared data. - The development runner gains a session-scoped
POST /sessions/{session_id}/filesupload endpoint (50 MB cap), mirroring Pipecat Cloud's session routes; it's enabled with the new-u/--uploads-folderflag (orPIPECAT_UPLOADS_FOLDERenv var) and responds with an RTVI file source ({"type": "url", "url": "pipecat:<id>"}) the client passes straight back insend-file. A session's uploads are deleted when its bot session ends;--uploads-folder-max-files(default 10) trims the oldest uploads as a backstop. A flatPOST /filesalso exists for transports whose clients never learn a session id (no session-end cleanup there)./startresponses advertise the route to use asfileUploadUrl(relative to the/startendpoint's origin; omitted when uploads are disabled), so clients never reconstruct upload routes themselves. To back the endpoints with a customFileStorage(e.g. a cloud bucket) instead of the local uploads folder, define acreate_file_storage()function alongside yourbot(): whichever host runs the bot discovers and calls it the same way, so the bot code stays identical across hosts. The active backend is injected into the bot asrunner_args.file_storage— hand it to your LLM service'sFileResolverso send-file messages can resolve the URLs it mints. LLMContext.remove_invalid_file_message()removes the oldest file or image message added since the model's last successful reply when the OpenAI, Anthropic, AWS Bedrock, or Gemini LLM service determines a request was rejected (including a local resolution or conversion failure that never reached the API), preventing the context from getting permanently stuck.
(PR #3415)
- New frames:
-
FishAudioTTSService.Settingsgainedchunk_length,min_chunk_length,condition_on_previous_chunksandprosody_normalize_loudness, passing Fish Audio's text-chunking and loudness-normalization request fields through to the synthesis request.
(PR #5534) -
Added
providertoOpenRouterLLMService.Settings, which carries OpenRouter's provider routing preferences — which upstream providers may serve a request, in what order, and under what latency, throughput, price, quantization and data-retention constraints:settings=OpenRouterLLMService.Settings(provider={"sort": "latency"}).
(PR #5550) -
Added a
reasoning_effortfield toGrokLLMService.Settings, which passes xAI's reasoning effort control through to the completion request. Accepted values vary by model:"low","medium", and"high"on the Grok 4 reasoning models,"xhigh"ongrok-4.6and later, and"none"on the models that can turn reasoning off.
(PR #5559) -
Added
LLMAssistantAggregator.has_synchronous_function_calls_in_progress: whether a synchronous function call is in flight, one whose result the model waits for before it runs again, as opposed to an asynchronous call (cancel_on_interruption=False) that has its placeholder in the context.
(PR #5689) -
Added
ExternalFunctionCallsStartedFrame,ExternalFunctionCallInProgressFrame,ExternalFunctionCallResultFrameandExternalFunctionCallCancelFrame: system frames reporting function calls that ran outside the pipeline (e.g. in a backend worker's pipeline on behalf of a tool), mirroring the pipeline's own function-call frames, whichRTVIObserverturns into the usualllm-function-call-*messages under the per-function report level and nothing else in the pipeline acts on.LLMWithBackendreports the backend's calls this way, and so doesOpenAILiveLLMServiceclient delegation.
(PR #5689) -
Added
LLMService.cancel_function_calls(), which cancels every function call in flight, async ones included, where an interruption cancels only the calls that opt into it. It is whatBackendLLMWorkeruses to stop its work when its frontend lets go of it, since the backend's slow tools are the async ones.
(PR #5689) -
Added
pairing_instructiontoBackendLLMWorkerandfrontend_pairing_instructiontoBackendConnector: the text each appends to its model's system instruction about working as one half of the pair, in place ofBACKEND_PAIRING_INSTRUCTIONandFRONTEND_PAIRING_INSTRUCTION, for an app that would rather say in its own words when and how to delegate, that to the user there is one assistant, what to do with the backend's messages, and, on the backend, that the user hears what it marks with>>and how to take requests that arrive while it works. The request strategies take their own texts as arguments too:frontend_instruction(how the request is worded, placed where the frontend's pairing instruction has{strategy}),tool_descriptionandrequest_instruction(what is placed after the request for the backend).
(PR #5689) -
Speech-to-speech LLM services now take messages appended to the conversation (
LLMMessagesAppendFrame) into their session, each on whatever second channel its provider offers: OpenAI Realtime, Azure Realtime and Grok as conversation items, Gemini Live as client content that does not complete the turn, Nova Sonic as a text event. Whether the model answers them is the assistant aggregator's call, as for a tool result: an append that asks to run comes back as the context frame the aggregator pushes upstream, which creates the response, so the hold while the bot speaks and the bundling of a burst apply. This is what carries a backend's messages to a speech-to-speech frontend of anLLMWithBackend.
(PR #5689) -
Added
LLMWithBackend(pipecat.pipeline.llm_with_backend), in alpha: a conversational frontend LLM (text or speech-to-speech) with a backend LLM (in aBackendLLMWorker) it delegates to for deeper thinking and tool orchestration. The frontend keeps the conversation moving fluidly while the backend does the heavy lifting, sending messages to the frontend as it works. AnLLMWithBackenddrops into a pipeline where a standalone LLM would go, with the frontend's system instruction defining who the assistant is while the backend's defines in more detail what work it does. Behind the scenes, the frontend calls adelegatetool and receives a stream of backend outputs, with aBackendConnectoras the (configurable) glue defining how the two pieces interact. Seeexamples/multi-worker/llm-with-backend/.llm = LLMWithBackend( frontend=OpenAIResponsesLLMService(...), backend=BackendLLMWorker( llm=AnthropicLLMService(...), context=LLMContext(tools=[search_codebase, read_file, apply_patch, run_tests]), ), ) pipeline = Pipeline([transport.input(), stt, user_agg, llm, tts, transport.output(), assistant_agg])
(PR #5689)
-
Speech-to-speech LLM services now deliver a tool's intermediate results (reported with
FunctionCallResultProperties(is_final=False)) to their model, each on whatever second channel its provider offers, since a function call itself takes one output: OpenAI Realtime, Azure Realtime and Grok put them in as conversation items, Gemini Live as awill_continuetool response, Nova Sonic as a text event and Ultravox as user-side text. The result'srun_llmsays whether the model should answer it or just take it in. A service that can't do this says so withLLMService.accepts_intermediate_function_call_results— Inworld Realtime, whose model calls the tool again rather than relaying what it was told — and drops intermediate results with a warning.
(PR #5689) -
Added
AzureVoiceLiveLLMService, a speech-to-speech service for Azure's Voice Live API. It supports function calling, caller transcription, Azure's semantic VAD (including the multilingual variant), Azure text-to-speech voices (standard, custom, personal and realtime-native), noise suppression and echo cancellation. Setturn_detection=Noneinsession_propertiesto drive turns from transport VAD instead. Voice Live is a separate product from Azure OpenAI Realtime, whichAzureRealtimeLLMServicecontinues to serve.
(PR #5718) -
Added
bulbul:v4-flash(SarvamTTSModel.BULBUL_V4_FLASH) toSarvamTTSServiceandSarvamHttpTTSService. It supportspitchandloudness, ignorestemperature, and uses its own speaker catalogue (defaultshubh_en_narration_gentle).
(PR #5844) -
Added Gemini 3.8 TTS model support in
GeminiTTSService:gemini-3.8-flash-ttsandgemini-3.8-flash-lite-tts.
(PR #5885) -
Added
TransportParams.audio_out_filter, aBaseAudioFilterthe output transport runs on outgoing audio before the mixer and the write. Likeaudio_in_filter, it starts on setup, stops on cleanup and receivesFilterEnableFrameandFilterUpdateSettingsFrame. Audio the transport generates itself (mixer audio, end silence, DTMF tones) is not filtered.
(PR #5920) -
Added
system_prompt_cache_ttltoAnthropicLLMService.Settingsto set how long the system prompt's cache entry lasts when prompt caching is enabled:"5m"(Anthropic's default) or"1h". A 1-hour entry keeps a system prompt shared by many conversations cached across gaps between them, at a higher cache-write price.
(PR #5928) -
AWSBedrockLLMService.Settingshas aneffortfield, Bedrock's ConverseoutputConfig.effortreasoning level (low,medium,high,xhigh,max), so reasoning models such asus.xai.grok-4.6can be asked for less reasoning and lower latency.
(PR #5934) -
Added
reasoningtoTogetherLLMService.Settings, which passes Together's reasoning toggle through to the request for the models that support one:reasoning={"enabled": False}stops a reasoning model such as GLM from running a reasoning pass before every answer, which shortens the wait for the first spoken token.
(PR #5939) -
Added an
aspect_ratiosetting toGoogleImageGenService, such as"1:1"or"16:9". It defaults to"1:1", so images stay square as they were with Imagen.
(PR #5954) -
Added an
imageevent to scripted eval scenarios: an image the bot output, with its size and format. The eval transport reports the bot's images only to a scenario that expects one.
(PR #5956) -
React clients generated by
pipecat initnow include the shadcn/ui MIT license insrc/components/ui/LICENSE-shadcn.md, next to the shadcn/ui components they ship with.
(PR #5957) -
Added Eleven v4 Turbo (
eleven_v4_turbo) support toElevenLabsDialogueTTSService, including its 84language_codevalues: the Eleven v3 set plus Asturian, Burmese, Cantonese, Māori, Maltese, Mongolian, Occitan, Odia, Tajik and Uzbek.
(PR #5959) -
AnthropicLLMServicereportsreasoning_tokenswhen extended thinking is on, from the thinking-token count in Anthropic's usage.completion_tokensalready includes them.
(PR #5984) -
RTVI's
bot-readymessage now includes acapabilitiesobject describing
what the bot does in the session:audio_in,audio_out,video_in,
screen_in,video_outandmetrics. Clients can use it to show only the
UI that applies, such as hiding the video panels for a voice-only bot. A
field the bot can't determine is left out, so the client keeps its default;
screen_inis known when the transport lists its sources in
video_in_sources. The RTVI protocol version is now 2.2.0."capabilities": { "audio_in": true, "audio_out": true, "video_in": false, "screen_in": false, "video_out": false, "metrics": true }
(PR #5990)
-
Added
PipelineWorker.capabilities, which returns the bot'sBotCapabilities(frompipecat.pipeline.capabilities), derived from the media the pipeline's transports send and receive and fromPipelineParams.enable_metrics. To set fields the pipeline can't show, passcapabilities=BotCapabilities(...)toPipelineWorker; its known fields replace the derived ones.
(PR #5990) -
TavusTransport,HeyGenTransportandLemonSliceTransportreportvideo_outin the bot's capabilities, since the avatar publishes its video to the room itself.
(PR #5990) -
Added a keepalive to
WebsocketTTSServicefor custom websocket TTS services whose provider closes idle connections. Override_send_keepalive()to send the provider's keepalive message, and call_create_keepalive_task(interval)/_cancel_keepalive_task()from_connect()/_disconnect()._send_keepalive()is called only while the websocket is open, and an exception it raises is logged.
(PR #6011) -
Added connection-time
keytermssupport toSarvamSTTServiceandSarvamRealtimeSTTServiceforsaaras:v4.
(PR #6013) -
Added
ClefClassifier, which answers classifier questions with Cloudflare's Clef on Workers AI. Passmodel="clef-flash"for the faster model. It takes a Cloudflare account ID and API token, and several classifiers can share oneClefClient. A choice question can have at most 255 options (CLEF_MAX_CHOICE_OPTIONS). Install withuv add "pipecat-ai[cloudflare]".classifier = ClefClassifier( account_id=os.getenv("CLOUDFLARE_ACCOUNT_ID"), api_key=os.getenv("CLOUDFLARE_API_KEY"), ) results = await classifier.yes_no( "Hi, you've reached Dana. Leave a message.", {"voicemail": YesNoQuestion(instructions="is this a voicemail?")}, )
(PR #6017)
-
Added Pulse 2.0 support to
SmallestSTTServiceviaSmallestSTTModel.PULSE_2(model="pulse-2"): an English-only streaming model with built-in end-of-turn detection (endpointinghas no effect on it), plus per-utteranceemotionandgenderon final transcripts, controlled by the newemotion_detection/gender_detectionsettings (on by default).
(PR #6018) -
Added
video_in_sourcestoTransportParams, so a transport captures the
user's video without anon_client_connectedhandler. List the sources to
capture, each with its ownVideoInSourceParams(framerate=...), and
DailyTransportcaptures them from each participant as they join, and
SmallWebRTCTransportwhen the peer connects.
VideoInSourceParams(on_request_only=True)passes on only the frames that
answer aUserImageRequestFrame. Sources not listed are still captured only
when the application asks, as before. Other transports log a warning and
ignorevideo_in_sources.DailyParams( video_in_enabled=True, video_in_sources={ "camera": VideoInSourceParams(framerate=1), "screenVideo": VideoInSourceParams(framerate=1), }, )
(PR #6027)
-
SmallWebRTCTransport.capture_participant_video()takes aframerate, as on Daily. It defaults toNone, which passes on every frame as before.
(PR #6027) -
pipecat initnow offers LiveKit as a web transport (-t livekit). The generated bot addsLiveKitParamsto its transport params,.env.examplelists theLIVEKIT_URL,LIVEKIT_API_KEYandLIVEKIT_API_SECRETcredentials, and generated React and vanilla JS clients connect through@pipecat-ai/livekit-transport. Run the bot withuv run bot.py -t livekit.
(PR #6035) -
Added
LiveKitParams.audio_in_user_tracks, matchingDailyParams.audio_in_user_tracks. Set it toFalseto mix all participants' audio into one real-time stream ofInputAudioRawFrames. With the default,True, each participant's audio enters the pipeline as its own stream ofUserAudioRawFrames, so a bot with one STT in a room where several participants publish a microphone receives audio faster than real time: its transcripts fall further and further behind and it answers earlier questions.
(PR #6038) -
Added the
on_active_speaker_changedevent toLiveKitTransport, as onDailyTransport: called with{"id": identity}when the room's loudest speaker changes (the bot can be the speaker while it talks).
(PR #6045) -
Added
PIPECAT_LOG_LLM_CONTEXTto control how much of the LLM context LLM services include in their DEBUG logs. Withoff, each response is still logged, but without the context, which otherwise repeats the whole conversation on every inference;full, the default, includes every message. An unknown value raisesInvalidEnvVarValueErrorwhen an LLM service is created.PIPECAT_LOG_LLM_CONTEXT=off python bot.py
(PR #6047)
-
Added
video_in_sourcessupport toLiveKitTransport, capturing each listed source from a participant at its own framerate, or only to answer image requests withon_request_only=True. It also answers aUserImageRequestFramewith the next frame of the requested source, so image-request tools such asfetch_user_imagework on LiveKit, and a request for a source with no subscribed track gets an error result.LiveKitTransport.capture_participant_video()captures a source at a framerate. Withoutvideo_in_sources, every frame of every video track is passed on.
(PR #6055) -
Added
BaseTransport.capture_participant_video(participant_id, framerate=30, video_source="camera", *, on_request_only=False), so a bot captures a user's
video on demand the same way on Daily, SmallWebRTC and LiveKit.
on_request_only=Truepasses on only the frames that answer image requests,
as inVideoInSourceParams, and aframerateofNonepasses on every
frame, whichDailyTransportnow accepts too. Transports that don't receive
user video log a warning.client_id = transport.get_client_id(client) await transport.capture_participant_video(client_id, framerate=1, video_source="screenVideo")
(PR #6056)
-
Added
BaseTransport.get_client_id(client), which returns the id of the client passed toon_client_connected: the participant id on Daily, the participant identity on LiveKit, the peer connection id on SmallWebRTC and the stream id on Vonage. Transports without client events log a warning and return an empty string.
(PR #6056) -
BlandTTSService.SettingsandBlandHttpTTSService.Settingsgainedauto_formatting, which asks Bland to rewrite phone numbers and SSNs into digit groups the voice reads digit by digit. It is fixed at session start, so changing it reconnectsBlandTTSService.
(PR #6061) -
pipecat evalcan drive a bot over a WebSocket the bot accepted itself, such as a Pipecat Cloud session or the dev runner's/wsroute. The harness sends its connection's flags in aneval-connectmessage ahead of the RTVI handshake, andcreate_transportbuilds the newEvalFastAPIWebsocketTransportfrom it, so a bot's existingtransport_params["eval"]entry works unchanged.
(PR #6064) -
Added
EvalSessionParams.bot_ready_timeout_sand--bot-ready-timeoutonpipecat eval run: how long the harness waits for the bot's RTVIbot-readyonce connected, which covers the bot's startup. Defaults to 60 seconds.
(PR #6064) -
The dev runner's WebSocket sessions (
/ws,/ws-client) receive the--runner-bodyfile as theirrunner_args.body, since they have no/startrequest to carry one.
(PR #6064) -
Added
audio_in_frame_sourcetoVonageVideoConnectorTransportParamsto choose which incoming audio framesVonageVideoConnectorTransportemits: the session-wide mixedInputAudioRawFrame(AudioInFrameSource.MIXED), per-participantUserAudioRawFrames (AudioInFrameSource.INDIVIDUAL), or both (AudioInFrameSource.BOTH).
(PR #6066) -
Added
TransportParams.audio_out_volumeto set the starting volume of the output audio, 1.0 by default.
(PR #6071) -
Added
VolumeFrameandVolumeGainFrameto control the volume of the bot's audio.VolumeFramesets the output transport's volume, andVolumeGainFramesets a gain on top of it for some audio, for exampleVolumeGainFrame(0.5)before aTTSSpeakFrameandVolumeGainFrame(1.0)after it to play that utterance at half volume. Both take effect in order with the audio, and an interruption doesn't drop them.
(PR #6071) -
Added
text_typetoTTSSpeakFrame, so the text the TTS service speaks for it carries that type instead of always being a sentence. A custom type, such as"status", lets RTVI keep that text from the client withRTVIObserverParams(skip_text_types=["status"]).
(PR #6072) -
Added
segment_idtoTTSTextFrame, the ID of theAggregatedTextFrameit speaks. TTS services set it on the words they report with timestamps, and on text they speak in one piece.
(PR #6072) -
Added
text_typeto the RTVIbot-outputmessage. It says what kind of text the message holds: a sentence, a word, or a custom type such as"code".
(PR #6072) -
Added
check_from: turn_startto the scripted eval scenario language (pipecat.evals). It lets an expectation run against events since the beginning of the turn that were not used by the expectations above it.
(PR #6088) -
DeepgramFluxSTTServicenow pushes anInterimTranscriptionFramefor each FluxUpdateevent, holding the transcript of the turn so far.
(PR #6092) -
Added
UserBackchannelFrame, for a short acknowledgment the user says while the bot talks, such as "mhm". It is pushed in place of a transcription, so the bot keeps talking and the acknowledgment stays out of the LLM context.
(PR #6093) -
Added
TextType.BACKCHANNELandBotBackchannelFrame, aTTSSpeakFramefor a short acknowledgment the bot says while the user talks, such as "Mm-hmm.". Its text frames have the backchannel text type, it stays out of the LLM context, and interruptions don't cut it off unless the frame is made interruptible.
(PR #6093) -
Added the RTVI
user-inputmessage, the user's counterpart ofbot-output: what the user said or typed, with aninput_typeoftranscription,chatorbackchannel, andfinalset to false for an interim transcription. The observer sends one for each transcription, alongsideuser-transcription, and for eachUserBackchannelFrame. It also acknowledges eachsend-textmessage, after any interruption it causes, withinput_typechat, the message's id asmsg_idand, on transports that know the sender such as Daily and LiveKit, their id asuser_id;RTVIProcessorpushes anRTVISendTextFramefor this.RTVIObserverParams.user_input_enabledturns it off.
(PR #6093) -
LLMUserAggregatorandLLMAssistantAggregatorreport backchannels, which stay out of the LLM context and its turn events.on_user_backchannelfires with aUserBackchannelMessagefor eachUserBackchannelFramewhile the user isn't muted, andon_assistant_backchannelfires with anAssistantBackchannelMessagewhen the assistant starts saying text of typebackchannel.
(PR #6093) -
RTVI now reports bot backchannels to clients of protocol 2.2.0 or later. Text of type
backchannel, such as aBotBackchannelFrame's, is sent asbot-outputwithtext_type(and the deprecatedaggregated_by) set to"backchannel", and asbot-tts-text. Add"backchannel"toRTVIObserverParams.skip_text_typesto keep it out of both messages for every client.
(PR #6093) -
Classifiers can now answer questions about images.
ask(),yes_no(),choice()andscore()takeimages=, a list ofClassifierImage(PNG, JPEG or WebP), andClassifierImage.from_frame()builds one from an image frame such as a frame from the user's camera.ClefClassifiertakes up to 4 images per call (CLEF_MAX_IMAGES), andLLMClassifiersends them to its LLM, which must be able to see them. A classifier says whether it can see images insupports_images, and one that cannot raisesClassifierErrorwhen given some.image = await ClassifierImage.from_frame(frame) results = await classifier.yes_no( "A frame from the user's camera.", {"person": YesNoQuestion(instructions="Is there a person in the frame?")}, images=[image], )
(PR #6101)
-
Added
OpenAIDecisionsClassifier, which answers classifier questions with OpenAI's Decisions API (gpt-6-luna). It takes an OpenAI API key, and several classifiers can share oneOpenAIDecisionsClient. A choice question can have at most 255 options (OPENAI_DECISIONS_MAX_CHOICE_OPTIONS). Install withuv add "pipecat-ai[openai]".classifier = OpenAIDecisionsClassifier(api_key=os.getenv("OPENAI_API_KEY")) results = await classifier.yes_no( "Hi, you've reached Dana. Leave a message.", {"voicemail": YesNoQuestion(instructions="is this a voicemail?")}, )
(PR #6102)
-
RTVIObservercan send the raw text of the STT, LLM and TTS asstt-raw-text,llm-raw-textandtts-raw-text, to clients of protocol 2.2.0 or later. They carry the same data asuser-transcription,bot-llm-textandbot-tts-text, and are off by default;RTVIObserverParams.stt_raw_text_enabled,llm_raw_text_enabledandtts_raw_text_enabledturn them on.
(PR #6104) -
OpenAIDecisionsClassifiercan see images: passimages=toask(),yes_no(),choice()orscore(), up to 128 per call (OPENAI_DECISIONS_MAX_IMAGES).
(PR #6106) -
Added
efforttoAnthropicLLMService.Settings, sent asoutput_config.effort. It sets how much the model spends on its response, thinking included, and lowering it is Anthropic's recommended way to reduce thinking.
(PR #6108) -
Added a
modesetting toGeminiSTTService.Settings, selecting Gemini Live'sVERBATIM(default) orSMARTtranscription, where the model removes disfluencies and applies light grammatical cleanup and formatting.
(PR #6129) -
Added an
image_sizesetting toGoogleImageGenService.Settings, which asks Gemini image models for a"1K","2K"or"4K"render.
(PR #6130) -
Added a
storesetting toOpenAILiveLLMService, which asks the Live API to keep the session for 30 days as a downloadable stereo recording and as a source a later session can fork from.
(PR #6135) -
NeuphonicTTSService.SettingsandNeuphonicHttpTTSService.Settingsgainedipa_key, selecting the regional variant (es-castilianorpt-br) whose sounds Neuphonic reads<phoneme:...>tags with.
(PR #6139) -
Added
qualitytoOpenAIImageGenService.Settings, forwarded to OpenAI's Images API. It accepts the tiers the chosen model supports (low,medium,high, andxhigh/maxon the GPT Image 2.5 models) and trades detail for generation speed; left unset, OpenAI picks the quality for the model.
(PR #6141) -
SmallestTTSServicenow accepts acontent_filtersetting, passing Smallest's opt-in profanity filter through to the synthesis request.
(PR #6142) -
SpeechifyHttpTTSServicegained asafety_identifiersetting, Speechify's opaque end-user id used to rate-limit or block one user instead of the whole workspace.
(PR #6143) -
Added
transcript_filtering_configtoSpeechmaticsSTTService.Settings, passing Speechmatics' transcript post-processing (remove_disfluencies,replacements) through to Agent STT.
(PR #6144) -
TogetherTTSService.Settingsgainedextra_params, the model-specific options Together accepts in its session update — apronunciation_dictof"<source>/<replacement>"entries, for example.
(PR #6145) -
Added
itn_normalizeandno_format_delaytoSmallestSTTService.Settings.itn_normalizeruns Smallest's inverse text normalization so spoken numbers come back as digits;no_format_delayopts out of the formatting delay ITN introduces on a number split across finals, emitting every final as soon as it is ready instead.
(PR #6154) -
Added
LLMSettings.use_provider_thinking_default. When it's set,AnthropicLLMService,OpenAIResponsesLLMService,OpenAIResponsesHttpLLMService,GoogleLLMServiceandGoogleVertexLLMServicesend no thinking setting to a model that has none configured, so it runs at its provider's default instead of the lowest setting they send a voice LLM.
(PR #6159) -
Added
bot_backchannelanduser_backchannelevents to scripted eval scenarios: a short acknowledgment the bot said while the user talked, from abot-outputof typebackchannelspoken to the end, and one the user said while the bot talked, from auser-inputof typebackchannel.
(PR #6164) -
Added pauses to scripted eval user turns in audio modality:
user:can be a list of text andpause_ms:silences, played to the bot as one stretch of audio.
(PR #6164) -
Added
redact_secrets()(pipecat.utils.security.redaction), which masks credentials in text bound for logs or error reports: values named like a credential (api_key=,Authorization:,X-Amz-Signature=,'token': '...',apiKey := ...), URL passwords, and tokens recognizable by format after gitleaks' rules for the same credentials (OpenAI, Anthropic, AWS and Amazon Bedrock, Google, GitHub, Slack, Hugging Face, Perplexity and Twilio keys, JWTs, PEM private keys).
(PR #6178) -
JevClassifiercan see images: passimages=toask(),yes_no(),choice()orscore(). Jev takes PNG and JPEG images, 256 to 1568 pixels on each side, and rejects images outside those limits rather than resizing them. A WebP image raisesClassifierErrorbefore any request. TypeSafe offers images as a preview, so the limits may change between Jev releases.
(PR #6179) -
Eval simulation results record what they were judged on:
successonEvalSimulationResultandcriterionon each judgedEvalSimulationMetricScore, also written toresults.jsonland topipecat eval judge'sjudged.jsonl.
(PR #6183) -
Added
pipecat eval judgeto judge a saved simulation conversation again without running the bot. Use it to reword asuccessor a metric and see the new verdicts on runs you already have. From Python, useEvalSimulationJudge(simulation).judge(messages).
(PR #6183) -
Eval simulation runs now save their conversation (what each side said and the bot's function calls) to
<stem>.conversation.json, ready forpipecat eval judge.
(PR #6183) -
Added
EvalConversationObserver, which records a bot's conversation in the same file format eval simulation runs save. Add it to a bot to judge its real calls withpipecat eval judge.
(PR #6184) -
Added
LLMContextUserTurnFrame, broadcast by the user aggregator each time it adds a user turn to the LLM context. It's the user's side ofLLMContextAssistantTurnFrame.
(PR #6184) -
Added
initial_receive_bitratetoDailyParams: the bot's expected download bandwidth, in bits per second. The SFU starts its estimate of what it can send the bot from this value instead of Daily's 1 Mbps default, so incoming video reaches full quality sooner.
(PR #6204) -
Added an
initial_receive_bitrateargument toTavusVideoService, applied when it joins the Tavus room, so incoming video reaches full quality sooner.
(PR #6204) -
Added
RimeV1TTSServicefor Rime's WebSocket v1 API, with binary and JSON protobuf envelopes, Coda text lookahead, and context cancellation without reconnecting. Text is sent as sentences by default; passtext_aggregation_mode=TextAggregationMode.TOKENto stream LLM tokens, which Rime accumulates into sentences. Installpipecat-ai[rime]to use it.
(PR #6208) -
Added the
on_network_connection_status_updatedevent toDailyTransport, called when a network connection needed for the call ("signalling","sendTransport"or"recvTransport") is"interrupted"or"connected".
(PR #6211) -
OLLamaLLMServiceaccepts anapi_keyfor an Ollama server that requires one, such as one behind an authenticating proxy. Passingapi_keyused to raiseTypeError.
(PR #6214) -
Added
minimax,ollama,openclawandxttsextras, souv add "pipecat-ai[ollama]"and the like install without a missing-extra warning. Like the extras of other services that need only the core dependencies, they are empty.
(PR #6223)
Changed
-
The minimum
aiohttpversion is now 3.14.0 (required bygoogle-genai>= 2.8.0). Applications pinningaiohttp<3.14will need to update their pin.
(PR #3415) -
The RTVI protocol version is now 2.2.0, adding the
send-fileclient message. Clients gatesendFile()on the bot advertising 2.2.0+.
(PR #3415) -
OpenAIImageGenServicenow defaults togpt-image-2.5-flare. Its previous default,dall-e-3, was removed from the OpenAI API on May 12, 2026 and every request naming it is rejected.
(PR #5546) -
BackendLLMWorkernow appends a pairing instruction to its LLM's system instruction (BACKEND_PAIRING_INSTRUCTION): that it is the backend of a voice assistant, which sends it requests and handles the rest of the conversation itself; that it is heard only through a message it begins with>>(the whole of it is told to the user, tool calls or not, and everything else it writes is a note the assistant keeps but does not speak: a result, a question it cannot proceed without, or news the user should have now is worth the mark, and that it has stopped or has nothing to do is not); and how to take requests that arrive while it works (follow the latest, cancel the tools whose results are no longer wanted, do the newest first, and give each result as soon as it has it). A backend prompt need only say what the backend does.
(PR #5689) -
OpenAILiveLLMServiceclient delegation now drives itsBackendLLMWorkerthrough the worker'sattach,messageandcanceljobs, the same contractLLMWithBackenduses: the service attaches once for the session, sends each delegation as a message, and takes the backend's outputs and tool calls from the stream. Delegations still run one at a time, a follow-up starting when the backend reports it has nothing left to do, andClientDelegation.timeout_secsstill abandons one the backend takes too long on.ClientDelegation.backendnow also takes a worker's name, for a backend running in another process on a shared bus.
(PR #5689) -
LLMWorkerandLLMContextWorkerno longer require aname. One is generated when omitted, as for any other worker; a worker that others address by name, such as one in another process, still needs one.
(PR #5689) -
Realtime LLM services take each function call result from the
FunctionCallResultFramethe service broadcasts, as it is produced, rather than finding it in the context on the next frame the aggregator pushes. A result therefore reaches the provider even when it asks for no inference, or arrives while the user is speaking. What runs the model is unchanged: the context frame the assistant aggregator pushes after a result, which weighsrun_llm, sibling calls, bot speech and user speech. On the OpenAI-protocol services a response asked for while one is already running is now held until that one is done, where before the API refused it and the reply was lost.
(PR #5689) -
An
LLMMessagesAppendFramethat asks to run inference now takes the same path at the assistant aggregator as a function call result: it is held while the bot speaks, since a run mid-sentence answers a context that lacks the answer in progress and the model answers it again; bundled with the results and appends queued behind it, so a burst runs inference once; and left to the user's turn while the user speaks, since that turn runs inference on the context as it stands.
(PR #5689) -
GeminiLiveLLMServicenow defaults tomodels/gemini-3.8-live, where it defaulted tomodels/gemini-2.5-flash-native-audio-preview-12-2025. Synchronous tools are declaredBLOCKINGon 3.8, whereNON_BLOCKINGis the model's default, so a tool call still finishes the model's turn.
(PR #5689) -
⚠️ Breaking change:BackendOutputno longer carriesis_final. A backend's outputs are a stream with no final one: aBackendLLMWorkersends every turn its model writes as it is produced, whether anLLMWithBackendor anOpenAILiveLLMServiceclient delegation is attached, and says when it has nothing left to do.transform_outputis called astransform_output(output), may returnNoneto send nothing for an output, and what it returns is sent as is, empty text included. A message the model begins with>>asks to be spoken (prefers_spoken=True) and arrives without the mark; anything else it writes, a reasoning summary included, does not. The transform sees each output after that, and is where code can change it.
(PR #5689) -
AssemblyAISTTServicenow defaults touniversal-3-6-pro, AssemblyAI's flagship streaming model and the server-side default. It has the same feature set asuniversal-3-5-proand covers 32 declared languages instead of 19.universal-3-5-proremains supported.
(PR #5933) -
AWSNovaSonicLLMService's docstrings and the AWS Nova Sonic examples now document only Nova 2 Sonic. Bedrock removed the first-generationamazon.nova-sonic-v1:0at its 2026-09-14 end of life, and requests naming it are rejected.
(PR #5935) -
SpeechifyHttpTTSServiceno longer accepts the legacysimba-englishandsimba-multilingualmodels. Speechify retires them at the API version the service now pins, so requests naming them fail withmodel_retired, including on workspaces whose default version still allowed them. Usesimba-3.2(English) orsimba-3.0(multilingual) instead.
(PR #5937) -
ElevenLabsDialogueTTSServicenow defaults toeleven_v4_turboinstead ofeleven_v3_conversational. Setmodel="eleven_v3_conversational"in itsSettingsto keep the previous model.
(PR #5959) -
Updated the
rnnoiseextra to requirepyrnnoise>=0.4.5and removed itsav<17.1.0cap. Withaudiolab0.5.2,pyrnnoise0.4.3 failed withTypeError: Graph.__init__() got an unexpected keyword argument 'rate'; 0.4.5 fixes that.
(PR #5975) -
The Gemini Live
llm_responsetracing span reports thinking tokens asgen_ai.usage.reasoning.output_tokens, the attribute the other LLM spans use, instead ofgen_ai.usage.reasoning_tokens. Itsgen_ai.usage.input_tokensandgen_ai.usage.output_tokensalso include tool-result and thinking tokens, matching the service's token metrics.
(PR #5984) -
WebsocketTTSServicecloses every open audio context, and moves a turn in progress to a new context ID, when it replaces its provider connection. Subclasses no longer need to close audio contexts in_disconnect_websocket.
(PR #6008) -
BlandTTSServicenow works like the other single-stream TTS services.- It waits until each bot turn has played before sending the next turn's text, since a new turn sent to Bland interrupts the one in progress.
- After reconnecting mid-turn, it sends the rest of the turn to the new session instead of dropping it with an error.
- After a turn-level error, it keeps sending the rest of that turn's text, as other services do.
- A turn that ends as
failedwithout anerrormessage from Bland is logged as a warning and no longer pushes anErrorFrame.
(PR #6014)
-
The
runnerextra now requirespipecat-ai-prebuilt>=1.3.0. The prebuilt client UI served by the development runner now has a camera toggle in the Devices section, next to the mic and screen-share controls. The camera starts off until the user turns it on.
(PR #6026) -
SmallWebRTCTransportanswers aUserImageRequestFramewith the next frame it passes on, which carries the request, asDailyTransportdoes. It previously pushed an extra copy of the frame for each request, with the request'suser_id; the answering frame now keeps the peer's id, and several waiting requests are answered by successive frames.
(PR #6027) -
Projects generated by
pipecat initnow requirepipecat-ai>=1.12.0(previously>=1.4.0), so a new project installs a release that matches the code it was generated with.
(PR #6035) -
LLM services log
<service>: Generating LLM response [...]on each inference, and realtime services (OpenAI Realtime, Gemini Live, Inworld, Azure Voice Live, xAI Grok) log<service>: Setting up LLM conversation [...]when they seed their server-side conversation. These lines replaceGenerating chat from context, the OpenAI Responses services'Generating response from universal context, the realtime services'Setting up ... conversation with initial messages, and Gemini Live'sCreating initial response. Log filters matching the old strings need updating.
(PR #6047) -
⚠️ get_llm_invocation_params()on LLM adapters is now async, so every LLM service can fetch the files a provider needs while building its request. Code that calls it mustawaitit, and a custom adapter must define it withasync def; one that defines it as a sync method gets aDeprecationWarningwhen the class is defined.AWSNovaSonicLLMService.get_setup_params()is now async as well.
(PR #6059) -
⚠️ Thegstreamerextra now requirespygobject>=3.52.0,<4, which builds against GLib 2.80 or newer. On Debian and Ubuntu, installlibgirepository-2.0-devinstead oflibgirepository1.0-dev.
(PR #6060) -
The default voice of
BlandTTSServiceandBlandHttpTTSServiceis now Karen (29158307-9893-4149-8a75-bc9ce313d64e) instead of River. Setvoiceto keep River.
(PR #6062) -
The eval harness sends an
eval-connectclient message first on every connection. A bot on an earlier Pipecat version reads its flags from the URL as before, but receives the message as an RTVI client message of typeeval-connect(anRTVIClientMessageFrameand anon_client_messageevent).
(PR #6064) -
⚠️ Local evals run through the dev runner: start the bot withpython bot.py, andpipecat eval runconnects tows://localhost:7860/wsby default. Each scenario gets a freshbot()and pipeline. A--bot-urlwithout the/wspath is rejected; add/wsto it.pipecat eval suitespawns bots without-t eval, connects to their/wsroute, and stops each bot as soon as its run ends. A manifest whosespawn:still passes-t evalkeeps working.- The harness waits up to 60 seconds for a bot's
bot-ready, up from 10. - The harness ends the bot's session when a run finishes, whatever the bot's
on_client_disconnectedhandler does. A bot serving the serverEvalTransport, such as one run with-t evalon an earlier Pipecat version, exits after its first scenario.
(PR #6064)
-
⚠️ Behavior change:VonageVideoConnectorTransportnow emits only the mixedInputAudioRawFrameby default and no longer emits per-participantUserAudioRawFrames. To keep the previous behavior, passaudio_in_frame_source=AudioInFrameSource.BOTH.
(PR #6066) -
The
vonage-video-connectorextra now requiresvonage-video-connector~=1.0.3, which installs on Linux with Python 3.11 and later (previously Python 3.13 only).
(PR #6066) -
The second parameter of the built-in text transforms in
pipecat.utils.text.transformsis now namedtext_typeinstead ofaggregation_type. Transforms are called with it positionally.
(PR #6072) -
KrispVivaFilternow emits aDeprecationWarning(instead of a log warning) when it falls back to the deprecatedKRISP_VIVA_MODEL_PATHenvironment variable. The warning states that the variable will be removed in 2.0.0; setKRISP_VIVA_FILTER_MODEL_PATHinstead.
(PR #6074) -
AWSNovaSonicLLMService's default model is now Nova 2.5 Sonic ("amazon.nova-2-5-sonic"), which keeps the Nova 2 Sonic API surface under a new Bedrock model id.
(PR #6077) -
Pipecat's deprecation warnings for parameters, fields, environment variables, and behavior changes now all show, once per line of code that triggers them, including warnings Pipecat raises from its own code while a pipeline runs. Previously about 50 of them were hidden unless the line they named was in your main script, and the rest repeated on every call. Like those, they show regardless of your warning filters.
(PR #6080) -
Pipecat's deprecation warnings for parameters, fields, environment variables, and behavior changes now name the release that deprecated them and the release that removes them, in the same form as the
@deprecatedones:`Subject` is deprecated since X.Y.Z and will be removed in 2.0.0. Use `Replacement` instead.Update anypytest.warns(match=...)orfilterwarnings(message=...)patterns written against the old text.
(PR #6080) -
The eval harness (
pipecat.evals) reports function calls made outside the bot's pipeline and reported into it (anExternalFunctionCall*Frame) asfunction_call/function_call_stoppedevents.
(PR #6088) -
SonioxSTTServiceandSonioxTTSServicenow report the errors Soniox sends in-stream with anErrorCategoryderived from the HTTP status code the message carries, so an exhausted balance arrives asquotaand a rejected key asauthenticationrather thanunknown. The error framesSonioxTTSService.run_ttsyields on a failed send or connect carry the exception, so those are classified too.
(PR #6089) -
AssemblyAISyncSTTServicenow defaults touniversal-3-6-pro, matchingAssemblyAISTTService. To keep usinguniversal-3-5-pro, passAssemblyAISyncSTTService.Settings(model="universal-3-5-pro").
(PR #6090) -
The deprecated
AssemblyAIConnectionParamsnow defaultsspeech_modeltouniversal-3-6-pro, so anAssemblyAISTTServicebuilt withconnection_params=and no explicitspeech_modeluses the same model as one built withsettings=.
(PR #6090) -
SonioxSTTServiceandSonioxTTSServicenow authenticate with anAuthorization: Bearerheader on the WebSocket handshake instead of anapi_keyfield in the config message, which Soniox deprecates and stops accepting on January 15, 2027.
(PR #6096) -
AnthropicLLMService,OpenAIResponsesLLMService,OpenAIResponsesHttpLLMService,GoogleLLMServiceandGoogleVertexLLMServicenow send each model they list the lowest reasoning setting its provider documents, unlessthinking,effortorreasoningis configured, and log the setting at INFO. A reasoning model they don't list runs at the provider's default and logs a WARNING.
(PR #6108) -
TTS services created with
pause_frame_processing=Truenow hold incoming frames after a reply until every open audio context has ended, instead of until the bot stops speaking. A context ends once its audio has all arrived, or afterstop_frame_timeout_swithout audio for services that never end it.
(PR #6118) -
DeepgramFluxSTTServiceandDeepgramFluxSageMakerSTTServicenow apply anumeralsupdate over the live connection with a Configure message instead of reconnecting.
(PR #6124) -
GeminiLiveVertexLLMServicenow defaults togoogle/gemini-3.8-live, the generally available Live model Vertex AI recommends, where it defaulted togoogle/gemini-live-2.5-flash-native-audio, which Vertex retires on 2026-12-13.
(PR #6127) -
TranslationConfig.modelforGladiaSTTServicenow documents all three values Gladia's live API accepts —base,batchandenhanced— and that Gladia usesbatchwhen the field is unset.
(PR #6128) -
The Google LLM examples now use
gemini-3.6-flash, which any Google API key can reach; access to the Gemini 2.5 models is limited to projects that already used them.
(PR #6131) -
GeminiLiveLLMService's docstrings now namegemini-3.8-liveas the example model for thethinking,enable_affective_dialogandproactivitysettings, where they namedgemini-2.5-flash-native-audio-preview-12-2025, which Google retires on 2026-11-17.
(PR #6132) -
OpenAIResponsesLLMServiceandOpenAIResponsesHttpLLMServicenow document"ultrafast"among the values theirservice_tierargument accepts, OpenAI's Ultrafast Processing tier.
(PR #6136) -
SarvamLLMServicenow acceptsdeepseekv4.1-flashandglm5.3, Sarvam's current open-weight models, in place of the retireddeepseekv4-flashandglm5.2.
(PR #6138) -
BackendConnectornow refuses a request strategy that reads the frontend's context, such asTranscriptBackendRequestStrategy, on a speech-to-speech frontend, whose context lags the audio:LLMWithBackendraisesValueError. Strategies mark this with the newBackendRequestStrategy.requires_contextattribute.
(PR #6157) -
BackendLLMWorkersetsuse_provider_thinking_defaulton its LLM, so the backend of anLLMWithBackendruns at its provider's default reasoning. Reasoning configured in its settings still applies.
(PR #6159) -
The eval harness reads the user's transcriptions from the bot's
user-inputmessages instead of the deprecateduser-transcription. Theuser_transcriptionscenario event is unchanged.
(PR #6164) -
PipelineWorkernow pushes theStartFramewhile processors are still being set up, and each processor holds it only until its ownsetup()is done. A slow connection (e.g. the LLM's) no longer delays processors ahead of it: the input transport starts reading audio right away and STT gets it as soon as its own connection is up.on_pipeline_startedstill fires when theStartFramereaches the end of the pipeline, after every processor has been set up and has handled it.start_timeout_secsnow counts from when every processor is set up. The input transport'son_client_connectedcan now fire before later services have connected; useon_pipeline_startedfor anything that needs them.
(PR #6167) -
If a
PipelineWorkeris cancelled or its setup times out before the pipeline has started, the processors still being set up never get theStartFrameand never start. The pipeline is just cleaned up: noCancelFrameis sent, and the processors that had already started are stopped by theircleanup().
(PR #6167) -
When Jev rejects a request, the
ClassifierErrorfromJevClassifiernow includes the reason Jev gave, such asJev rejected the request: HTTP 400: Invalid image request.
(PR #6179) -
The eval judge's
reasonnow points to what in the conversation supports the classifier's verdict, so it never argues against that verdict. It doesn't say how the classifier decided, since a classifier gives no reasons. Each verdict also reports the classifier'sconfidenceandprobabilitiesin fields of their own.
(PR #6183) -
⚠️ Breaking change: Services for OpenAI-compatible providers other than OpenAI now raiseValueErrorwhenapi_keyisNoneor empty, andGroqSTTServiceandOpenRouterLLMServicenow requireapi_keyas an argument. None of these services reads its provider's key from the environment, so pass the key explicitly, e.g.GroqSTTService(api_key=os.environ["GROQ_API_KEY"]).NvidiaLLMServicestill accepts no key, for a local NIM.
(PR #6193) -
Updated the bundled Silero VAD model to v6.2, which detects quiet, muted, and unusual voices and lower-quality phone audio more reliably, and stays accurate through long stretches of silence or noise. Speech stop events can arrive slightly later than with v6.
(PR #6202) -
The
dailyextra now requiresdaily-python>=0.35.0.
(PR #6204) -
The
dailyextra now requiresdaily-python>=0.36.0, the first version with the network connection status event.
(PR #6211) -
pipecat.services.aws.utils.resolve_credentials()returns keys from its parameters or the environment only. When neither supplies them,access_keyandsecret_keyareNone, so the caller's AWS session can take them from the default chain and renew them.
(PR #6212) -
In
pipecat eval suite, a manifest entry'sconcurrency:now caps only that entry's runs. Before, entries for the same bot without aname:shared one cap, the lowest any of them set. EachEvalRuncarries its entry's position in the manifest'ssuite:list asentry.
(PR #6225) -
bot-outputfor text that isn't spoken now always hasspoken_status: "new", since its only message is the first of its segment. That includes a segment of a type in a TTS service'sskip_text_typesand a speech-to-speech service's transcript, which had nospoken_statusbefore.
(PR #6227)
Deprecated
-
The development runner's
-f/--folderflag is renamed to--downloads-folder;--folderremains as a deprecated alias.
(PR #3415) -
GoogleImageGenService.Settings.negative_promptis deprecated with no replacement. Gemini image models do not accept a negative prompt, so the value is ignored.
(PR #5954) -
SarvamRealtimeSTTService.Settings.language_codeis deprecated. Uselanguageinstead, withNonefor auto-detection.
(PR #6010) -
pipecat.classifiers.jev.classifierandpipecat.classifiers.jev.clientare deprecated. Usepipecat.classifiers.typesafe.jev.classifierandpipecat.classifiers.typesafe.jev.clientinstead. The old paths still work until 2.0.0.
(PR #6017) -
The
jevextra is deprecated. Usetypesafeinstead:uv add "pipecat-ai[typesafe]". Thejevextra installs the same packages until 2.0.0.
(PR #6017) -
Deprecated
get_transport_client_id()inpipecat.runner.utils. Calltransport.get_client_id(client)instead.
(PR #6056) -
Deprecated passing
framerate=0tocapture_participant_video()to capture only the frames that answer image requests. Passon_request_only=Trueinstead, as inVideoInSourceParams.
(PR #6056) -
Deprecated
maybe_capture_participant_camera()andmaybe_capture_participant_screen()inpipecat.runner.utils. List the sources inTransportParams.video_in_sourcesto capture them as each user connects, or callBaseTransport.capture_participant_video()to capture one on demand.
(PR #6056) -
Deprecated calling
SmallWebRTCTransport.capture_participant_video()without a participant id, ascapture_participant_video(video_source="camera")orcapture_participant_video("camera"). Pass the peer's id first, as on other transports:capture_participant_video(client_id, framerate, video_source). Calls without it keep passing on every frame unless aframerateis given.
(PR #6056) -
pipecat eval run's--stop-botand--trigger-disconnect,EvalSessionParams.stop_botandEvalSessionParams.trigger_disconnect, and the scenario fieldtrigger_disconnect:are deprecated with no replacement. The harness ends the bot's session after every run, and a bot fireson_client_disconnectedwhenever the harness disconnects.
(PR #6064) -
The dev runner's
-t evalis deprecated and ignored: run the bot without-t, and the eval harness connects to its/wsroute.EvalRunnerArgumentsis deprecated in favor ofWebSocketRunnerArguments, and the WebSocket serverEvalTransportin favor ofEvalFastAPIWebsocketTransport. All are removed in 2.0.0.
(PR #6064) -
Deprecated
AggregationType. UseTextTypefrompipecat.utils.text.base_text_aggregatorinstead, which has the same members. ImportingAggregationTypefrom there or frompipecat.frames.framesstill works and returnsTextType.
(PR #6072) -
Deprecated the
aggregation_typeargument ofTTSService.add_text_transformer(),TTSService.remove_text_transformer(),RTVIObserver.add_bot_output_transformer()andRTVIObserver.remove_bot_output_transformer(). Usetext_typeinstead.
(PR #6072) -
Deprecated the
aggregated_byfield of the RTVIbot-outputmessage. Readtext_typeinstead, which carries the same value. The bot sends both fields.
(PR #6072) -
Deprecated
aggregated_byonAggregatedTextFrame,TTSTextFrameandAggregatedTextProgressFrame. Usetext_typeinstead, which means the same.aggregated_bystill works as a constructor argument and as an attribute: reading it returnstext_type, and assigning to it setstext_type.
(PR #6072) -
Deprecated
skip_aggregator_typesonTTSServiceandRTVIObserverParams. Useskip_text_typesinstead.
(PR #6072) -
pipecat.utils.deprecation.warn_deprecated_read()is deprecated. Usewarn_deprecated(message, stacklevel=2)from the same module instead, which raises any hand-written deprecation warning, including one for a field read through__getattribute__.
(PR #6080) -
The RTVI
user-transcription,bot-llm-textandbot-tts-textmessages are deprecated and will be removed in 2.0.0. Useuser-inputfor what the user said, orstt-raw-text,llm-raw-textandtts-raw-textfor each processor's raw output. They're still sent by default until then.RTVIObserverParams.user_transcription_enabled, which only turns offuser-transcription, is deprecated with it.
(PR #6104)
Fixed
-
A plain-dict
TTSUpdateSettingsFramewhose nestedprosodysets only some keys no longer resets the other prosody settings onFishAudioTTSService.
(PR #5534) -
Fixed
OpenAIImageGenServicefailing against OpenAI's GPT Image models. Those models return the image inline as base64 rather than as a hosted URL, which the service reported asImage generation failed; it now decodes the inline data and emits the frame either way.
(PR #5546) -
Fixed
DailyTransportstopping a Daily room's transcription when the bot joins. Daily reportstranscription-startedfor every transcription in a room, including ones started by the room or another participant, and the transport replied with its own participant list. That list is empty whentranscription_enabledis False, and an empty list tells Daily to transcribe nobody. The transport now leaves transcriptions it was not asked to manage alone.
(PR #5571) -
TogetherTTSServicenow sends itslanguagesetting to Together, resolved to the ISO 639-1 or lowercase locale code the API accepts. The setting was previously ignored, so every voice was synthesized in English.
(PR #5581) -
TogetherTTSServicenow emits sample-aligned audio frames. Together's WebSocket audio deltas can end mid-sample, and the odd-length frame that resulted crashed the service's audio task when metrics were enabled, leaving the turn without audio.
(PR #5581) -
Fixed
BackendLLMWorkercarrying on with work its requester had let go of. WhenOpenAILiveLLMServiceabandons a client delegation, because it timed out or the conversation was reset, the worker's model kept running and any tool call it was waiting on kept waiting, and their output could be reported under a later delegation. The worker now interrupts its pipeline and cancels its function calls in flight when itsattachjob is cancelled, andOpenAILiveLLMServicedetaches and attaches again in both cases.
(PR #5689) -
Tweaked the async-tool guidance given to LLMs (
ASYNC_TOOL_INSTRUCTIONSand the final-result message) so a result that lands after the model has already answered the user is more reliably delivered on its own, without the model repeating its previous reply first.
(PR #5689) -
Fixed Inworld Realtime sending a user's message twice when a function call result arrived between the server's VAD turn and the aggregator writing that message.
(PR #5689) -
Fixed a
PipelineWorkerwhose pipeline ended on its own (anEndFrameit queued, an idle timeout, a fatal error) leaving its child workers running, which kept theWorkerRunnerfrom exiting. The worker now cancels its children as it finishes.
(PR #5689) -
Fixed a tool result asking for inference being dropped when another result was queued behind it and that one asked for none: the assistant aggregator now runs inference once the burst is in, whatever the last result asks for. A run that starts meanwhile, such as the one the user's turn brings, settles the owed push, since it sees the context as it stands; a turn with nothing in it brings no run, so the push stays owed until the next result or append.
(PR #5689) -
Fixed an LLM service's built-in tools (the tools the framework adds on the service's behalf, such as the
cancel_<tool>tool for a tool registered withcancellable_by_llm=True, orLLMWithBackend'sdelegate) never reaching the provider when the context advertised no tools of its own. That is the case for a realtime service whose tools are given at construction, viatools=or a session's properties, rather than through the context: a cancellable tool given that way had no cancel tool at the provider. Fixed the realtime services (OpenAI, Grok and Inworld Realtime, Gemini Live, Nova Sonic) dropping those constructor-given tools whenever the context's only tools were built-in ones. Built-in tools are now sent on every inference alongside whatever tools the context or the service provides.
(PR #5689) -
Fixed
RTVIObserverreporting a function call as stopped on each intermediate result (FunctionCallResultProperties(is_final=False)). It now reports the call stopped on the final result only.
(PR #5689) -
Fixed
GoogleImageGenServiceleaving itsgenai.Clientasync session open at shutdown. It now closes the session incleanup().
(PR #5762) -
Fixed
WhisperSTTServicedecoding speech on the event loop, which blocked other tasks, including transport audio, for the length of each transcription. Decoding now runs in the worker thread.
(PR #5931) -
Fixed
GoogleLLMServiceleaving Gemini's thinking tokens out ofcompletion_tokens. Gemini reports them inthoughts_token_count, apart fromcandidates_token_count, so with thinking on the output count was low andreasoning_tokenscould exceedcompletion_tokens.completion_tokensnow includes them, which is how the OpenAI services report reasoning.
(PR #5932) -
SpeechifyHttpTTSServicenow pins Speechify's API version to2026-09-30instead of leaving it to the workspace default. On earlier versions Speechify answers thepcm_16000output format with 24 kHz audio, which the service stamped as 16 kHz, so 16 kHz pipelines played Speechify speech at the wrong rate.
(PR #5937) -
SpeechmaticsSTTServicenow mapsLanguage.TLandLanguage.FILonto Speechmatics'tllanguage pack.Language.FILpreviously resolved tofil, which Speechmatics rejects.
(PR #5938) -
Fixed
DeepSeekLLMServiceignoring its own request parameters: it sent the stock OpenAI ones instead, sothinkingnever reached DeepSeek (V4 models ran a reasoning pass before every answer despite thedisableddefault) and it sentseedandmax_completion_tokens, which DeepSeek doesn't support.
(PR #5939) -
Fixed
InceptionLLMServicedropping anextra_bodypassed throughSettings.extrawhenrealtimeis set, andSarvamLLMServicewritingwiki_groundinginto theextra_bodyofSettings.extra. In both, anextra_bodyfromSettings.extranow wins key by key over the service's own fields.
(PR #5939) -
TogetherTTSServicenow escapes thevoiceit sends to Together, so Kokoro's blended voice names (af_bella(2)+af_heart(1)) reach the server intact instead of being rejected as an unknown voice.
(PR #5940) -
GrokRealtimeLLMServicenow accepts 11025 Hz PCM audio, which xAI's realtime API supports but Pipecat rejected.
(PR #5941) -
Fixed
TwilioFrameSerializer,TelnyxFrameSerializerandExotelFrameSerializerending the call when the carrier sent a malformed or incomplete WebSocket message. Such messages are now logged and ignored, as in the other telephony serializers.
(PR #5946) -
Fixed
GoogleImageGenServicefailing on every request with "This method is only supported in Gemini Enterprise Agent Platform mode". Google shut down the Imagen models the service used; it now generates images with Gemini image models, defaults togemini-3.1-flash-image, and emits RGB frames.number_of_imagesmakes one request per image.
(PR #5954) -
Fixed Japanese sentence aggregation splitting Latin abbreviations such as
Dr.andU.S..
(PR #5961) -
Fixed
AnthropicLLMServiceover-reporting token usage on every response:prompt_tokenswas double the real input count andcompletion_tokenswas a few tokens high. Anthropic's streamed usage counts are cumulative, and the service added them up across events instead of taking the latest.
(PR #5984) -
Fixed
GrokLLMServiceleaving reasoning tokens out ofcompletion_tokens. xAI reports them incompletion_tokens_details.reasoning_tokensapart fromcompletion_tokens, so with a reasoning model the output count was low andreasoning_tokenscould exceedcompletion_tokens.completion_tokensnow includes them, which is how the OpenAI services report reasoning.
(PR #5984) -
Fixed
GoogleLLMServiceandGeminiLiveLLMServiceleaving tool results out ofprompt_tokens. When Gemini runs code execution, reads a URL with URL context, or (on some models) grounds with Google Search, the results fed back to the model are billed as input but were reported only intotal_tokens.prompt_tokensnow includes them, so it can rise sharply on those turns: a URL-context turn can add several thousand input tokens.
(PR #5984) -
Fixed
GeminiLiveLLMServiceleaving thinking tokens out ofcompletion_tokens, and reporting atotal_tokensthat could also leave them out. Gemini Live reports thinking tokens inthoughts_token_count, apart fromresponse_token_count, and its owntotal_token_countsometimes omits them.completion_tokensnow includes them, andtotal_tokensisprompt_tokenspluscompletion_tokens.
(PR #5984) -
Fixed
MOQTransportending the call when a relay cancelled the peer's audio subscription, which a relay does when the session publishing the peer's broadcast ends: the peer's relay went away, or a reconnecting peer replaced its broadcast with a fresh one. The cancel is now treated as the peer's tracks ending, so the transport subscribes again or redials and follows the peer to its new broadcast.
(PR #5986) -
Fixed
DeepgramHttpTTSServiceemitting audio frames that could end mid-sample when the HTTP response was split there. With metrics enabled, this crashed the audio playback task and left the bot mute for the rest of the session (buffer size must be a multiple of element size). The service now emits only whole 16-bit samples.
(PR #5989) -
Fixed TTS services that could go silent for the rest of the session with
buffer size must be a multiple of element sizewhen metrics were enabled. A provider can split its audio stream mid-sample, and the odd-length audio frames crashed the TTS playback task. Every TTS service now emits audio frames that hold whole 16-bit samples, carrying a split sample over to the next frame, and the time-to-first-audio scan ignores a trailing partial sample.
(PR #5992) -
Fixed non-ASCII text (Hindi, Japanese, etc.) being escaped as
\uXXXXin async tool result messages and in the conversation history the OpenAI, Grok and Inworld realtime adapters pack into a single message.
(PR #5995) -
Fixed
GradiumSTTServicewithenable_turn_detection=Trueopening no turn when the user kept talking after a turn ended mid-sentence, for example after a hesitation. The rest of the sentence stayed in interim transcripts and never reached the LLM until the user paused and spoke again. After a turn's transcript is pushed, the next end-pointing reading beloweot_thresholdnow opens a turn.
(PR #6007) -
Fixed websocket TTS services mishandling the rest of a bot turn after reconnecting mid-turn. The remaining sentences reused the turn's context ID on the new connection, which the provider had never seen, so
InworldTTSServicecould drop them without an error andElevenLabsTTSServicecould speak them without the configured voice settings. Open audio contexts now close when the service replaces its connection, and the rest of the turn continues under a new context ID.
(PR #6008) -
Fixed
ResembleAITTSServicefreezing the whole bot when the server closed the connection cleanly. It now reconnects through the standard websocket reconnect handling, with backoff.
(PR #6008) -
Fixed TTS services going silent for the rest of the session when a provider-specific cleanup step failed, for example
RimeTTSServicesendingclearwhile reconnecting. A failingon_audio_context_completedoron_audio_context_interruptedoverride is now logged as a warning, and audio playback continues.
(PR #6008) -
Fixed
SonioxTTSServicerejecting text with "Stream not found" when the stream's config hadn't reached the current connection, such as after a reconnect. The config is now sent before a stream's first text.
(PR #6008) -
Added missing Indian language locale members to
Languageand Sarvam STT mappings. Base language values resolve to Sarvam locale codes, andSarvamRealtimeSTTServicesends alanguagestring that is not aLanguagevalue to Sarvam as-is.
(PR #6010) -
Fixed websocket TTS services losing their keepalive partway through a session. In
AsyncAITTSService,ElevenLabsTTSService,ElevenLabsDialogueTTSService,InworldTTSService,NeuphonicTTSService,SarvamTTSService,SmallestTTSServiceandSonioxTTSService, a single failed keepalive stopped all later ones, so the provider closed the connection after every idle period and the service reconnected each time. A failed keepalive is now logged and the next one is sent on schedule.NeuphonicTTSServicealso no longer sends keepalives on a closed connection.
(PR #6011) -
Fixed STT services losing their keepalive after a single failed send.
NvidiaSTTService,SarvamSTTServiceandGeminiSTTService(when its keepalive is enabled) stopped sending keepalives for the rest of the session, so an idle connection could be closed by the provider. Websocket STT services such asCartesiaSTTService,GladiaSTTServiceandSonioxSTTServicewere affected until their next reconnect. A failed keepalive is now logged and the next one is sent on schedule.
(PR #6012) -
The Deepgram SageMaker services (
DeepgramSageMakerSTTService,DeepgramFluxSageMakerSTTService,DeepgramSageMakerTTSService,DeepgramFluxSageMakerTTSService) now handle a throttled or failed session start:SageMakerBidiClient.start_session()retries throttling and transient server failures with jittered backoff (max_connect_attempts, default 4), and raisesSageMakerBidiSessionErrorcarrying the AWS error id and HTTP status.- The error is classified (
RATE_LIMIT,SERVER,CONNECTIVITYorINVALID_REQUEST) instead ofUNKNOWN. - A service that cannot start its session and has no way to reconnect is marked unusable, so failover and
ProcessorUnusablePolicyact on it instead of the service dropping audio for the rest of the session.
(PR #6019)
-
NvidiaSageMakerSTTServiceno longer drops all audio for the rest of a session after its SageMaker session fails to start. The failure is reported as an error and the service is marked unusable, so failover andProcessorUnusablePolicycan act on it.NvidiaSageMakerSTTServiceandNvidiaSageMakerTTSServiceclassify SageMaker session failures (RATE_LIMIT,SERVER,CONNECTIVITYorINVALID_REQUEST), so rejected credentials are reported asCONNECTIVITYand a throttled start asRATE_LIMIT.NvidiaSageMakerTTSServicereports a failed session start as an error and stays usable, starting a new session on the next turn.
(PR #6020)
-
Fixed
OpenAIResponsesLLMServiceandOpenAIResponsesHttpLLMServicefailing every request togpt-6.1-solwhenreasoningisn't configured. They sentreasoning.effort="none", which the model rejects; they now leave it at the model's default effort.
(PR #6023) -
Fixed
DailyTransportleaving a function call waiting forever when it requests an image from a participant or video source that isn't being captured. The request is now answered with an error result, so the LLM can tell the user.
(PR #6027) -
Fixed bots generated by
pipecat initwith video input never receiving the user's video on Daily or SmallWebRTC. They now capture the camera at 1 frame per second throughvideo_in_sources, with the screen share listed commented out to enable when the bot should receive it.
(PR #6027) -
Fixed
SmallWebRTCTransportnever delivering frames from a screen share of a high-resolution, still screen, which left image requests for it unanswered. Its keyframes were larger than aiortc's video receive buffer could hold, so each was dropped and the next one was as large. The screen share receiver now holds frames of about 600 KB, and the camera receiver keeps aiortc's default.
(PR #6027) -
Fixed
SmallWebRTCTransportnever receiving the user's screen share. The bot's peer connection now bundles every transceiver on one transport; with aiortc's default bundle policy, the screen share's transceiver was left off the bundled transport, so its packets were routed to the camera receiver.
(PR #6027) -
Fixed
DailyTransportpassing on roughly twice the requested video framerate for rates between 5 and 30 frames per second. Frames now follow a fixed schedule that delivers the requested rate.
(PR #6027) -
Fixed
OpenAIResponsesLLMServiceandOpenAIResponsesHttpLLMServicefailing every request togpt-5,gpt-5-mini,gpt-5-nanoand the-promodels whenreasoningisn't configured. They sentreasoning.effort="none", which those models reject.gpt-5,gpt-5-miniandgpt-5-nanonow get"minimal", their lowest effort, and the-promodels are left at the provider default.
(PR #6029) -
Fixed
SarvamRealtimeSTTServiceending transcription for the rest of the session when its websocket dropped. It now reconnects with backoff (reconnect_on_error, defaultTrue), closes the utterance in progress, resendsspeech_startfor an open manual-endpointing turn, and countsspeech_end_audio_position_sfrom the start of the new session.
(PR #6031) -
Fixed websocket STT services staying usable but transcribing nothing for the rest of the session when their first connect failed. A failed connect is now retried like a dropped connection, with backoff, and reported as permanent once the attempts run out, so a
ServiceSwitchercan fail over. This applies toAssemblyAISTTService,AWSTranscribeSTTService,GladiaSTTService,MetaSTTService,OpenAIRealtimeSTTService,SarvamRealtimeSTTService,SonioxSTTService,TogetherSTTServiceandXAISTTService.
(PR #6032) -
Fixed
LiveKitTransportmixing the audio of participants who speak at the same time: each participant now has their own input resampler, released when their audio track is unsubscribed.
(PR #6033) -
GradiumSTTServiceno longer carries a dropped connection's text and turn state into the reconnected one. Text from an utterance that had already ended was prefixed to the next transcript, and a drop while a turn was ending could stop server turn detection (enable_turn_detection=True) from opening any further turns. The text received so far is now pushed as the transcript when the connection drops, and a turn still in progress stays open.
(PR #6039) -
GradiumSTTServicenow retries when its first connection fails, with the same backoff as a dropped connection. A failed first connect used to leave the service without a connection, and without transcripts, for the rest of the session.
(PR #6039) -
GradiumSTTServicenow transcribes audio at input sample rates other than 8, 16 or 24 kHz. At any other rate it declared 16 kHz while sending audio at the real rate, so Gradium decoded it at the wrong speed; such audio is now resampled to a rate Gradium accepts.
(PR #6039) -
CartesiaSTTService,ElevenLabsRealtimeSTTServiceandSmallestSTTServiceno longer stop transcribing after a dropped connection is reconnected. Audio arriving during the reconnect could open a second connection alongside the automatic one, leaving the service reading one socket while sending audio to the other, so no transcripts arrived until the provider closed the idle socket. The automatic reconnect is now the only one, audio arriving while the connection is down is dropped, and a failed first connection is retried with the same backoff.
(PR #6048) -
Fixed
get_transport_client_id()returning an empty string forLiveKitTransport, so bots that passed the client id to their tools sent requests for no participant. It returns the participant's identity.
(PR #6055) -
Fixed a LiveKit user's screen share replacing their camera video, and stopping the share leaving the bot with no video.
LiveKitTransportreceives the camera and screen share side by side, and eachUserImageRawFramesays which one it came from (transport_sourceis"camera"or"screenVideo").
(PR #6055) -
Fixed image requests on
DailyTransportandSmallWebRTCTransportthat no video frame would answer leaving the function call that made them waiting forever, for example afetch_user_imagecall while the user's camera is off. A request for a video source the user isn't sending, and any request still waiting when the user leaves or the transport disconnects, now completes with an error result the LLM can act on. On Daily, so does a request still waiting when the user turns that source off.LiveKitTransportbehaves like Daily.
(PR #6055) -
Fixed
LiveKitTransportnot raisingon_participant_connectedandon_client_connectedfor participants already in the room when the bot connects, so a bot that set itself up inon_client_connectednever did when the user joined first. Participants already in the room now raise the same events as those who join later.
(PR #6055) -
Fixed the
DeprecationWarningfor a custom user turn start or stop strategy that overrides the deprecatedreset()never being shown under Python's default warning filters. It now appears when the strategy class is defined, pointing at that class's definition.
(PR #6058) -
Fixed
SmallWebRTCTransportleaving an image request waiting until the peer disconnected when it was still waiting as the peer turned their camera or screen share off, for example afetch_user_imagecall made just as the user turned their camera off. The request now completes with an error result the LLM can act on, and the source is sampled again when it's turned back on.
(PR #6063) -
A finished session's pipeline is no longer kept in memory for up to
turn_end_timeout_secs(2.5 s by default) after it ends.TurnTrackingObserver, whichPipelineWorkeradds by default (enable_turn_tracking=True), could leave its turn-end timer scheduled when the pipeline was torn down before the observer saw the finalEndFrameorCancelFrame, and the timer's callback held the pipeline until it fired. A garbage collection or memory release right after the session could not free it.
(PR #6070) -
Fixed RTVI
bot-outputmessages giving a segment differentsegment_ids when the TTS service doesn't report word timestamps. The"completed"message carried the ID of the spoken text instead of the segment's, so clients couldn't match it to the segment's"new"message.
(PR #6072) -
Fixed
RTVIObserverParams.skip_text_typesnot fully hiding text from the client when the TTS service reports word timestamps (e.g. Cartesia, ElevenLabs). The skipped text'sbot-outputmessage was dropped, but its words were still sent as the bot spoke them.
(PR #6072) -
Fixed observers receiving no frames when the first observer is added to a
WorkerObserveraftersetup().
(PR #6079) -
Fixed
TTSSpeakFramerequests withinterruptible=Falsebeing cancelled during synthesis or losing their generated audio and text during playback interruptions.
(PR #6081) -
Fixed an observer whose
cleanup()raises stopping aPipelineWorker's shutdown, so the remaining observers and the pipeline's processors were never cleaned up. The error is logged and shutdown continues.
(PR #6086) -
Fixed an observer that calls
PipelineWorker.remove_observer()from its own callback logging a warning and leaving its delivery task running for the rest of the process.
(PR #6086) -
Fixed
cleanup()on a processor, service, transport or observer never returning when called from one of that object's own event handlers.
(PR #6086) -
Fixed
PipelineWorker.remove_observer()not calling the removed observer'scleanup(), so resources it held outlived it: aTurnTrackingObservercould still fireon_turn_endedafter removal, and anRTVIObserverwith system logs enabled stayed attached to the logger.
(PR #6086) -
Fixed a worker's
@jobhandlers running on after the worker stopped. A handler still running when its worker stopped, such as one holding a long-lived job open, was left as a live task: adangling tasks detectedwarning at shutdown, and in a long-lived host that adds and removes workers, a leaked task for each.BaseWorker.stop()now cancels them, after answering their jobs as cancelled.
(PR #6087) -
Fixed
SonioxSTTServiceandSonioxTTSServicesendingnbforLanguage.NBandLanguage.NB_NO, which Soniox does not accept. Both now map to Soniox'sno.
(PR #6094) -
Fixed
SonioxSTTServiceignoring thelanguagesetting, at construction and in a settings update (andset_language): it connected withlanguage_hintsalone. Alanguagegiven withoutlanguage_hintsnow becomes the single language hint.
(PR #6095) -
LLMContext.add_image_frame_message()andLLMContext.create_image_message()now encode RGBA frames, which raisedOSError: cannot write mode RGBA as JPEG.
(PR #6101) -
Fixed
AnthropicLLMServicerequests to Claude Sonnet 5.5 failing with a 400 error when neitherthinkingnoreffortis configured. The service now sendsthinkingbetween_tools, the model's lowest thinking setting.
(PR #6108) -
The eval harness ends a run at once when the bot closes the connection or refuses it before sending
bot-ready, instead of waiting out the bot-ready timeout. A close is reported as the newclosed_before_readyfailure kind, with the close code and reason (for example Pipecat Cloud's1011 Concurrency limit reached), and a refused connection, such as an HTTP 403, asconnect_failed.
(PR #6117) -
Fixed TTS services whose audio doesn't carry its request's context ID, such as
FishAudioTTSService, sometimes playing a request's audio as part of the request before it.
(PR #6118) -
Fixed a TTS service playing no more audio after an interruption when an uninterruptible request had already stopped because of a provider error.
(PR #6118) -
Fixed TTS services with word timestamps, when created with
reuse_context_id_within_turn=False, ending the assistant message after a reply's first sentence: the rest of the reply was added to the next assistant message. The reply'sLLMFullResponseEndFramenow comes after its last word.
(PR #6118) -
Fixed the last words of a
TTSSpeakFramegoing missing from the context on TTS services with word timestamps, when the provider didn't time those words and the audio context ended by timing out.
(PR #6118) -
Fixed a
TTSSpeakFramespoken after an LLM reply not committing its text to the context, on TTS services without word timestamps.
(PR #6118) -
Fixed TTS services with word timestamps dropping the
LLMFullResponseEndFrameof a reply that spoke no timed words, such as a reply made only of text inskip_text_types.
(PR #6118) -
Fixed
DeepgramSageMakerTTSServiceending a reply's audio contextstop_frame_timeout_safter its last audio, which delayed itsTTSStoppedFrame, instead of when Deepgram reports the flush.
(PR #6119) -
Fixed
NvidiaSageMakerTTSServicereporting each request as completed with no audio, and becoming unusable after three, because its audio skipped the request's audio context.
(PR #6120) -
CartesiaTurnsSTTSettingsnow documents Cartesia's current turn detection ranges and defaults:turn_eager_end_thresholdaccepts 0.3–0.8 and defaults to 0.6,turn_end_thresholddefaults to 0.3, and the three thresholds must stay in descending order.
(PR #6123) -
Fixed
AzureTTSServiceandAzureHttpTTSServicelogging an error about the uninitializedeffectsetting every time the pipeline started.
(PR #6126) -
Fixed an issue where ElevenLabs TTS services dropped the
language_codewhen usingeleven_v4, which accepts the same languages aseleven_v4_turbo.
(PR #6133) -
Fixed the
examples/update-settings/stt/stt-fal.pysettings update, which passed a fieldFalSTTService.Settingsdoes not define and raised aTypeErrormid-call. It now switcheslanguage, like the other STT settings examples.
(PR #6134) -
Fixed an error logged on every
OpenRouterLLMServicestart reporting that theprovidersetting was left uninitialized.
(PR #6137) -
NeuphonicTTSServiceandNeuphonicHttpTTSServicenow sendhiforLanguage.HI, matching the language code Neuphonic documents and returns for its Hindi voices.
(PR #6139) -
Fixed the eval harness judging, as part of the bot's reply, what the bot was still saying after the user's send talked over it and before the bot noticed.
(PR #6146) -
Fixed a scripted eval turn without user input judging the previous reply's last sentence as its response, when the bot had stopped speaking but that sentence was still being transcribed.
(PR #6146) -
Fixed a scripted eval turn with no
user:ignoring what the bot said without its LLM, such as a presence check pushed as aTTSSpeakFrame, when the turn first had to wait for the bot's previous reply to end.
(PR #6164) -
Fixed DTMF on Daily SIP dial-in calls.
OutputDTMFFramewithout an explicitsession_idnow falls back to the active dial-in session when there is no dial-out session, instead of silently sending nothing.
(PR #6165) -
Fixed
PipelineWorker.cancel()not taking effect until every processor finished setting up (up tosetup_timeout_secs), e.g. when a caller hung up while a service was still connecting.
(PR #6167) -
Fixed audio-mode eval simulations ending as soon as the persona called
end_callwhen it said goodbye in the same reply: the persona's last line was left out of the conversation the judge reads, and the run didn't wait for the bot's reply to it, so a flow's closing tool calls, such as anend_conversation, were never made. The persona'send_callis now reported after its last line has been spoken.
(PR #6180) -
Fixed audio-mode eval simulations leaving out what the persona said before the bot's speech cut it off: those words are now a persona turn in the conversation the judge reads.
(PR #6180) -
Fixed a
DailyTransportbot that keeps running after its call ends withoutleave()(the bot is ejected, or the room or meeting ends) raising thousands ofErrorFrames per second until the process is killed. A failed app message raised anErrorFrame, whichRTVIProcessor(added by default withenable_rtvi=True) turned into another app message. The transport now reports the disconnect once, throughon_error, and drops app messages until it joins again.
(PR #6186) -
Fixed
AssemblyAISTTServiceending the pipeline when AssemblyAI confirmed a session termination. That confirmation arrives whenever the service disconnects, including when anSTTUpdateSettingsFramechanges a connection-time setting and the service reconnects mid-call. The service no longer pushes anEndFrameon Termination.
(PR #6192) -
FilterEnableFramenow enables or disables the input transport'saudio_in_filter. Previously the input transport forwarded onlyFilterUpdateSettingsFrameto its filter, so aFilterEnableFramepassed downstream and toggled the output transport's filter instead.
(PR #6194) -
GeminiLiveLLMServicenow sends aLanguagevalue set throughsettings=orLLMUpdateSettingsFrameas Gemini's locale code, soLanguage.ESis sent as"es-ES"instead of"es". A string is still sent as given.
(PR #6194) -
Sending a DTMF frame on
DailyTransport(DailyOutputDTMFFrame,DailyOutputDTMFUrgentFrameorOutputDTMFFrame) now pushes a non-fatalErrorFramewhen the send fails, such as on a call with no dial-in or dial-out session. Previously the failure was silently dropped.
(PR #6194) -
DailySIPReferFrameandDailyTransport.sip_refer()now use the active dial-out session, then the active dial-in session, when the settings have nosessionId, matching SIP call transfer and DTMF. With no session, the REFER fails with an error.
(PR #6194) -
RimeTTSService.INLINE_SPEED()now sends one speed per bracketed span ininlineSpeedAlpha. Previously the list started with an empty value (",0.5,1.5"), so Rime received one more speed than there were spans.
(PR #6195) -
DailyTransportnow accepts handlers foron_transcription_stoppedandon_transcription_error. Previously both events were raised but never registered, so adding a handler only logged "event handler ... not registered" and the handler never ran.
(PR #6195) -
Fixed
OpenAIRealtimeLLMServicesending user audio at the pipeline's input sample rate while the session expects 24 kHz, so the model heard speech at the wrong speed (1.5x fast at the default 16 kHz). Input audio is now resampled to 24 kHz before it is appended, and the manual turn-detection pre-roll replayed after an interruption holds 24 kHz audio too.
(PR #6196) -
AnthropicLLMServicenow logs a warning naming Claude'sstop_reasonwhen a response ends for a notable reason, for both streamed responses andrun_inference(): refused by a safety classifier (with the refusal category), or truncated atmax_tokensor the context window. Previously these ended the turn, or the one-shot inference, with little or no text and no indication why. Whatever text did arrive is still passed downstream or returned, and responses ending normally are unaffected.
(PR #6198) -
Fixed
SileroVADAnalyzersometimes reporting that the user stopped speaking partway through a word, which could cut off the end of a transcript or close the turn on incomplete audio. The analyzer reset the model's state every five seconds of wall-clock time, and a reset during speech could drop the model's confidence below the threshold. Resets now happen only during clear non-speech and are timed by the audio processed, so they never land on speech and the result no longer depends on wall-clock timing.
(PR #6202) -
GoogleLLMService.run_inference()now logs a warning naming Gemini'sfinish_reasonwhen a reply ends for a notable reason, as streamed responses already do. Previously a one-shot inference that was withheld for safety or cut off at the output token limit returnedNoneor partial text with no indication why, which affected features built on it such as context summarization and the LLM classifier. Partial text is still returned.
(PR #6205) -
CartesiaTurnsSTTServicegives up on a connection whoseconnectedmessage doesn't arrive within 10 seconds, instead of waiting indefinitely and holding back incoming audio.
(PR #6210) -
DeepgramFluxSTTServiceandCartesiaTurnsSTTServiceretry a failed first connection when audio arrives, instead of transcribing nothing for the rest of the session. If every attempt fails, the service reports a permanent error, so aServiceSwitcherusingServiceSwitcherStrategyFailoverswitches to its next service.
(PR #6210) -
Fixed
DailyTransportsending app messages while its signalling connection to Daily is interrupted, where every send failed and anRTVIProcessorcould turn each failure into another error message until signalling reconnected. The transport now logs a warning and drops app messages until signalling reconnects, and a failed app message is reported once until a send succeeds.
(PR #6211) -
AWSBedrockLLMService,AWSPollyTTSService,AWSAgentCoreProcessorandAWSTranscribeSTTServicekeep working past the expiry of temporary credentials from the default AWS chain (IRSA, EKS Pod Identity, ECS task roles, EC2 instance profiles, SSO). They used to copy those credentials once at construction and fail with expired-token errors once the copy expired; the AWS session now renews them, and Transcribe signs each connection with current keys.
(PR #6212) -
FishAudioTTSServicekeeps audio chunks of 1024 bytes or less, which it used to drop, clipping the end of utterances.
(PR #6214) -
WhisperSTTServiceMLXaccepts a runtime language change instead of raisingAttributeError.
(PR #6214) -
DeepgramSageMakerSTTServicesends list settings such askeyterm,keywords,redact,replaceandsearchas one query parameter per item, and URL-encodes every value. Lists used to be sent as their Python representation, and values with spaces,&or:broke the query string.
(PR #6214) -
SmallWebRTCTransport's output processor is named withoutput_name. It used to takeinput_name, so both processors appeared under one name in logs, metrics and observers.
(PR #6214) -
RimeTTSService.PRONOUNCE()wraps the phoneme in curly braces, so Rime reads it as a custom pronunciation instead of speaking it as text.
(PR #6214) -
InworldRealtimeLLMServiceapplies a runtimetemperatureupdate to the live session. It used to be stored but never sent.
(PR #6214) -
MistralLLMServicesendsseed=0asrandom_seed. A seed of 0 used to be dropped.
(PR #6214) -
GoogleHttpTTSServiceescapes text placed inside its SSML, so&,<and>in LLM output no longer make requests fail for voices that use SSML (Neural2, Wavenet, Standard).
(PR #6214) -
OpenAIResponsesHttpLLMServiceandOpenAIResponsesLLMServicenow report token usage for responses that end incomplete (e.g. at the output token limit) or failed, which are billed but previously reported no usage. The WebSocket service also reports the actual reason fromresponse.error/response.incomplete_detailsinstead of a generic message.
(PR #6215) -
Fixed
pipecat-aibuilt from a source tree without git metadata, such as a GitHub "Source code" release archive, leaving out its data files: the DTMF tones, the Silero VAD and Smart Turn ONNX models, the Nova Sonicready.wav, the flow config schema andframes.proto. Wheels and sdists published to PyPI were not affected.
(PR #6223) -
Fixed
pipecat eval suiterunning a bot's later manifest entry early, right after the bot's first entry. Each entry's runs now start in their place in manifest order, so the live dashboard no longer jumps to the bottom of the list and back.
(PR #6225) -
Fixed scripted
pipecat evalscenarios waiting out a turn's whole timeout after the bot ended the call, for example when its LLM rejected the API key. The turn now fails right away with the newbot_endedfailure kind, after anything the bot said last has been transcribed. Anabsent:expectation passes when the bot ends the call, since the event can no longer arrive.
(PR #6225) -
Fixed
RTVIObserversending thebot-outputof sentences an interruption cut off before their audio started together with the bot's next speech, and holding a following text-only reply behind them. An interruption now drops that queued text, except for uninterruptible speech, whose audio still plays.
(PR #6227) -
Fixed a reply that skips TTS, such as one to text sent with
audio_response: false, never reaching clients asbot-output, so clients that show the bot's text frombot-outputdidn't show it. The TTS service now cuts the LLM's text into sentences and pushes them as unspoken output, whichRTVIObserversends asbot-output, one message per sentence, withwill_be_spoken: falseandspoken_status: "new". Processors after the TTS service get theseAggregatedTextFrames for such a reply instead of itsLLMTextFrames.
(PR #6227) -
Fixed
RTVIObserverholdingbot-outputfor text the TTS service left out of speech until the bot next started speaking, so a text-only reply, such as one from a bot with anLLMTextProcessor, could wait for the next spoken reply or never go out. The TTS service marks the text it leaves out withskip_tts, and the observer sends that text right away, unless text queued before it is still waiting for the bot to speak. Other text, such as a speech-to-speech service's transcript, still waits for the bot to start speaking.
(PR #6227) -
Fixed
RTVIObservernever sending spokenbot-outputwithbot_speaking_enabled=False, since it only followed when the bot started and stopped speaking while sending those messages.bot_speaking_enablednow only decides whetherbot-started-speaking,bot-stopped-speakingandbot-interruptedare sent.
(PR #6227) -
Fixed a scripted
pipecat evalturn whosesend_afterwaits for a bot event waiting its whole 30 seconds after the bot ended the call and then failing withsend_after_timeout. It now fails right away with thebot_endedfailure kind.
(PR #6230)
Performance
-
AnthropicLLMServicewithenable_prompt_cachingnow adds a cache breakpoint to the system prompt, so tools and system instructions are cached once and reused across conversations.
(PR #5887) -
LLM services no longer build the context for their DEBUG log line on every inference when DEBUG logging is disabled or
PIPECAT_LOG_LLM_CONTEXTisoff.
(PR #6047)
Security
-
Error messages no longer carry the credentials they quote.
ErrorFramemasks any API key, token, signed URL signature, auth header or URL password in itserroras[REDACTED]and keeps the rest of the message, so the ERROR log line, observers such asErrorObserver,on_errorhandlers and RTVI error messages all get the masked text. Previously, for example, a rejected Sarvam STT connection logged the API key (the Sarvam SDK's error prints the request headers), and a file URL the LLM couldn't fetch was logged with its signature.
(PR #6178) -
Services for OpenAI-compatible providers no longer send
OPENAI_API_KEYto their provider when given noapi_key. The OpenAI client they build on falls back to that variable when given no key, so an application that set it for OpenAI handed its OpenAI key to the other provider, including when anapi_key=os.getenv(...)read an unset variable. AffectsBasetenLLMService,CerebrasLLMService,CrusoeLLMService,DeepSeekLLMService,FireworksLLMService,GrokLLMService,GroqLLMService,GroqSTTService,InceptionLLMService,MistralLLMService,NebiusLLMService,NovitaLLMService,NvidiaLLMService,OpenRouterLLMService,PerplexityLLMService,QwenLLMService,SambaNovaLLMService,SarvamLLMServiceandTogetherLLMService.
(PR #6193)
Other
-
Added
examples/image-generation/bots for fal, Google, and OpenAI, and agenerate_imagerelease-eval scenario that checks each one produces an image.
(PR #5956) -
TTSServiceaudio context playback moved intoAudioContextManager, and word timestamp tracking intoWordTimestampTracker, both inpipecat.utils.context.
(PR #6118)