All notable changes to Pipecat Flows will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
Added
cancel_on_interruptiontoFlowsFunctionSchemas. -
Added
@flows_direct_functiondecorator for attaching metadata to Pipecat direct functions. This allows configuring behavior likecancel_on_interruptionon the function definition.Example usage:
from pipecat_flows import flows_direct_function, FlowManager @flows_direct_function(cancel_on_interruption=False) async def long_running_task(flow_manager: FlowManager, query: str): """Perform a task that should not be cancelled on interruption. Args: query: The query to process. """ # ... implementation return {"status": "complete"}, None
Non-decorated direct functions use
cancel_on_interruption=Falseby default, ensuring all function calls complete even during user interruptions.
- Changed
cancel_on_interruptiondefault fromTruetoFalsein bothFlowsFunctionSchemaand@flows_direct_function. Function calls now complete even during user interruptions by default, preventing stalled transitions and dropped results.
- Fixed interrupted transition leaving flow permanently stuck when a user interruption cancelled a function call mid-execution (#234).
- Added support for
global_functionsparameter inFlowManagerinitialization. Global functions are available at every node in a flow without needing to be specified in each node's configuration. Supports bothFlowsFunctionSchemaandFlowsDirectFunctiontypes.
-
Changed the fallback strategy to
APPENDin the event thatRESET_WITH_SUMMARYfails. -
Updated food ordering examples (food_ordering.py and food_ordering_direct_functions.py) to demonstrate global function usage with a delivery estimate function.
-
Add support for the new Pipecat
LLMSwitcher, which can be used as a drop-in replacement forLLMServices in scenarios where you want to switch LLMs at runtime.There are a couple of pre-requisites to using
LLMSwitcher:- You must be using the new universal
LLMContextandLLMContextAggregatorPair(as of Pipecat 0.0.82, supported only by Pipecat's OpenAI and Google LLM implementations, but with more on the way). - You must be using "direct" functions or
FlowsFunctionSchemafunctions (as opposed to provider-specific formats).
Using
LLMSwitcherlooks like this:# Create shared context and aggregators for your LLM services context = LLMContext() context_aggregator = LLMContextAggregatorPair(context) # Instantiate your LLM services llm_openai = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) llm_google = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY")) # Instantiate a switcher # (ServiceSwitcherStrategyManual defaults to OpenAI, as it's first in the list) llm_switcher = LLMSwitcher( llms=[llm_openai, llm_google], strategy_type=ServiceSwitcherStrategyManual ) # Create your pipeline as usual (passing the switcher instead of an LLM) pipeline = Pipeline( [ transport.input(), stt, context_aggregator.user(), llm_switcher, tts, transport.output(), context_aggregator.assistant(), ] ) task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True)) # Initialize your flow manager as usual (passing the switcher instead of an LLM) flow_manager = FlowManager( task=task, llm=llm_switcher, context_aggregator=context_aggregator, ) # ... # Start your flow as usual @transport.event_handler("on_client_connected") async def on_client_connected(transport, participant): await flow_manager.initialize(create_main_node()) # ... # Whenever is appropriate, switch LLMs! await task.queue_frames([ManuallySwitchServiceFrame(service=llm_google)])
- You must be using the new universal
- Added an
@propertyfor the followingFlowManagerattributes in order to officially make them part of the public API:state,task,transport, andcurrent_node.
- Corrected an issue where the Flow Manager state was marked as private. To
make this clear, added a
@propertyto access the state.
- Static Flows are now deprecated and will be removed in v1.0.0. Use Dynamic
Flows in their place. The deprecation includes the
flow_configarg of theFlowManagerand theFlowConfigtype.
-
Addded a new optional
namefield toNodeConfig. When using dynamic flows alongside "consolidated" functions that return a tuple (result, next node), giving the next node anameis helpful for debug logging. If you don't specify aname, an automatically-generated UUID is used. -
Added support for providing "consolidated" functions, which are responsible for both doing some work as well as specifying the next node to transition to. When using consolidated functions, you don't specify
transition_toortransition_callback.Usage:
# "Consolidated" function async def do_something(args: FlowArgs) -> tuple[FlowResult, NodeConfig]: foo = args["foo"] bar = args.get("bar", "") # Do some work (optional; this function may be a transition-only function) result = await process(foo, bar) # Specify next node (optional; this function may be a work-only function) # This is either a NodeConfig (for dynamic flows) or a node name (for # static flows) next_node = create_another_node() return result, next_node def create_a_node() -> NodeConfig: return NodeConfig( task_messages=[ # ... ], functions=[FlowsFunctionSchema( name="do_something", description="Do something interesting.", handler=do_something, properties={ "foo": { "type": "integer", "description": "The foo to do something interesting with." }, "bar": { "type": "string", "description": "The bar to do something interesting with." } }, required=["foo"], )], )
-
Added support for providing "direct" functions, which don't need an accompanying
FlowsFunctionSchemaor function definition dict. Instead, metadata (i.e.name,description,properties, andrequired) are automatically extracted from a combination of the function signature and docstring.Usage:
# "Direct" function # `flow_manager` must be the first parameter async def do_something(flow_manager: FlowManager, foo: int, bar: str = "") -> tuple[FlowResult, NodeConfig]: """ Do something interesting. Args: foo (int): The foo to do something interesting with. bar (string): The bar to do something interesting with. """ # Do some work (optional; this function may be a transition-only function) result = await process(foo, bar) # Specify next node (optional; this function may be a work-only function) # This is either a NodeConfig (for dynamic flows) or a node name (for static flows) next_node = create_another_node() return result, next_node def create_a_node() -> NodeConfig: return NodeConfig( task_messages=[ # ... ], functions=[do_something] )
functionsare now optional in theNodeConfig. Additionally, for AWS Bedrock, Anthropic, and Gemini, you no longer need to provide a no_op function. The LLM adapters now handle this case on your behalf. This allows you to either omitfunctionsfor nodes, which is common for the end node, or specify an empty function call list, if desired.
-
The
ttsparameter inFlowManager.__init__()is now deprecated and will be removed in a future version. Thetts_sayaction now pushes aTTSSpeakFrame. -
Deprecated
transition_toandtransition_callbackin favor of "consolidated"handlers that return a tuple (result, next node). Alternatively, you could use "direct" functions and avoid usingFlowsFunctionSchemas or function definition dicts entirely. See the "Added" section above for more details. -
Deprecated
set_node()in favor of doing the following for dynamic flows:- Prefer "consolidated" or "direct" functions that return a tuple (result,
next node) over deprecated
transition_callbacks - Pass your initial node to
FlowManager.initialize() - If you really need to set a node explicitly, use
set_node_from_config()
In all of these cases, you can provide a
namein your new node's config for debug logging purposes. - Prefer "consolidated" or "direct" functions that return a tuple (result,
next node) over deprecated
-
Fixed an issue where
RESET_WITH_SUMMARYwasn't working for theGeminiAdapter. Now, theGeminiAdapteruses thegoogle-genaipackage, aligning with the package used bypipecat-ai. -
Fixed an issue where if
run_in_parallel=Falsewas set for the LLM, the bot would trigger N completions for each sequential function call. Now, Flows uses Pipecat's internal function tracking to determine when there are more edge functions to call. -
Overhauled
pre_actionsandpost_actionstiming logic, making their timing more predictable and eliminating some bugs. For example, nowtts_sayactions will always run after the bot response, when used inpost_actions.
- Added support for
AWSBedrockLLMServiceby adding anAWSBedrockAdapter.
-
Added
respond_immediatelytoNodeConfig. Setting it toFalsehas the effect of making the bot wait, after the node is activated, for the user to speak before responding. This can be used for the initial node, if you want the user to speak first. -
Bumped the minimum required
pipecat-aiversion to 0.0.67 to align with AWS Bedrock additions in Pipecat. This also adds support forFunctionCallParamswhich were added in 0.0.66. -
Updated to use
FunctionCallParamsas args for the function handler. -
Updated imports to use the new .stt, .llm, and .tts paths.
-
Added AWS Bedrock examples for insurance and patient_intake.
-
Updated examples to
audio_in_enabled=Trueand removevad_enabledandvad_audio_passthroughto align with the latest PipecatTransportParams.
-
Added a new "function" action type, which queues a function to run "inline" in the pipeline (i.e. when the pipeline is done with all the work queued before it).
This is useful for doing things at the end of the bot's turn.
Example usage:
async def after_the_fun_fact(action: dict, flow_manager: FlowManager): print("Done telling the user a fun fact.") def create_node() -> NodeConfig: return NodeConfig( task_messages=[ { "role": "system", "content": "Greet the user and tell them a fun fact." }, post_actions=[ ActionConfig( type="function", handler=after_the_fun_fact ) ] ] )
-
Added support for
OpenAILLMServicesubclasses in the adapter system. You can now use any Pipecat LLM service that inherits fromOpenAILLMServicesuch asAzureLLMService,GrokLLMService,GroqLLMService, and other without requiring adapter updates. See the Pipecat docs for supported LLM services. -
Added a new
FlowsFunctionSchemaclass, which allows you to specify function calls using a standard schema. This is effectively a subclass of Pipecat'sFunctionSchema.
Example usage:
# Define a function using FlowsFunctionSchema
collect_name = FlowsFunctionSchema(
name="collect_name",
description="Record the user's name",
properties={
"name": {"type": "string", "description": "The user's name"}
},
required=["name"],
handler=collect_name_handler,
transition_to="next_node"
)
# Use in node configuration
node_config = {
"task_messages": [...],
"functions": [collect_name]
}- Function handlers can now receive either
FlowArgsonly (legacy style) or bothFlowArgsand theFlowManagerinstance (modern style). Adding support for theFlowManagerprovides access to conversation state, transport methods, and other flow resources within function handlers. The framework automatically detects which signature you're using and calls handlers appropriately.
- Updated minimum Pipecat version to 0.0.60 to use
FunctionSchemaand provider-specific adapters.
-
Update restaurant_reservation.py and insurance_gemini.py to use
FlowsFunctionSchema. -
Updated examples to specify a
paramsarg forPipelineTask, meeting the Pipecat requirement starting 0.0.58.
- The
ActionManagernow has access to theFlowManager, allowing more flexibility to create custom actions.
- Fixed an issue with the LLM adapter where you were required to install all LLM dependencies to run Flows.
- Temporarily reverted the deprecation of the
ttsparameter inFlowManager.__init__(). This feature will be deprecated in a future release after the required Pipecat changes are completed.
- Added context update strategies to control how context is managed during node
transitions:
APPEND: Add new messages to existing context (default behavior)RESET: Clear and replace context with new messages and most recent function call resultsRESET_WITH_SUMMARY: Reset context but include an LLM-generated summary along with the new messages- Strategies can be set globally or per-node
- Includes automatic fallback to RESET if summary generation fails
Example usage:
# Global strategy
flow_manager = FlowManager(
context_strategy=ContextStrategyConfig(
strategy=ContextStrategy.RESET
)
)
# Per-node strategy
node_config = {
"task_messages": [...],
"functions": [...],
"context_strategy": ContextStrategyConfig(
strategy=ContextStrategy.RESET_WITH_SUMMARY,
summary_prompt="Summarize the key points discussed so far."
)
}- Added a new function called
get_current_contextwhich provides access to the LLM context.
Example usage:
# Access current conversation context
context = flow_manager.get_current_context()- Added a new dynamic example called
restaurant_reservation.py.
-
Transition callbacks now receive function results directly as a second argument:
async def handle_transition(args: Dict, result: FlowResult, flow_manager: FlowManager). This enables direct access to typed function results for making routing decisions. For backwards compatibility, the two-argument signature(args: Dict, flow_manager: FlowManager)is still supported. -
Updated dynamic examples to use the new result argument.
- The
ttsparameter inFlowManager.__init__()is now deprecated and will be removed in a future version. Thetts_sayaction now pushes aTTSSpeakFrame.
- Support for inline action handlers in flow configuration:
- Actions can now be registered via handler field in config
- Maintains backwards compatibility with manual registration
- Built-in actions (
tts_say,end_conversation) work without changes
Example of the new pattern:
"pre_actions": [
{
"type": "check_status",
"handler": check_status_handler
}
]- Updated dynamic flows to use per-function, inline transition callbacks:
- Removed global
transition_callbackfrom FlowManager initialization - Transition handlers are now specified directly in function definitions
- Dynamic transitions are now specified similarly to the static flows'
transition_tofield - Breaking change: Dynamic flows must now specify transition callbacks in function configuration
- Removed global
Example of the new pattern:
# Before - global transition callback
flow_manager = FlowManager(
transition_callback=handle_transition
)
# After - inline transition callbacks
def create_node() -> NodeConfig:
return {
"functions": [{
"type": "function",
"function": {
"name": "collect_age",
"handler": collect_age,
"description": "Record user's age",
"parameters": {...},
"transition_callback": handle_age_collection
}
}]
}- Updated dynamic flow examples to use the new
transition_callbackpattern.
- Fixed an issue where multiple, consecutive function calls could result in two completions.
-
Updated
FlowManagerto more predictably handle function calls:- Edge functions (which transition to a new node) now result in an LLM completion after both the function call and messages are added to the LLM's context.
- Node functions (which execute a function call without transitioning nodes) result in an LLM completion upon the function call result returning.
- This change also improves the reliability of the pre- and post-action execution timing.
-
Breaking changes:
- The FlowManager has a new required arg,
context_aggregator. - Pipecat's minimum version has been updated to 0.0.53 in order to use the
new
FunctionCallResultPropertiesframe.
- The FlowManager has a new required arg,
-
Updated all examples to align with the new changes.
-
Nodes now have two message types to better delineate defining the role or persona of the bot from the task it needs to accomplish. The message types are:
role_messages, which defines the personality or role of the bottask_messages, which defines the task to be completed for a given node
-
role_messagescan be defined for the initial node and then inherited by subsequent nodes. You can treat this as an LLM "system" message. -
Simplified FlowManager initialization by removing the need for manual context setup in both static and dynamic flows. Now, you need to create a
FlowManagerand initialize it to start the flow. -
All examples have been updated to align with the API changes.
- Fixed an issue where importing the Flows module would require OpenAI, Anthropic, and Google LLM modules.
- Fixed function handler registration in FlowManager to handle
__function__:tokens- Previously, the handler string was used directly, causing "not callable" errors
- Now correctly looks up and uses the actual function object from the main module
- Supports both direct function references and function names exported from the Flows editor
- Improved type safety in FlowManager by requiring keyword arguments for initialization
- Enhanced error messages for LLM service type validation
- New
transition_tofield for static flows- Combines function handlers with state transitions
- Supports all LLM providers (OpenAI, Anthropic, Gemini)
- Static examples updated to use this new transition
- Static flow transitions now use
transition_toinstead of matching function names- Before: Function name had to match target node name
- After: Function explicitly declares target via
transition_to
- Duplicate LLM responses during transitions
- New FlowManager supporting both static and dynamic conversation flows
- Provider-specific examples demonstrating dynamic flows:
- OpenAI:
insurance_openai.py - Anthropic:
insurance_anthropic.py - Gemini:
insurance_gemini.py
- OpenAI:
- Type safety improvements:
FlowArgs: Type-safe function argumentsFlowResult: Type-safe function returns
- Simplified function handling:
- Automatic LLM function registration
- Optional handlers for edge nodes
- Updated all examples to use unified FlowManager interface
-
Added LLM support for:
- Anthropic
- Google Gemini
-
Added
LLMFormatParser, a format parser to handle LLM provider-specific messages and function call formats -
Added new examples:
- movie_explorer_anthropic.py (Claude 3.5)
- movie_explorer_gemini.py (Gemini 1.5 Flash)
- travel_planner_gemini.py (Gemini 1.5 Flash)
- New example
movie_explorer.pydemonstrating:- Real API integration with TMDB
- Node functions for API calls
- Edge functions for state transitions
- Proper function registration pattern
-
Renamed function types to use graph terminology:
- "Terminal functions" are now "node functions" (operations within a state)
- "Transitional functions" are now "edge functions" (transitions between states)
-
Updated function registration process:
- Node functions must be registered directly with the LLM before flow initialization
- Edge functions are automatically registered by FlowManager during initialization
- LLM instance is now required in FlowManager constructor
-
Added flexibility to node naming with the Editor:
- Start nodes can now use any descriptive name (e.g., "greeting")
- End nodes conventionally use "end" but support custom names
- Flow configuration's
initial_nodeproperty determines the starting state
- All examples updated to use new function registration pattern
- Documentation updated to reflect new terminology and patterns
- Editor updated to support flexible node naming
-
Added an
examplesdirectory which contains five different examples showing how to build a conversation flow with Pipecat Flows. -
Added a new editor example called
patient_intake.jsonwhich demonstrates a patient intake conversation flow.
pipecat-ai-flowsnow includespipecat-aias a dependency, making it easier to get started with a fresh installation.
- Fixed an issue where terminal functions were updating the LLM context and tools. Now, only transitional functions update the LLM context and tools.
- Fixed an issue where
pipecat-aiwas mistakenly added as a dependency
-
Initial public beta release.
-
Added conversation flow management system through
FlowStateandFlowManagerclasses. This system enables developers to create structured, multi-turn conversations using a node-based state machine. Each node can contain:- Multiple LLM context messages (system/user/assistant)
- Available functions for that state
- Pre- and post-actions for state transitions
- Support for both terminal functions (stay in same node) and transitional functions
- Built-in handlers for immediate TTS feedback and conversation end
-
Added
NodeConfigdataclass for defining conversation states, supporting:- Multiple messages per node for complex prompt building
- Function definitions for available actions
- Optional pre- and post-action hooks
- Clear separation between node configuration and state management