From 25b927f0f00f0839f2e9ca044fd9cf0c0cf00084 Mon Sep 17 00:00:00 2001 From: Anas Khan <83116240+anxkhn@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:09:11 +0530 Subject: [PATCH] fix(utils): emit multi-type tool params as JSON array convert_function_to_tool serialized a parameter with a union type (e.g. Union[int, str]) as the comma-joined string "integer, string" instead of the JSON-Schema array ["integer", "string"]. The server's PropertyType.UnmarshalJSON reads that as a single, invalid type name, so the parameter's type constraint is silently dropped. Because types is a set, the joined string order also varied between runs. Emit a single type as a bare string (unchanged) and multiple types as a sorted list, which matches Tool.Function.Parameters.Property.type (Union[str, Sequence[str]]) and the array form the server expects. Sorting removes the non-determinism. Update test_function_with_all_types to assert the multi-type property is the array ['integer', 'string'] rather than parsing a string. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com> --- ollama/_utils.py | 7 ++++++- tests/test_utils.py | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ollama/_utils.py b/ollama/_utils.py index 15f1cc0c..ce2aace1 100644 --- a/ollama/_utils.py +++ b/ollama/_utils.py @@ -73,9 +73,14 @@ def convert_function_to_tool(func: Callable) -> Tool: schema['required'].remove(k) types.discard('null') + # Emit a single type as a string and multiple types as a JSON-Schema array. + # A comma-joined string (e.g. 'integer, string') is parsed by the server as one + # invalid type name, silently dropping the constraint. Sorting also makes the + # output deterministic across runs since types is a set. + sorted_types = sorted(types) schema['properties'][k] = { 'description': parsed_docstring[k], - 'type': ', '.join(types), + 'type': sorted_types if len(sorted_types) > 1 else ''.join(sorted_types), } tool = Tool( diff --git a/tests/test_utils.py b/tests/test_utils.py index cb9e0d4f..1c99ee0a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -98,8 +98,10 @@ def all_types( if sys.version_info >= (3, 10): assert tool['function']['parameters']['properties']['z']['type'] == 'array' assert tool['function']['parameters']['properties']['w']['type'] == 'object' - assert {x.strip().strip("'") for x in tool['function']['parameters']['properties']['v']['type'].removeprefix('[').removesuffix(']').split(',')} == {'string', 'integer'} - assert tool['function']['parameters']['properties']['v']['type'] != 'null' + # A multi-type parameter must be a JSON array, not a comma-joined string, so the + # server parses each type instead of one invalid 'integer, string' type name. + assert isinstance(tool['function']['parameters']['properties']['v']['type'], list) + assert tool['function']['parameters']['properties']['v']['type'] == ['integer', 'string'] assert tool['function']['parameters']['required'] == ['x', 'y', 'z', 'w'] else: assert tool['function']['parameters']['properties']['z']['type'] == 'array'