Added
-
Added
LatencyBreakdown.contributions, a timeline of the user-to-bot interval whose durations sum to the measured latency. It names the time no service reports — VAD silence, turn detection, turn-completion markers and holds, sentence aggregation, and function handlers — and attributes time a setting governs to that setting rather than to the service it runs inside.The first thing the bot says is measured too, from where
StartupTimingObserverstops — the pipeline ready with a client on it — so the two reports meet rather than overlap. Waiting to be asked is usually most of that wait, and no service reports it.Each part carries a stable
keyand anowner_kind— service, setting, bot or pipeline — so data keyed on them survives a label being reworded, and the breakdown says what it wasmeasured_fromalongside thetotal_secsit measured. Print it withLatencyBreakdown.turn_contribution_lines();UserBotLatencyObserver(min_contribution_secs=...)sets how brief a part must be before it rolls into a singlepipelineentry.``` 0.200s endpointing wait [config: VAD stop_secs] 0.125s transcription [DeepgramSTTService#0] 0.336s LLM inference [OpenAILLMService#0] 0.024s turn completion [config: filter_incomplete_user_turns] 0.359s speech synthesis [CartesiaTTSService#0] 1.044s TOTAL ```(PR #5445)
-
Eval scenario turns can play an audio file as the user instead of synthesizing text. A turn's
audio:names a recording, resolved relative to the scenario file, in any formatsoundfilereads (WAV, MP3, FLAC, OGG, ...); multi-channel audio is downmixed to mono and the file keeps its own sample rate.user:is required alongside it and is what the recording says, so the judge andtext_containsstill have the turn's input. A scenario whose audio turns all name a file needs nouser.speech:block.
(PR #5473) -
Added a
cancellable_by_llmargument toLLMSwitcher.register_function(), which forwards it to every LLM the switcher fronts. Tools registered through a switcher can now opt into LLM-side cancellation, matching whatLLMService.register_function()accepts.
(PR #5474) -
Added
AssemblyAISyncSTTService, a segmented speech-to-text service backed by AssemblyAI's Sync API: pipeline VAD segments the audio and each segment (up to 120 seconds) is transcribed in one HTTP request, with no streaming session to manage. It takes anaiohttp_session,base_urlselects a data-residency endpoint, andSettingssupportsmodel,language,prompt,keyterms_prompt, andconversation_context.Recent user and agent turns are sent automatically as
conversation_contexton each request, bounded bymax_context_turns(0disables) andmax_context_chars; settingconversation_contextyourself replaces that buffer. The connection is pre-warmed when the user starts speaking so the transcription request skips the handshake (enable_prewarming).
(PR #5488) -
AzureSTTService.Settingsgainedsegmentation_silence_timeout_ms, which sets how much silence (100–5000 ms) Azure allows inside a phrase before it emits a final transcript. Azure's default of 500 ms applies when it's unset.
(PR #5511) -
Added
on_progressandon_updateevent handlers to the eval framework.EvalSessionandEvalSuiteare nowBaseObjects, so eval progress is observed the same way as every other Pipecat event.EvalSuite'son_updatehandlers run synchronously and should return promptly, since anEvalRunis mutated in place as it executes;EvalSession'son_progresshandlers run as tasks, andEvalSession.run()waits for them before it returns.@session.event_handler("on_progress") async def on_progress(session, progress): print(progress.event_name, progress.status)(PR #5520)
-
Added a
voice_parameterssetting toAzureTTSServiceandAzureHttpTTSService, which passes SSML'sparametersattribute on<voice>through to Azure so HD voices can be tuned (temperature,top_p,top_k,cfg_scale,enhancePronunciation).
(PR #5522) -
Added
profanity_filterandredacttoDeepgramFluxSTTService.SettingsandDeepgramFluxSageMakerSTTService.Settings, exposing Flux's profanity masking and number redaction.
(PR #5523) -
Added a
versionfield toDeepgramSTTService.Settings(andDeepgramSageMakerSTTService.Settings), pinning transcription to a specific Deepgram model version instead of whichever onelatestcurrently resolves to. Previously reachable only as an untypedextrakey, which continues to work.
(PR #5527) -
Added
thinkingtoDeepSeekLLMService.Settings, which controls DeepSeek's thinking mode:settings=DeepSeekLLMService.Settings(thinking={"type": "disabled"})turns off the reasoning pass that V4 models run by default.
(PR #5528) -
CartesiaTurnsSTTSettingsgainedturn_start_threshold,turn_eager_end_threshold,turn_end_thresholdandturn_end_timeout_ms, Cartesia's turn detection tuning parameters. Unset by default, so the server's own defaults apply.
(PR #5533) -
Added a
denoiser_configsetting toGoogleSTTService, which passes Google's Chirp 3 background-noise removal (denoise_audio,snr_threshold) through to the recognition request.
(PR #5536) -
Added a
temperaturefield toHumeTTSService.Settings, passing Hume's sampling temperature through to the synthesis request. Higher values increase variation, lower values increase consistency; when unset, Hume applies its own per-model default.
(PR #5540) -
Added a
speedsetting toDeepgramTTSServiceandDeepgramHttpTTSService, passing Deepgram's Aura speech-rate multiplier (0.7 to 1.5) through to/v1/speak.
(PR #5552) -
Added a
no_verbatimsetting toElevenLabsSTTServiceandElevenLabsRealtimeSTTService, which asks Scribe to drop filler words, false starts and non-speech sounds from the transcript.
(PR #5555) -
Added a
hotwordsfield toWhisperSTTService.Settings, which biases Faster Whisper's transcription towards the words or phrases it names, such as product names or domain jargon.
(PR #5558) -
Added a
reduce_silencesetting toSonioxTTSService, which shortens the pauses between words on models that support silence reduction.
(PR #5567) -
Added an
include_resultssetting toSpeechmaticsSTTService, which asks Speechmatics to include word-level results in transcript messages.
(PR #5568) -
WebsocketServicenow acceptsreconnect_backoff_min_waitandreconnect_backoff_max_waitto configure the wait between reconnection attempts. Defaults (4s / 10s) preserve the previous hardcoded behavior.
(PR #5586) -
Added
MetaSTTService, a streaming speech-to-text service using Meta'smuse-voice-transcribe-1.0model over the Muse Voice realtime API. Install it withpipecat-ai[meta], or pick Meta when scaffolding withpipecat init.MetaSTTService.Settingstakeslanguageorlanguage_biasto bias recognition across the 25 supported languages,keywordsfor domain-specific terms, andmodeto select the model's own endpointing, client-delimited turns, or diarization.
(PR #5587) -
Added
universal-3-6-proas a supportedAssemblyAISTTServicemodel. It isuniversal-3-5-proupgraded — the same model with the same feature set — and is recognized as part of the Universal-3 Pro family, so everyu3-rt-profeature (built-in turn detection, prompting, continuous partials,interruption_delay, context carryover, and voice focus) applies to it as well.universal-3-5-proremains supported and stays the default.
(PR #5598) -
StartupTimingReport.warmupreports what warming Pipecat's deferred imports cost startup, which no processor accounts for and which is often the largest single contributor to a cascaded bot's start.warmup.duration_secsis how long warming took andwarmup.blocking_duration_secsis what it added beyond the slowest processor — zero for a pipeline whose services take longer to connect than warming takes to load. Observers can also handle the newBaseObserver.on_startup_warmup()event directly.
(PR #5600) -
StartupTimingReportnow accounts for the whole of a pipeline's startup, so a slow start can be read off the report rather than inferred.setup_phase_secsandstart_phase_secssplit the span into the two phases that compose differently: processors are set up concurrently, so the longest single piece of work decides that phase, while theStartFramereaches them one at a time, so what each spends on it adds up.ProcessorStartupTiming.start_duration_secsreports the latter per processor alongside the existingsetup_duration_secs.
(PR #5600) -
Added
language_codesandtimestampssettings toAssemblyAISyncSTTService, aligning it with the Sync API's config surface.language_codes(e.g.[Language.EN, Language.ES]) declares the audio's languages for multilingual or code-switching audio, and is bound in preference to the singlelanguagewhen both are set; regional variants resolve to their base code and duplicates are dropped, preserving declaration order.timestampsrequests per-wordstart/endtimings on the transcription result, and is unset by default so the API default ofFalseapplies.
(PR #5603) -
Added
ServiceMetricsObserver, which reports each metric a service publishes as a record rather than a log line.on_service_latencycarries time to first byte, first audio and first answer token, with the parts each decomposes into;on_service_usagecarries speech-to-text audio seconds, text-to-speech characters, and the token counts an LLM reports. One record per metric, never summed, so a consumer groups them by turn, session or model.
(PR #5607) -
Added
SpeakingObserver, which reports a conversation's speaking lifecycle throughon_speech_event:user_speech_started/user_speech_stoppedas the detector heard them,user_turn_started/user_turn_stoppedas the turn strategy ruled on them,bot_speech_started/bot_speech_stopped, andinterruption. Speech is timed to where it began and ended rather than to where the detector confirmed it, and a moment that closes a stretch of speech names itsstarted_at, so an interval reads whole from one record.
(PR #5612) -
Added
ErrorObserver, which reports every error a pipeline raises throughon_erroras anErrorEvent: themessage, thecategoryit was attributed to, theexception_typebehind it, theprocessorthat raised it, and whether that processor can still do its job (processor_usable). Errors are read where they are raised, so ones a processor answers for itself — aServiceSwitcherfailing over, or a service it holds in reserve — are reported too;PipelineWorker'son_pipeline_errorsees only the errors that reach the top of the pipeline.
(PR #5618) -
Added
FunctionCallObserver, which reports each function call throughon_function_call_eventas aFunctionCallEvent:function_call_startedwhen the LLM asks for it,function_call_in_progresswhen it begins running, and one offunction_call_completed,function_call_failed,function_call_timed_outorfunction_call_cancelledwhen it settles. Each moment names the one before it, so the wait to run and the time spent running each read from one record. Arguments travel by default and results do not;include_argumentsandinclude_resultschange either.
(PR #5622) -
DeepgramFluxSTTService,DeepgramFluxSageMakerSTTServiceandCartesiaTurnsSTTServicepush anEagerTranscriptionFramewhen they predict an end of turn and anEagerEndOfTurnCancelFramewhen they withdraw it, whichEagerUserTurnStrategiesacts on. Both frames are emitted only whileenable_eager_end_of_turnis on.
(PR #5625) -
Added support for answering an eager end of turn: a response is generated from a service's predicted end of turn and held until the turn is confirmed, so the gap before the committed end of turn is spent generating rather than waiting. Turn it on with
enable_eager_end_of_turn=TrueonDeepgramFluxSTTService,DeepgramFluxSageMakerSTTServiceorCartesiaTurnsSTTService, which then recommendEagerUserTurnStrategiesin place ofExternalUserTurnStrategies. The LLM service holds the response, so no extra processor is needed. The response is discarded if the user resumes speaking or if the committed transcript differs from the predicted one, per the strategy'smatch_policy(NormalizedMatchby default, which ignores the capitalization and punctuation services commonly add when committing a transcript, orExactMatchto require the two to be identical). An unconfirmed turn never reaches the user or the LLM context, and tool calls are never executed for one.
(PR #5625) -
Added
FlowConfig, which describes a Pipecat Flows conversation as data so a bot can load its flow at runtime. A config names the initial node and, for each node, its messages, the tools it offers, and where each tool leads. Load it withFlowConfig.from_file(),from_yaml(), orfrom_json(), construct aFlowfrom it and the module holding your direct functions and action handlers, or a list of modules, and hand that toFlowManager.Tools stay in Python and return
(result, TRANSITION_IN_YAML); a function
that only moves the conversation is written in the config alone, as a
transition_onlyentry with adescription. The config picks the next node,
by name or with a branch table keyed on a field of the result, where case
keys may be strings, booleans, or numbers. Constructing theFlowreports
every tool, handler, or variable the config names but cannot resolve,
together, as aFlowReferenceError. Actions use the built-intts_say,
end_conversation, andfunctiontypes, or a custom type registered in code
or given ahandlername in the config. Prompts may use{{ key }}
placeholders, whichFlowManagerfills from itsstateon entering the
node, and a YAML config can keep long prompts in their own files with
!include path, resolved relative to the config. The format's JSON Schema is
kept in the repository atsrc/pipecat/flows/flow_config.schema.jsonfor
editors and other tools to vendor.initial_node: greet nodes: greet: role_message: You take food orders for {{ restaurant_name }}. task_messages: - role: developer content: Greet the caller and ask: pizza or sushi? functions: - name: choose_pizza transition_only: true description: The caller wants pizza. transition_to: pizza pizza: task_messages: - role: developer content: Take the pizza order. functions: - name: select_pizza_order transition_to: field: status cases: ok: confirm unavailable: pizza
import handlers # direct functions: select_pizza_order, ...; action handlers config = FlowConfig.from_file("flow.yaml") flow = Flow(config, handlers=handlers) flow_manager = FlowManager( worker=worker, llm=llm, context_aggregator=context_aggregator, global_functions=flow.global_functions, ) flow_manager.state["restaurant_name"] = "Luigi's" # fills {{ restaurant_name }} await flow_manager.initialize(flow.initial_node)
The flows examples are grouped by form:
examples/flows/yaml/holds a
folder per YAML flow withbot.py,flow.yaml, andhandlers.py
(hello_world, food_ordering, restaurant_reservation, patient_intake,
insurance_quote, and podcast_interview), andexamples/flows/python/holds
the flows that show what needs code. The README says when to write a flow in
each form.
(PR #5628) -
Added
pipecat.utils.yaml.include_loader(), which returns ayaml.SafeLoadersubclass that resolves!include <relative-path>tags against a base directory. Flow configs and eval scenarios use it; any YAML-based configuration can.
(PR #5628) -
A suite manifest lists scripted and simulated scenarios side by side under
scenarios:, andpipecat eval suite -k simulation(or-k script) runs only one kind.
(PR #5646) -
Simulations for
pipecat eval: give a scenario apersona:and agoal:
instead of scripted turns, and an LLM plays the caller, holding the whole
conversation with your bot, in text or over synthesized speech, and hanging
up once it has what it came for. A judge then reads the transcript, the bot's
tool calls in place, and decides whether the bot did its job (success:) and
how every reply scored on yourmetrics:, judged per turn (criterion, with
min_scoreas the share of turns that must pass) or measured from the run
(measure: turns,duration,words, orlatencyagainst a range, or
function_callsagainst the list of calls the bot should make,[]for
none).pipecat eval runplays one; a suite runs itruns:times and every
run must pass.persona: "Jamie, booking dinner for two tonight." goal: "Book a table for two at 6 PM, then end the call." success: "the bot confirmed a reservation for two at 6 PM" metrics: - name: politeness criterion: "the reply is courteous, never curt or dismissive" min_score: 1 - measure: latency max_value: 5 runs: 3
(PR #5646)
-
RTVIClientTransportandRTVIClientSerializerlet a Pipecat pipeline join a bot as an RTVI client. The eval harness now runs on them: its STT, TTS, persona, and recording are stages of a pipeline of its own, and each suite run gets its own process.
(PR #5646) -
Added
require_given()topipecat.utils.types. It narrows a settings value pastNOT_GIVENandNonelikeassert_given(), and also rejects the empty string, raisingValueError("<what> must be specified")for settings a service cannot run without.
(PR #5670) -
A simulation's results tell judge trouble from bot trouble: a turn the judge left unanswered carries
verdict: none, a failed metric carries afailure_kind(judge_no,judge_no_verdict,out_of_range,function_calls), and a judge that gives no verdict on the goal makes the run an error with kindjudge_no_verdict, kept out of the suite's pass rate. A failedlatencymeasure's reason says what was timed, the first token in text mode or the first spoken sentence in audio mode.
(PR #5678) -
A simulation ends as soon as it is going nowhere:
silencewhen neither side does anything formax_silence_s(30 s by default), and an error the moment the harness's own pipeline fails, the persona LLM first among them, instead of waiting outmax_duration_s.
(PR #5678) -
A simulation's
simulator:block is optional: without one the persona runs on the same local Ollama model as the default judge,gemma4:12b, so a simulation needs no API key. The release-eval simulations use it.
(PR #5678) -
Added
BackendLLMWorkerandBackendOutput(pipecat.workers.llm): a worker that runs any LLM service, with its own context and multi-step tool calling, as the backendOpenAILiveLLMServiceclient delegation hands work to. Everything the backend produces comes back as aBackendOutputsaying what it is and whether the user may hear it, andtransform_outputdecides that per output, or rewrites the text on its way out — seeexamples/realtime/realtime-openai-live-client-delegation-spoken-updates.py.
(PR #5688) -
Added
OpenAILiveLLMService, a speech-to-speech service for the OpenAI Live API (gpt-live-1). The live model is full-duplex — it listens and speaks at the same time and handles being interrupted itself — and delegates search, reasoning and tool use to a backend model while the conversation continues. Both delegation modes are supported:OpenAILiveLLMService.ResponsesDelegationlets OpenAI host the backend (Responses API) model, with its function calls executed by the pipeline's registered tool handlers;OpenAILiveLLMService.ClientDelegationruns any Pipecat LLM service as the backend through aBackendLLMWorker. Seeexamples/realtime/realtime-openai-live-responses-delegation.pyandexamples/realtime/realtime-openai-live-client-delegation.py, plusexamples/realtime/realtime-openai-live-client-delegation-spoken-updates.pyfor a backend that chooses what the user hears.
(PR #5688)
Changed
-
⚠️ Behavior change:
DeepgramSTTServiceandDeepgramSageMakerSTTServiceno longer sendprofanity_filter, so Deepgram's own default of off applies. Deepgram's filter rewrites the words it matches rather than tagging them, so a false positive silently corrupts a transcript that may be stored, analyzed, or sent downstream. To keep the previous behavior, passsettings=DeepgramSTTService.Settings(profanity_filter=True).
(PR #5462) -
CartesiaTTSServiceandCartesiaHttpTTSServicenow default tosonic-3.6, Cartesia's current Sonic model. Passmodel="sonic-3.5"to stay on the previous default.
(PR #5472) -
soundfileis now a required dependency instead of an optional extra, soSoundfileMixerand the eval harness can load audio files with no extra install step. Thepipecat-ai[soundfile]extra still resolves and is now a no-op, so existing installs keep working unchanged.
(PR #5473) -
text_containson auser_transcriptioneval expectation now accumulates the STT's final transcription segments within the turn before checking, so an STT that finalizes an utterance in pieces can still satisfy a phrase. Substring checks also ignore whitespace differences between the expected text and the event's text.
(PR #5473) -
Raised the
pipecat-ai[cli]Context Hub floor topipecat-ai-context-hub0.6.0, which registers its MCP server with Claude Code for every directory rather than only the oneinstallran in. A project scaffolded after the hub was set up now has the server without re-runninginstall.
(PR #5480) -
Bots scaffolded by
pipecat initbuild theirWorkerRunnerbefore wiring up event handlers, so handlers can end the session withawait runner.cancel(). The runner takeshandle_sigint=runner_args.handle_sigint, letting a bot run directly install its own signal handler while a hosted one leaves signals to its host.
(PR #5482) -
The AssemblyAI STT examples and docs now name
universal-3-5-pro, AssemblyAI's current Universal-3 Pro streaming model. Theu3-rt-pronames are legacy aliases AssemblyAI redirects to it and are still accepted byAssemblyAISTTService.
(PR #5507) -
AWSTranscribeSTTServicenow maps 46 more AWS Transcribe streaming languages, including Turkish, Hungarian, Tamil, Telugu, Swahili, Mexican Spanish and Welsh. Passing one of these as aLanguageenum previously sent a bare base code such astr, which AWS Transcribe rejects.
(PR #5509) -
AWSPollyTTSServicenow mapsLanguage.EN_IEandLanguage.EN_SGto Polly'sen-IEanden-SGlocales, so Irish and Singaporean English no longer log an unverified-language warning.
(PR #5510) -
Raised the
pipecat-ai[cli]Context Hub floor topipecat-ai-context-hub0.7.0, whoserefreshindexes the framework at its newest release tag rather than tracking its default branch. Hub answers stop mixing in unreleased APIs stamped with the previous release's number, whichversion_compatibilityreported as compatible to anyone running that release.Pass
--framework-version headto index the default branch — the right choice when developing Pipecat itself or building against unreleased code. A--framework-versiontag that does not exist now fails the refresh instead of logging a warning and exiting 0.
(PR #5513) -
SarvamTTSServiceandSarvamHttpTTSServicenow default tobulbul:v3, Sarvam's current TTS model. Sarvam's API no longer servesbulbul:v2and rejects requests for it, so the previous default could not synthesize. The v3 defaults come with it:shubhas the speaker and a 24000 Hz sample rate.
(PR #5517) -
SmallestTTSServicenow maps all 31 languages the Waves Lightning API accepts, and no longer mapsLanguage.HE, which the API rejects.
(PR #5519) -
FireworksLLMServicenow defaults toaccounts/fireworks/models/nemotron-3-ultra-nvfp4. Fireworks no longer serves the previous default,accounts/fireworks/models/firefunction-v2, on its serverless endpoint, so requests using it returned a 404.
(PR #5524) -
CartesiaTTSServiceandCartesiaHttpTTSServicenow mapLanguage.OR(Odia) andLanguage.UR(Urdu), the two languages Sonic 3.6 added.
(PR #5532) -
Added a
turn_coveragesetting toGeminiLiveLLMService.Settings, which selects how much of the realtime input stream a user turn covers (for exampleTURN_INCLUDES_ONLY_ACTIVITYto keep video frames outside detected audio activity out of the turn).
(PR #5535) -
GroqTTSServicenow supports a variety of sample rates, defaulting to 24khz.
(PR #5538) -
LiveAvatarNewSessionRequestnow defaults toH264video encoding, matching the LiveAvatar API's own default. LiveAvatar has deprecatedVP8; passvideo_settings=VideoSettings(encoding=VideoEncoding.VP8)to keep the previous encoding while it lasts.
(PR #5539) -
KokoroTTSService.Settingshas aspeedfield, kokoro-onnx's speech rate multiplier (0.5 to 2.0, default 1.0), settable at construction and at runtime.
(PR #5543) -
OpenAIResponsesLLMService'sservice_tierdocumentation now lists OpenAI's full set of tiers, includingfast— the low-latency tier that replaced thepriorityname.
(PR #5548) -
The OpenAI server-side turn detection example now uses
gpt-transcribe, replacinggpt-4o-transcribe, which shuts down on 2027-02-26.
(PR #5549) -
OpenAISTTServicenow defaults togpt-transcribe, OpenAI's replacement forgpt-4o-transcribe, which shuts down on 2027-02-26. Withinclude_prob_metrics=True, GPT transcription models now requestlogprobsalongside ajsonresponse; only Whisper models useverbose_json.
(PR #5549) -
PocketTTSServicenow loads pocket-tts's distilled models for German, Spanish, Italian and Portuguese instead of the 24-layer variants, which are too slow to synthesize in real time on a typical CPU.
(PR #5553) -
FalImageGenService.Settingsnow acceptsnegative_promptandguidance_scale, passed through to Fal's image generation request when set.
(PR #5556) -
MoonshineSTTServicenow mapsLanguage.DE(German) andLanguage.TL/Language.FIL(Tagalog/Filipino), the languages Moonshine added to its model catalogue.
(PR #5557) -
TogetherLLMServicenow defaults tozai-org/GLM-5.2. The previous default,zai-org/GLM-5.1, has been retired from Together's serverless inference and returnsmodel_not_available. Passsettings=TogetherLLMService.Settings(model=...)to choose a different model.
(PR #5570) -
NeuphonicTTSServiceandNeuphonicHttpTTSServicenow accepttemperaturein their settings, passing Neuphonic's synthesis randomness control (0.0–1.0) through to the API. Left unset, Neuphonic's own default applies.
(PR #5578) -
NvidiaLLMServicenow defaults tonvidia/nemotron-3-super-120b-a12b. The previous default,nvidia/nemotron-3-nano-30b-a3b, reached end of life on NVIDIA's NIM cloud endpoint and now returns HTTP 410.a
(PR #5579) -
⚠️ Behavior change: A word-timestamp event that matches nothing left to speak is now dropped instead of being emitted as a
TTSTextFrame. Passing it through put text the LLM never wrote into the conversation context. Nothing is lost: the text that word should have covered is carried by the word that puts the sentence back in step, or emitted when the audio context ends.
(PR #5585) -
⚠️
Mem0MemoryServicenow adds retrieved memories with the"developer"role instead of"system"whenadd_as_system_messageis set. Memories are extra guidance for the model, not a system prompt. Services without a"developer"role receive them as"user"— the same treatment non-OpenAI services already gave the"system"message.
(PR #5596) -
SegmentedSTTServicenow appends 0.5 s of silence to each speech segment before transcribing it, so the model hears the end of speech and finishes the last word instead of dropping or garbling it. This applies to every service built on it, such asWhisperSTTService,OpenAISTTService,GroqSTTServiceandMoonshineSTTService. The padding is submitted to the provider, so it counts toward STT usage metrics. Passtrailing_silence_secs=0to the service to disable it, or another value to tune it.
(PR #5621) -
FunctionCallResultFramenow carries anerrorfield, set when a call ends because its handler raised, so a failed call can be told apart from a successful one. Theresultis unchanged: it remains the stand-in message the LLM reads.
(PR #5622) -
⚠️ Behavior change: the Deepgram Flux services no longer push their eager end-of-turn transcript as an
InterimTranscriptionFrame. That transcript is a prediction the service may withdraw, and it now travels as anEagerTranscriptionFrame, emitted only underenable_eager_end_of_turn, so it is no longer surfaced to clients as partial user speech. Flux emits no interim transcriptions as a result; subscribe toon_updatefor incremental transcript text.
(PR #5625) -
The
on_user_turn_inference_triggeredevent emitted byUserTurnControllerand byBaseUserTurnStopStrategynow carries aUserTurnSpeculation | Noneas its last argument, set when the inference answers a turn that hasn't ended yet and carrying the turn text it was run against. A custom stop strategy requests one by passing it totrigger_user_turn_inference_triggered(). The events of the same name onLLMUserAggregatorandUserTurnProcessorare unchanged, so handlers registered on those keep their signature.```python @controller.event_handler("on_user_turn_inference_triggered") async def on_user_turn_inference_triggered(controller, strategy,speculation): ...
```
(PR #5625) -
LLMContextFramecarries aspeculationflag marking an inference as speculative, so the response it produces is held until the turn it answers is confirmed.
(PR #5625) -
The
AGENTS.mdthatpipecat initwrites now describes Pipecat Flows as part of Pipecat (pipecat.flows) and directs coding agents to build a new flow as aFlowConfigYAML file plus a Python tools module, reserving Python-built flows for routing or prompts that need code.
(PR #5628) -
Eval suite
results.jsonlrecords carryscenarioandkind(scriptorsimulation), and a repeated sweep (--repeat) reports pass rates and exits 0.
(PR #5646) -
pipecat evalloads the nearest.env(walking up from the working directory, shell variables winning) before running, so a simulation's persona LLM and a hosted judge find their credentials the way the bots do.python-dotenvis now a core dependency.
(PR #5646) -
CrusoeLLMServicenow defaults toopenai/gpt-oss-120b. The previous default waszai/GLM-5.2. Passsettings=CrusoeLLMService.Settings(model=...)to choose a different model.
(PR #5656) -
The Speechify service now sends the standard
Speechify-Caller-Versionattribution header (valued frompipecat_version()), replacing the non-standardX-Pipecat-Versionheader.
(PR #5668) -
The
runnerextra now requirespipecat-ai-prebuilt>=1.1.0. The prebuilt client UI served by the development runner gains a LiveKit option in its transport selector, backed by@pipecat-ai/livekit-transport1.0.0, and moves to@pipecat-ai/voice-ui-kit0.14.0.
(PR #5675) -
EvalSession.from_scenario()builds the session for a loaded scenario of either kind, anEvalScriptSessionfor a scripted scenario or anEvalSimulationSessionfor a simulation, andEvalSessionis the base class of both. ConstructingEvalSession(...)directly is no longer supported; use the kind's own class.
(PR #5676) -
EvalClientParamsholds only what the scenario asks of the bot, andEvalClienttakes it together with the run'sEvalSessionParamsand the services.EvalClient.for_scenario()andEvalClient.for_simulation()are gone; the sessions construct the client.
(PR #5676) -
The eval sessions and
EvalSuite.run()take how a run behaves as oneEvalSessionParams(connect_timeout_s,default_timeout_ms,record_path,cache_dir,use_cache,stop_bot,trigger_disconnect), and the services a run uses (judge,user_tts,bot_stt, and a simulation'spersona_llm) as keyword arguments on bothfrom_scenario()and the constructors.
(PR #5676) -
Cartesia word timestamps for Chinese and Japanese now keep one token per reported entry, each with its own start time, instead of joining a whole
word_timestampsmessage into a single token.
(PR #5679) -
PiperTTSServicedownloads voice models to~/.cache/pipecat/piperby default, the same cache locationKokoroTTSServiceuses, instead of the current working directory. Passdownload_dirto keep models elsewhere; a model already downloaded into a working directory is fetched again into the cache on first use.
(PR #5687)
Deprecated
-
Deprecated
LatencyBreakdown.chronological_events(). UseLatencyBreakdown.turn_contribution_lines()instead, which names every part of the interval rather than the services that happened to report a metric, and whose lines sum to the measured latency. Will be removed in 2.0.0.
(PR #5445) -
Deprecated
SarvamTTSModel.BULBUL_V2. Sarvam's API rejectsbulbul:v2on both the REST and the WebSocket endpoint, so it cannot synthesize; useSarvamTTSModel.BULBUL_V3. ConstructingSarvamTTSServiceorSarvamHttpTTSServicewith it still resolves a model config and now emits aDeprecationWarning. It will be removed in 2.0.0.
(PR #5517) -
Deprecated the
on_progressparameter ofEvalSession()andEvalSession.from_scenario(), and theon_updateparameter ofEvalSuite.run(). Use the event handlers of the same name instead. Passing a callback still works, keeps its existing signature, and emits aDeprecationWarning. They will be removed in 2.0.0.
(PR #5520) -
Setting the system prompt as a
"system"message at the start ofLLMContextis deprecated and will stop working in 2.0.0. Set it on the LLM service instead:```python # Before context = LLMContext([{"role": "system", "content": "Be helpful."}]) # After llm = OpenAILLMService(api_key=..., system_instruction="Be helpful.") context = LLMContext() ```To change the prompt at runtime, push an
LLMUpdateSettingsFramecarryingLLMSettings(system_instruction=...)instead of rewriting the first context message.
(PR #5596) -
EvalScenario,EvalResult,EvalTurn,EvalTurnResult, andEvalTurnProgressare nowEvalScriptScenario,EvalScriptResult,EvalScriptTurn,EvalScriptTurnResult, andEvalScriptTurnProgress, the scripted kind of scenario. The old names remain as aliases until 2.0.0.
(PR #5646) -
pipecat.evals.harnessmoved topipecat.evals.script_session. The old module path remains until 2.0.0.
(PR #5646) -
RTVIEvalSerializeris nowEvalSerializer, the serializer of the bot'sEvalTransport; its client-side counterpart isEvalClientSerializer. The old name remains as an alias until 2.0.0.
(PR #5646) -
Deprecated the
connect_timeout_s,default_timeout_ms,record_path,cache_dir,use_cache,stop_bot, andtrigger_disconnectkeyword arguments ofEvalSession.from_scenario()andEvalScriptSession.from_scenario(), and theuse_cacheanddefault_timeout_mskeyword arguments ofEvalSuite.run(). Passparams=EvalSessionParams(...)instead. The old arguments still work, override theparamsfield of the same name, and emit aDeprecationWarning. They will be removed in 2.0.0.EvalSimulationSession.from_scenario()is new and takesparamsonly.
(PR #5676) -
Deprecated
service: openaiin a scenario'sjudge.eval:andsimulator:blocks andservice: cartesiain itsuser.speech:block, along with theopenai_service()andcartesia_service()builders behind them. The built-in names are the local, keyless services (Ollama, Kokoro, Moonshine, Whisper); any other provider is afactory:, a dotted path to a callable that takes the block and returns the service, documented with examples inpipecat.evals.services. The old names still work and emit aDeprecationWarning; they will be removed in 2.0.0.
(PR #5678)
Removed
- Removed
SEND_CHUNK_MSfrompipecat.evals.harness. The harness now sends the user's audio through its own output transport, which paces it like any Pipecat transport does.\n
(PR #5646)
Fixed
-
Fixed
AsyncAITTSServiceignoring runtimemodel,voiceandlanguageupdates. Those three are sent only in the websocket init message and never repeated per utterance, so a change to any of them now opens a new session instead of being stored and warned about.
(PR #5464) -
Deepgram Flux STT now applies a
modelornumeralsupdate by reconnecting, since Flux reads those only from the connection URL. The reconnect waits until the user stops speaking, and settings Flux accepts on a live connection (keyterm,eot_threshold,eager_eot_threshold,eot_timeout_ms,language_hints) are still sent without dropping it.
(PR #5477) -
A Deepgram Flux
FatalErrornow reports the code and description Flux sends, instead of a genericUnknown error.
(PR #5477) -
Deepgram Flux STT now fails instead of hanging when a connection cannot be established. A connection setting the endpoint rejects leaves the SageMaker session opening forever and produces no confirmation on either transport, so both the session handshake and the wait for Flux to confirm it are now bounded and reported through
on_connection_error.
(PR #5498) -
Deepgram Flux STT now marks itself unusable once it can no longer transcribe — after a fatal error from Flux, or a connection that never establishes — so the failure reaches the pipeline instead of the service staying connected and silent.
(PR #5498) -
Fixed
GladiaSTTServicedropping Gladia'sstop_recordingmessage on a graceful end. The websocket closed before the message was sent, so sessions ended by dropping the connection rather than closing cleanly, and the service'son_disconnectedevent fired twice.
(PR #5499) -
Fixed the pipeline idle timeout cancelling a live session while the user is still talking, on pipelines that get their turns from a provider instead of local VAD.
idle_timeout_framesnow countsUserStartedSpeakingFrame,TranscriptionFrameandInterimTranscriptionFrameas user activity, alongsideBotSpeakingFrameand the VAD-drivenUserSpeakingFrame.
(PR #5502) -
Corrected the Nova Sonic region lists in
AWSNovaSonicLLMService's docstring and example:eu-north-1is a supported region for both Nova 2 Sonic and Nova Sonic.
(PR #5508) -
Fixed the Vonage-Google package conflict so it no longer prevents
GeminiSTTServiceusage in environments where the Vonage connector can't be installed anyway, such as development environments on macOS. The repo lockfile resolvedgoogle-genaito 2.8.0 — below the 2.9.0 the service needs — for every Python 3.13 environment, to satisfyvonage-video-connector'spydantic<2.12pin, though the connector is installable only on Python 3.13 Linux. That environment is now resolved apart from every other. Installs from PyPI were never affected.
(PR #5512) -
Fixed
SarvamHttpTTSServiceignoring itssample_rate. It named the fieldsample_ratein the request body where Sarvam's REST API readsspeech_sample_rate, so synthesis always came back at the model's default rate while the audio frames were tagged with the requested one — audio played back at the wrong speed for any other rate.
(PR #5517) -
Fixed the Fireworks function-calling example, which named
accounts/fireworks/models/gpt-oss-20b. Fireworks retired that model from its serverless endpoint, so the example now usesaccounts/fireworks/models/nemotron-3-ultra-nvfp4.
(PR #5524) -
Fixed the
examples/flows/llm_switching.pyBedrock service, which pinned a Claude model Bedrock has retired and a latency mode Bedrock does not offer for its replacement. It now usesus.anthropic.claude-haiku-4-5-20251001-v1:0.
(PR #5525) -
Corrected
GeminiTTSService's documented model names: they differ between the Cloud Text-to-Speech and Gemini API backends, and the docstrings named only Cloud Text-to-Speech'sgemini-2.5-flash-tts/gemini-2.5-pro-tts, which the Gemini API rejects with a 404.
(PR #5526) -
Fixed Deepgram STT settings passed through
extrabeing discarded when the key matched a declared settings field (for exampleextra={"diarize": True}). Such keys are now promoted to their field and sent to Deepgram.
(PR #5527) -
Fixed
GeminiTTSServiceignoring itslanguagesetting on the Gemini API (GenAI) backend. The configured language is now sent as the speech config'slanguage_code, as it already is on the Cloud Text-to-Speech backend.
(PR #5537) -
LmntTTSServicenow maps all 31 languages LMNT's Blizzard model speaks, adding Assamese, Bengali, Czech, Danish, Finnish, Malayalam, Marathi, Slovak, Tamil and Telugu. These languages already reached the API through the base-code fallback, which logged a "not verified" warning for each of them.
(PR #5544) -
Fixed an
OpenAISTTServiceerror whereinclude_prob_metrics=Truesent a request that diarization models reject. Those models report no per-token or per-segment probabilities, so the service now transcribes without requesting them and warns once.
(PR #5549) -
Fixed the
examples/voice/voice-pockettts.pyimport ofPocketTTSService, which named a module that does not exist.
(PR #5553) -
Fixed an issue where
ElevenLabsRealtimeSTTServiceignored the realtime API'squota_exceeded,unaccepted_termsandinvalid_requestmessages instead of reporting them as errors.
(PR #5555) -
Fixed the PCM sample rates
GrokRealtimeLLMServiceaccepts: the Grok Voice Agent API's 22050 Hz option is now allowed, and 21050 Hz — which the API rejects — is not.
(PR #5560) -
Fixed the Rime TTS services' language mapping: Arabic, Italian, Japanese and Portuguese — all served by Rime's
codamodel — now resolve to Rime's language codes, and a region-qualified language such asLanguage.EN_USfalls back to a base code Rime accepts instead of a BCP-47 tag that made Rime close the connection without synthesizing.
(PR #5564) -
SambaNovaLLMServicenow sends thefrequency_penalty,presence_penaltyandseedsettings, which SambaNova's chat completions API accepts. They were previously dropped from the request.
(PR #5565) -
Fixed a
SpeechmaticsSTTServiceerror where settingsplit_sentencesraisedValueError: "VoiceAgentConfig" object has no field "split_sentences"and the service failed to construct.
(PR #5568) -
Fixed
SpeechmaticsTTSServicereporting thespeechmatics-rtSDK version, rather than Pipecat's, in thesm-apptag it sends to Speechmatics. The tag now carries the Pipecat version, matchingSpeechmaticsSTTService.
(PR #5569) -
Fixed the NVIDIA examples
update-settings/llm/llm-nvidia.pyandvoice/voice-nvidia-sagemaker.py, which requested Llama models that NVIDIA's NIM cloud endpoint no longer serves.
(PR #5579) -
Corrected the
modeldocstring inOneShotInputParamsforUltravoxRealtimeLLMService: the field defaults toNone, which lets Ultravox pick its current default model, rather than to the legacyfixie-ai/ultravoxalias.
(PR #5582) -
Fixed word-level tracking stalling for the rest of a sentence when a TTS service reports a word-timestamp event that doesn't match what is being spoken. The next correctly reported word is now matched past the gap and carries the text that was never reported, so
TTSTextFrames and RTVI progress keep pace with the audio. Previously the sentence stayed stuck at the first unmatched word, every word after it went unmatched too, and the remainder arrived as a single frame once the audio context ended.
(PR #5585) -
Fixed
NvidiaLLMServiceemitting reasoning frames before TTFB metrics, so observers receive TTFB before reasoning output for structured reasoning fields and inline<think>blocks.
(PR #5589) -
The LLM usage debug log now includes cache creation tokens alongside cache reads.
(PR #5602) -
OpenAI-compatible LLM services now report prompt-cache write tokens as
LLMTokenUsage.cache_creation_input_tokens, so usage-based cost tracking accounts for them. CoversBaseOpenAILLMServiceand the services inheriting it,OpenAIResponsesLLMService, andSambaNovaLLMService. Providers that do not report the count leave the field unset.
(PR #5602) -
OutputAudioRawFrames pushed from upstream of an STT service (for example a sound effect or a recording played into the pipeline before the STT) now reach the output transport untouched. STT services only transcribeInputAudioRawFrames. Previously any audio frame was treated as user speech, so the audio was sent to the transcription provider and, withaudio_passthrough=False, never reached the transport.
(PR #5609) -
VoicemailDetectorno longer swallowsEndWorkerFrametravelling upstream through a closed gate, so a handler that ends the call reachesPipelineWorker. Closed classifier and conversation gates already admittedCancelWorkerFrame(a system frame) but dropped other worker lifecycle frames.
(PR #5614) -
SegmentedSTTService(Whisper, Moonshine, and the other segmented STT services) now transcribes each speech segment in a background task. Previously the transcription blocked the service's input audio frames, so a VAD downstream could not analyze them until it finished. Transcripts are still pushed in segment order.
(PR #5630) -
A punctuation mark reported as its own word-timestamp entry no longer shifts the text around it or records the mark twice. Trailing punctuation is attributed to the word before it, so the mark's own event arrived with nothing left to account for and spent a character of the following word instead — no text was lost and the cursors resynced a word later, but the words in between were recorded with the break misplaced (
"Yeah",",","I"gave"Yeah, I c an help"), and a turn ending inside that window kept the misplaced split. Where a service forwards raw tokens (pre_merge_tokens=False) the mark was also written to the context a second time. Most visible for languages reported one character at a time, where every mark is affected, though not specific to them.
(PR #5633) -
LLMServicenow logs the composed system instruction only when it changes. Previously every tool sync recomposed it, so the full prompt was logged once per turn.
(PR #5635) -
Fixed
LLMContextSummarizer's message-count trigger (max_unsummarized_messages), which never reset after a summary was applied and counted the injected summary message as a new conversation turn.
(PR #5642) -
Bots scaffolded by
pipecat initwith Sarvam, Whisper, or Moonshine STT, Sarvam TTS, or Google STT no longer crash at startup. Sarvam and Google STT now use each service's default model or location, and Whisper and Moonshine read an optionalWHISPER_MODELorMOONSHINE_MODELvariable that falls back to the default model. Previously the generated code read a variable the generated.env.examplenever listed, and the unset variable arrived as an explicitNonethat overrode the working default and failed validation.
(PR #5661) -
The generated
.env.examplenow lists every variable the scaffolded bot reads. This adds entries for ElevenLabs realtime STT, Gemini STT and TTS, Mistral STT, xAI STT, Soniox TTS, Deepgram Flux TTS, Deepgram Flux SageMaker TTS, and the NVIDIA TTS voice ID, names the Fal keyFAL_API_KEYto match the generated code, and reads the AWS Transcribe session token fromAWS_SESSION_TOKENinstead of the misspelledAWS_AWS_SESSION_TOKEN.
(PR #5661) -
Fixed
WhisperSTTServiceraisingValueErrorat construction whenSettings(language=None)is paired with a multilingual model.Noneasks Whisper to detect the language itself, which every model supports; the language check previously lookedNoneup in the model's supported-language list.
(PR #5669) -
Fixed the
Model.DISTIL_LARGE_V2andMLXModel.DISTIL_LARGE_V3Whisper models being documented as multilingual. Every Distil-Whisper release is English-only.
(PR #5669) -
Local STT and TTS services now reject a missing or blank model or voice at startup with a "must be specified" error. Piper, Pocket TTS, Kokoro, and XTTS check the voice in the constructor (Pocket TTS before loading its model, and Kokoro also against the loaded voices file), and Whisper, Whisper MLX, and Moonshine check the model. Previously a blank value, which is what a copied
.env.exampleproduces, reached the underlying library and failed with an unrelated message, sometimes only on the first utterance. XTTS also reports an unknown studio speaker as an error frame instead of raising aKeyError.
(PR #5670) -
Fixed
XAISTTServiceemitting each utterance's text twice. xAI sends a chunk final for every locked segment and then an utterance final that restates the whole utterance, and both were pushed asTranscriptionFrame, so LLM context aggregators and other consumers that accumulate finals saw the text duplicated. Chunk finals are now pushed asInterimTranscriptionFrameand only the utterance final becomes aTranscriptionFrame, always withfinalized=True.
(PR #5673) -
Fixed
SonioxSTTServicedropping Soniox's end-of-audio frame on a graceful end. The websocket closed before it was sent, so sessions ended by dropping the connection rather than closing cleanly, and the service'son_disconnectedevent fired twice.
(PR #5682) -
Fixed
SarvamSTTServicemislabeling every transcript as Hindi when no language was detected, the default behavior intranscribemode. Unresolved languages now stayNone, andur-IN,mai-IN,sd-IN, andkok-INare now correctly mapped.
(PR #5692) -
The
pipecat eval suitedashboard fits the terminal: rows are windowed to the active runs, including in a repeated sweep, and never wrap, so the tally at the bottom is always on screen. A running row's spinner animates and each row carries a clock, from its first attempt's start to its last attempt's end; the failures list is set apart from its header by a blank line.
(PR #5694)
Other
-
CartesiaSTTService,DeepgramFluxSTTService, andDeepgramFluxSageMakerSTTServicenow log the provider's request ID when the connection is established, so a session can be traced in provider-side diagnostics.
(PR #5475) -
Documented
SonioxSTTService'scontextsetting as accepting either aSonioxContextObjector a plain string of background text on any current Soniox model, along with the 8k-token cap Soniox applies to the context object.
(PR #5566)