diff --git a/.changeset/lazy-boats-drum.md b/.changeset/lazy-boats-drum.md new file mode 100644 index 000000000..2ccad190b --- /dev/null +++ b/.changeset/lazy-boats-drum.md @@ -0,0 +1,5 @@ +--- +"livekit-plugins-openai": patch +--- + +Handle optional func args in tool calls when set to `None` diff --git a/.changeset/wild-plants-dream.md b/.changeset/wild-plants-dream.md new file mode 100644 index 000000000..51f737a24 --- /dev/null +++ b/.changeset/wild-plants-dream.md @@ -0,0 +1,6 @@ +--- +"livekit-plugins-openai": patch +"livekit-agents": patch +--- + +fix: Handle optional func args in tool calls when set to `None` diff --git a/livekit-agents/livekit/agents/llm/function_context.py b/livekit-agents/livekit/agents/llm/function_context.py index 9564c3a1c..4290d121e 100644 --- a/livekit-agents/livekit/agents/llm/function_context.py +++ b/livekit-agents/livekit/agents/llm/function_context.py @@ -54,6 +54,7 @@ class FunctionArgInfo: type: type default: Any choices: tuple | None + is_optional: bool @dataclass(frozen=True) @@ -188,6 +189,7 @@ def _register_ai_function(self, fnc: Callable) -> None: type=inner_th, default=param.default, choices=choices, + is_optional=is_optional, ) self._fncs[metadata.name] = FunctionInfo( diff --git a/livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/_oai_api.py b/livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/_oai_api.py index a19829685..b82c29de9 100644 --- a/livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/_oai_api.py +++ b/livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/_oai_api.py @@ -64,13 +64,19 @@ def create_ai_function_info( inner_type = typing.get_args(arg_info.type)[0] sanitized_value = [ _sanitize_primitive( - value=v, expected_type=inner_type, choices=arg_info.choices + value=v, + expected_type=inner_type, + choices=arg_info.choices, + is_optional=arg_info.is_optional, ) for v in arg_value ] else: sanitized_value = _sanitize_primitive( - value=arg_value, expected_type=arg_info.type, choices=arg_info.choices + value=arg_value, + expected_type=arg_info.type, + choices=arg_info.choices, + is_optional=arg_info.is_optional, ) sanitized_arguments[arg_info.name] = sanitized_value @@ -147,8 +153,11 @@ def type2str(t: type) -> str: def _sanitize_primitive( - *, value: Any, expected_type: type, choices: tuple | None + *, value: Any, expected_type: type, choices: tuple | None, is_optional: bool = False ) -> Any: + if is_optional and value is None: + return None + if expected_type is str: if not isinstance(value, str): raise ValueError(f"expected str, got {type(value)}")