brief-vase-33757
07/10/2025, 1:30 PMtall-airline-16756
07/10/2025, 1:30 PMtall-airline-16756
07/10/2025, 1:31 PM@function_tool
decorator along with Python type hints. Here is an example:
from livekit.agents import function_tool, RunContext
@function_tool
def process_numbers(context: RunContext, numbers: list[float]) -> str:
"""Sums a list of numbers and returns the result."""
return str(sum(numbers))
You can specify the expected list type—such as list[str]
for a list of strings, list[int]
for integers, etc. The type hints ensure the schema sent to the LLM correctly identifies argument types, letting your tool receive lists as expected.
For more complex structures (such as lists of objects/dictionaries), you can use a raw schema in your function tool definition. Refer to this example:
raw_schema = {
"type": "function",
"name": "process_user_list",
"parameters": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
},
"required": ["users"]
}
}
@function_tool(raw_schema=raw_schema)
def process_user_list(raw_arguments, context: RunContext):
users = raw_arguments["users"]
return f"Processed {len(users)} users"
Type hints and docstrings are important for making the expected data structure clear. Tool return values are auto-converted to strings, so handle your function output accordingly.
See the complete guide and more examples in the LiveKit knowledge base if you need to handle advanced use-cases like tool factories or lists of nested objects.
• Type hint the list in function arguments for standard lists
• Use raw_schema
for complex list/object inputs Sources: Tool definition and use | LiveKit Docs | realtime/openai-realtime-tools.pybrief-vase-33757
07/10/2025, 1:37 PMtall-airline-16756
07/10/2025, 1:38 PM@function_tool
decorator, you'll need to define fixed properties in your object's schema. But what if you want more flexibility? That's where RawFunctionTool comes in! This approach lets you work with dynamic or arbitrary properties by accepting arguments as a raw JSON dictionary. Think of it like giving yourself some extra room to play around at runtime. The trade-off? You'll need to handle validation and parsing yourself. Check out this quick example:
@function_tool(raw_schema={...})
def handle_dynamic_objects(raw_arguments, context: RunContext):
objects = raw_arguments["objects"]
for obj in objects:
# obj can have any set of properties
• Standard tools: require fixed property definitions
• Raw tools: allow dynamic/arbitrary properties in the payload Sources: agents/src/llm/function_context.ts | LLM Integrations | livekit/livekit_composite | DeepWiki | Tool definition and use | LiveKit Docs | mcp_client/util.pybrief-vase-33757
07/10/2025, 1:41 PMtall-airline-16756
07/10/2025, 1:43 PMRawFunctionTool
that can point you in the right direction. Take a look at examples/voice_agents/raw_function_description.py
, which shows you how to parse raw arguments as a dictionary. While there isn't a specific GitHub example for handling lists with dynamic properties, I've got a tip for you!
• Official example: examples/voice_agents/raw_function_description.py
shows basic use of raw arguments
• Pro tip: To handle dynamic/arbitrary properties in list items, you can adapt the raw argument pattern by processing each list element as a dict
• The main LiveKit examples repo and MCP Agent examples might also have some cool patterns for complex tool definition Sources: livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/utils.py | Tool definition and use | LiveKit Docsbrief-vase-33757
07/10/2025, 1:44 PMtall-airline-16756
07/10/2025, 1:45 PMDict[str, Any]
as a welcoming doorway for dictionary inputs. The framework will make sure everything looks good as it checks those hints.
from typing import Dict, Any
@function_tool
def process_user_info(context: RunContext, user_data: Dict[str, Any]) -> str:
# handle the dictionary
return str(user_data)
• Raw function tools (maximum flexibility): If you want ultimate freedom, use raw_schema
by setting type: "object"
and additionalProperties: true
. This lets you accept all kinds of key-value pairs, giving you total control!
raw_schema = {
"type": "function",
"name": "handle_dict",
"parameters": {
"type": "object",
"properties": {"data": {"type": "object", "additionalProperties": True}},
"required": ["data"]
}
}
@function_tool(raw_schema=raw_schema)
def handle_dict(raw_arguments, context: RunContext):
data = raw_arguments["data"]
# process the dictionary
return f"Keys: {list(data.keys())}"
Standard function tools are super straightforward for known structures, while raw function tools are perfect when you need more flexibility with dynamic or nested dictionaries. Hope this helps! 😊
Sources: Tool definition and use | LiveKit Docs | livekit-agents/livekit/agents/llm/utils.py