diff --git a/pipecat/learn/context-management.mdx b/pipecat/learn/context-management.mdx index b5049705..c5d4d672 100644 --- a/pipecat/learn/context-management.mdx +++ b/pipecat/learn/context-management.mdx @@ -90,43 +90,34 @@ Key properties: ### 2. Context with Function Calling -Context can also include tools (function definitions) that the LLM can call during conversations: +Context can also include tools (function definitions) that the LLM can call during conversations. You can pass tools as a list of direct functions, `FunctionSchema` objects, or a `ToolsSchema`: ```python -from pipecat.adapters.schemas.function_schema import FunctionSchema -from pipecat.adapters.schemas.tools_schema import ToolsSchema - -# Define available functions -weather_function = FunctionSchema( - name="get_current_weather", - description="Get the current weather", - properties={ - "location": { - "type": "string", - "description": "The city and state, e.g. San Francisco, CA", - }, - "format": { - "type": "string", - "enum": ["celsius", "fahrenheit"], - "description": "The temperature unit to use.", - }, - }, - required=["location", "format"], -) - -# Create tools schema -tools = ToolsSchema(standard_tools=[weather_function]) - -# Create context with both messages and tools -context = LLMContext(messages, tools) +from pipecat.services.llm_service import FunctionCallParams + +# Define a direct function +async def get_current_weather(params: FunctionCallParams, location: str, format: str): + """Get the current weather. + + Args: + location: The city and state, e.g. San Francisco, CA. + format: The temperature unit to use. Must be either "celsius" or "fahrenheit". + """ + weather_data = {"conditions": "sunny", "temperature": "75"} + await params.result_callback(weather_data) + +# Create context with messages and tools +# Direct functions are auto-registered with the LLM service +context = LLMContext(messages, tools=[get_current_weather]) user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) ``` Function call results are also automatically stored in the context, maintaining a complete conversation history including tool interactions. - We'll cover function calling in detail in an upcoming section. The context - aggregator handles function call storage automatically. + We'll cover function calling in detail in the [Function + Calling](/pipecat/learn/function-calling) guide. The context aggregator handles + function call storage automatically. ### 3. Add Context Aggregators to Your Pipeline diff --git a/pipecat/learn/function-calling.mdx b/pipecat/learn/function-calling.mdx index d9e2574c..1ac4a8df 100644 --- a/pipecat/learn/function-calling.mdx +++ b/pipecat/learn/function-calling.mdx @@ -95,7 +95,7 @@ The bot's personality (e.g. "You are a helpful assistant") is set via [`system_i #### Using Direct Functions (Shorthand) -You can bypass specifying a function configuration as a `FunctionSchema` and instead pass the function directly to your `ToolsSchema`. Pipecat will auto-configure the function, gathering relevant metadata from its signature and docstring. Metadata includes: +You can bypass specifying a function configuration as a `FunctionSchema` and instead pass the function directly to your `LLMContext`. Pipecat will auto-configure the function, gathering relevant metadata from its signature and docstring. Metadata includes: - name - description @@ -105,7 +105,6 @@ You can bypass specifying a function configuration as a `FunctionSchema` and ins Note that the function signature is a bit different when using direct functions. The first parameter is `FunctionCallParams`, followed by any others necessary for the function. ```python -from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.services.llm_service import FunctionCallParams # Define a direct function @@ -119,12 +118,26 @@ async def get_current_weather(params: FunctionCallParams, location: str, format: weather_data = {"conditions": "sunny", "temperature": "75"} await params.result_callback(weather_data) -# Create a tools schema, passing your function directly to it -tools = ToolsSchema(standard_tools=[get_current_weather]) +# Pass your functions directly to LLM context +# Functions listed here are automatically registered with the LLM service +context = LLMContext(tools=[get_current_weather]) +user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) +``` -# Pass this to your LLM context +**Auto-registration**: Direct functions listed in `LLMContext(tools=[...])` are automatically registered with the LLM service when the context is used. No separate `register_direct_function()` call is needed unless you want to override the default call options. + +You can also mix direct functions with `FunctionSchema` objects in the list, or pass a `ToolsSchema` if you need provider-specific `custom_tools`: + +```python +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema + +# Mix direct functions and FunctionSchema objects +context = LLMContext(tools=[get_current_weather, weather_function]) + +# Or use ToolsSchema for advanced cases (custom_tools, etc.) +tools = ToolsSchema(standard_tools=[get_current_weather]) context = LLMContext(tools=tools) -user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) ``` #### Provider-Specific Custom Tools @@ -203,12 +216,23 @@ tools = ToolsSchema( ### 2. Register Function Handlers -Register handlers for your functions using one of these [LLM service methods](https://reference-server.pipecat.ai/en/latest/api/pipecat.services.llm_service.html#llm-service): +**Auto-registration (Recommended)**: Direct functions listed in `LLMContext(tools=[...])` are automatically registered with the LLM service. No explicit registration call is needed: + +```python +# Direct functions are auto-registered +context = LLMContext(tools=[get_current_weather, get_restaurant_recommendation]) +``` + +**Explicit registration**: Use these [LLM service methods](https://reference-server.pipecat.ai/en/latest/api/pipecat.services.llm_service.html#llm-service) when you need more control: -- `register_function` -- `register_direct_function` +- `register_function` — for non-direct functions (separate schema and handler) +- `register_direct_function` — for direct functions with custom call options or not in context -Which one you use depends on whether your function is a ["direct" function](#using-direct-functions-shorthand). +Explicit registration is useful when: +- You want to override call options (`cancel_on_interruption`, `timeout_secs`) +- You're registering a catch-all handler (`function_name=None`) +- You're registering a function that's not advertised in the context +- You need the handler registered before the context is created @@ -232,11 +256,9 @@ llm.register_function( ) ``` -```python Direct Function +```python Direct Function (Auto-registered) from pipecat.services.llm_service import FunctionCallParams -llm = OpenAILLMService(api_key="your-api-key") - # Direct function async def get_current_weather(params: FunctionCallParams, location: str, format: str): """Get the current weather. @@ -248,11 +270,15 @@ async def get_current_weather(params: FunctionCallParams, location: str, format: weather_data = {"conditions": "sunny", "temperature": "75"} await params.result_callback(weather_data) -# Register direct function +# Auto-registered when listed in LLMContext +context = LLMContext(tools=[get_current_weather]) + +# Or explicitly register to override call options +llm = OpenAILLMService(api_key="your-api-key") llm.register_direct_function( get_current_weather, cancel_on_interruption=False, # Don't cancel on interruption - timeout_secs=60.0, # Optional: Override global timeout for this function + timeout_secs=60.0, # Override global timeout for this function ) ``` @@ -270,6 +296,41 @@ Use `cancel_on_interruption=True` (the default) when the LLM should wait for the Use `timeout_secs` to set a specific timeout for a function that differs from the global default. For example, you might want a longer timeout for database queries or shorter timeouts for quick lookups. +#### Using the @direct_function decorator + +The optional `@direct_function` decorator allows you to specify call options directly on your function definition. This is useful when you want to set custom options but still benefit from auto-registration: + +```python +from pipecat.adapters.schemas.direct_function import direct_function +from pipecat.services.llm_service import FunctionCallParams + +@direct_function(cancel_on_interruption=False, timeout=60) +async def end_call(params: FunctionCallParams, reason: str): + """End the call. + + Args: + reason: Why the call is ending. + """ + await params.result_callback({"status": "ending"}) + +# Auto-registered with the decorator's options +context = LLMContext(tools=[end_call]) +``` + +The decorator can be used with or without arguments: + +```python +@direct_function # Uses defaults: cancel_on_interruption=True, timeout=None +async def quick_lookup(params: FunctionCallParams, query: str): + ... + +@direct_function(cancel_on_interruption=False, timeout=120) +async def long_running_task(params: FunctionCallParams, task_id: str): + ... +``` + +The decorator does not register the function itself — it only attaches metadata that the auto-registration system reads when the function appears in `LLMContext(tools=[...])`. + #### Async Function Call Cancellation If you register async function calls with `cancel_on_interruption=False`, you can also enable model-directed cancellation: @@ -285,11 +346,12 @@ When `enable_async_tool_cancellation=True` and at least one async function is re ### 3. Create the Pipeline -Include your LLM service in your pipeline with the registered functions: +Include your LLM service in your pipeline with your functions: ```python -# Initialize the LLM context with your function schemas -context = LLMContext(tools=tools) +# Initialize the LLM context with your functions +# Direct functions are auto-registered with the LLM service +context = LLMContext(tools=[get_current_weather, get_restaurant_recommendation]) # Create the context aggregator to collect the user and assistant context user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context)