diff --git a/python/packages/autogen-ext/src/autogen_ext/models/ollama/_ollama_client.py b/python/packages/autogen-ext/src/autogen_ext/models/ollama/_ollama_client.py index e8abf67614a9..83699bbe548a 100644 --- a/python/packages/autogen-ext/src/autogen_ext/models/ollama/_ollama_client.py +++ b/python/packages/autogen-ext/src/autogen_ext/models/ollama/_ollama_client.py @@ -315,8 +315,17 @@ def convert_tools( if parameters is not None: ollama_properties = {} for prop_name, prop_schema in parameters["properties"].items(): + # Determine property type, checking "type" first, then "anyOf", defaulting to "string" + prop_type = prop_schema.get("type") + if prop_type is None and "anyOf" in prop_schema: + prop_type = next( + (opt.get("type") for opt in prop_schema["anyOf"] if opt.get("type") != "null"), + None, # Default to None if no non-null type found in anyOf + ) + prop_type = prop_type or "string" + ollama_properties[prop_name] = OllamaTool.Function.Parameters.Property( - type=prop_schema["type"], + type=prop_type, description=prop_schema["description"] if "description" in prop_schema else None, ) result.append( diff --git a/python/packages/autogen-ext/tests/models/test_ollama_chat_completion_client.py b/python/packages/autogen-ext/tests/models/test_ollama_chat_completion_client.py index 7499e31beec3..c020c6f932e0 100644 --- a/python/packages/autogen-ext/tests/models/test_ollama_chat_completion_client.py +++ b/python/packages/autogen-ext/tests/models/test_ollama_chat_completion_client.py @@ -1,6 +1,6 @@ import json import logging -from typing import Any, AsyncGenerator, Dict, List, Mapping +from typing import Any, AsyncGenerator, Dict, List, Mapping, Optional import httpx import pytest @@ -13,11 +13,11 @@ FunctionExecutionResultMessage, UserMessage, ) -from autogen_core.tools import FunctionTool +from autogen_core.tools import FunctionTool, ToolSchema from autogen_ext.models.ollama import OllamaChatCompletionClient -from autogen_ext.models.ollama._ollama_client import OLLAMA_VALID_CREATE_KWARGS_KEYS +from autogen_ext.models.ollama._ollama_client import OLLAMA_VALID_CREATE_KWARGS_KEYS, convert_tools from httpx import Response -from ollama import AsyncClient, ChatResponse, Message +from ollama import AsyncClient, ChatResponse, Message, Tool from pydantic import BaseModel @@ -206,6 +206,46 @@ async def _mock_chat(*args: Any, **kwargs: Any) -> ChatResponse: assert create_result.usage.completion_tokens == 12 +@pytest.mark.asyncio +async def test_convert_tools() -> None: + def add(x: int, y: Optional[int]) -> str: + if y is None: + return str(x) + return str(x + y) + + add_tool = FunctionTool(add, description="Add two numbers") + + tool_schema_noparam: ToolSchema = { + "name": "manual_tool", + "description": "A tool defined manually", + "parameters": { + "type": "object", + "properties": { + "param_with_type": {"type": "integer", "description": "An integer param"}, + "param_without_type": {"description": "A param without explicit type"}, + }, + "required": ["param_with_type"], + }, + } + + converted_tools = convert_tools([add_tool, tool_schema_noparam]) + assert len(converted_tools) == 2 + assert isinstance(converted_tools[0].function, Tool.Function) + assert isinstance(converted_tools[0].function.parameters, Tool.Function.Parameters) + assert converted_tools[0].function.parameters.properties is not None + assert converted_tools[0].function.name == add_tool.name + assert converted_tools[0].function.parameters.properties["y"].type == "integer" + + # test it defaults to string + assert isinstance(converted_tools[1].function, Tool.Function) + assert isinstance(converted_tools[1].function.parameters, Tool.Function.Parameters) + assert converted_tools[1].function.parameters.properties is not None + assert converted_tools[1].function.name == "manual_tool" + assert converted_tools[1].function.parameters.properties["param_with_type"].type == "integer" + assert converted_tools[1].function.parameters.properties["param_without_type"].type == "string" + assert converted_tools[1].function.parameters.required == ["param_with_type"] + + @pytest.mark.asyncio async def test_create_stream_tools(monkeypatch: pytest.MonkeyPatch) -> None: def add(x: int, y: int) -> str: