I got this error when using function_tool with raw...
# ask-ai
g
I got this error when using function_tool with raw_schema. What kind of the error and which way I should try?
Copy code
Traceback (most recent call last):
  File "/workspace/crew/.venv/lib/python3.12/site-packages/livekit/agents/utils/log.py", line 16, in async_fn_logs
    return await fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/crew/.venv/lib/python3.12/site-packages/livekit/agents/voice/agent_activity.py", line 1646, in _pipeline_reply_task
    await exe_task
  File "/workspace/crew/.venv/lib/python3.12/site-packages/livekit/agents/utils/log.py", line 16, in async_fn_logs
    return await fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/crew/.venv/lib/python3.12/site-packages/livekit/agents/voice/generation.py", line 416, in _execute_tools_task
    fnc_args, fnc_kwargs = llm_utils.prepare_function_arguments(
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/crew/.venv/lib/python3.12/site-packages/livekit/agents/llm/utils.py", line 390, in prepare_function_arguments
    bound = signature.bind(**{**raw_fields, **context_dict})
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.asdf/installs/python/3.12.11/lib/python3.12/inspect.py", line 3280, in bind
    return self._bind(args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.asdf/installs/python/3.12.11/lib/python3.12/inspect.py", line 3269, in _bind
    raise TypeError(
TypeError: got an unexpected keyword argument 'raw_arguments'
t
Thanks for reaching out, Phuong. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
The error happens because when you use
@function_tool
with
raw_schema
, your decorated function needs to have
raw_arguments
as its very first parameter. To fix this, just double-check that your function’s signature is set up the right way. Here’s a quick example to guide you:
Copy code
@function_tool(raw_schema=raw_schema)
async def my_function(raw_arguments: dict[str, object], context: RunContext):
    # Implementation here
    pass
Remember to pull out the arguments from the
raw_arguments
dictionary, and if you have extra context parameters like
RunContext
, they should come after. If you want, I’m here to help with any other questions!
g
How about the
self
argument? Usually we wrote
Copy code
@function_tool
async def my_function(self, context: RunContext)
Should I place it after
raw_arguments
?
t
When you're using
@function_tool
with
raw_schema
, the placement of the
self
argument really depends on whether your function is part of a class or just a standalone function: • If it’s a standalone function, you don’t need to include
self
. Just make sure the function accepts
raw_arguments
as the first parameter and
context
as the second. • If it’s a class method, then go ahead and include
self
as the first parameter, followed by
raw_arguments
and
context
. Here’s a quick example for a class method:
Copy code
class MyAgent(Agent):
    @function_tool(raw_schema=raw_schema)
    async def my_function(self, raw_arguments: dict[str, object], context: RunContext):
        # Implementation here
        pass
Feel free to ask if you want me to walk you through it some more!
g
When working with
raw_arguments
, I found that the type of
dict[str,object]
is hard to work with.
Copy code
int(raw_arguments.get("duration_sec", 90))
Do we have any better approach? Or should I do typing for the
raw_arguments
myself?
t
To make working with
raw_arguments
a bit easier, you can set up clear schemas in
raw_schema
that spell out the expected parameters and their types. This way, your arguments stay well-structured and much simpler to handle. Here’s a quick example to show you what I mean:
Copy code
@function_tool(
    raw_schema={
        "name": "process_duration",
        "description": "Processes a duration in seconds.",
        "parameters": {
            "type": "object",
            "properties": {
                "duration_sec": {"type": "integer", "default": 90}
            },
            "required": []
        }
    }
)
async def process_duration(raw_arguments: dict[str, object], context: RunContext):
    duration = int(raw_arguments.get("duration_sec", 90))
    # Implementation here
    return f"Processed duration: {duration} seconds"
Using this approach makes
raw_arguments
way easier to work with and cuts down on the need for manual typing. If you want, I’m here to help with anything else you might need!
g
The problem is the step
duration = int(raw_arguments.get("duration_sec", 90))
rather than the
raw_schema
Since the input argument
raw_arguments: dict[str, object]
rather than
raw_arguments: dict[str, int]
so I have to do the converting step, which is the issue.
t
To help with the type conversion for
raw_arguments
, you might want to create a helper function or use a clear approach to check and convert the argument types. Here’s a simple example to get you started:
Copy code
@function_tool(
    raw_schema={
        "name": "process_duration",
        "description": "Processes a duration in seconds.",
        "parameters": {
            "type": "object",
            "properties": {
                "duration_sec": {"type": "integer", "default": 90}
            },
            "required": []
        }
    }
)
async def process_duration(raw_arguments: dict[str, object], context: RunContext):
    try:
        # Convert and validate the duration
        duration = int(raw_arguments.get("duration_sec", 90))
    except (ValueError, TypeError):
        # Handle invalid input gracefully
        duration = 90

    # Implementation here
    return f"Processed duration: {duration} seconds"
Another option is to create a handy utility function that takes care of type conversion and validation for
raw_arguments
. This way, your code stays neat and easy to reuse. If you’d like, I’m happy to share an example of that too! Just let me know.
g
Another option is to create a handy utility function that takes care of type conversion and validation for
raw_arguments
. This way, your code stays neat and easy to reuse.
please share me an example.
t
Here’s a handy little utility function to help you handle type conversion and validation for
raw_arguments
. It’s a neat way to keep your code tidy and easy to reuse:
Copy code
def get_argument(raw_arguments: dict[str, object], key: str, expected_type: type, default: object):
    """
    Utility function to extract and validate an argument from raw_arguments.

    :param raw_arguments: The dictionary of raw arguments.
    :param key: The key to extract from the dictionary.
    :param expected_type: The expected type of the argument.
    :param default: The default value to return if the key is missing or invalid.
    :return: The validated and converted argument.
    """
    value = raw_arguments.get(key, default)
    if not isinstance(value, expected_type):
        return default
    return value

# Example usage in a function tool
@function_tool(
    raw_schema={
        "name": "process_duration",
        "description": "Processes a duration in seconds.",
        "parameters": {
            "type": "object",
            "properties": {
                "duration_sec": {"type": "integer", "default": 90}
            },
            "required": []
        }
    }
)
async def process_duration(raw_arguments: dict[str, object], context: RunContext):
    duration = get_argument(raw_arguments, "duration_sec", int, 90)
    # Implementation here
    return f"Processed duration: {duration} seconds"
This little helper makes sure each argument is checked and converted to the right type, and if something’s off, it falls back to a default value. If you want me to walk you through it or need a hand with anything else, just let me know!