mirror of
https://github.com/microsoft/autogen.git
synced 2025-06-26 22:30:10 +00:00
fix: ollama fails when tools use optional args (#6343)
## Why are these changes needed? `convert_tools` failed if Optional args were used in tools (the `type` field doesn't exist in that case and `anyOf` must be used). This uses the `anyOf` field to pick the first non-null type to use. ## Related issue number Fixes #6323 ## Checks - [ ] I've included any doc changes needed for <https://microsoft.github.io/autogen/>. See <https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to build and test documentation locally. - [x] I've added tests (if relevant) corresponding to the changes introduced in this PR. - [x] I've made sure all auto checks have passed. --------- Signed-off-by: Peter Jausovec <peter.jausovec@solo.io> Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
parent
89d77c77c5
commit
d051da52c3
@ -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(
|
||||
|
@ -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 @@ from autogen_core.models import (
|
||||
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 test_create_tools(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user