Python: [BREAKING] Replace Hosted*Tool classes with tool methods (#3634)

* Replace Hosted*Tool classes with client static factory methods

* fixed failing test

* mypy fix

* mypy fix 2

* declarative mypy fix

* addressed comments

* ToolProtocol removal

* fixed test

* agents mypy fix

* fix failing tests

* mypy fix

* addressed comments

* fixed tests

* addressed comments + added factory method overrides for azureai v2 client

* mypy fix

* added kwargs to azureai tool methods

* fixed in test

* _sessions fix

* test fix
This commit is contained in:
Giles Odigwe
2026-02-11 00:04:27 +00:00
committed by GitHub
parent d249473a6d
commit 7a88af0aef
133 changed files with 3018 additions and 2650 deletions
@@ -9,7 +9,6 @@ from collections.abc import (
Awaitable,
Callable,
Mapping,
MutableMapping,
Sequence,
)
from itertools import chain
@@ -26,10 +25,8 @@ from agent_framework import (
FunctionInvocationConfiguration,
FunctionInvocationLayer,
FunctionTool,
HostedWebSearchTool,
Message,
ResponseStream,
ToolProtocol,
UsageDetails,
get_logger,
)
@@ -343,7 +340,7 @@ class OllamaChatClient(
self.model_id = ollama_settings.model_id
self.client = client or AsyncClient(host=ollama_settings.host)
# Save Host URL for serialization with to_dict()
self.host = str(self.client._client.base_url)
self.host = str(self.client._client.base_url) # pyright: ignore[reportUnknownMemberType,reportPrivateUsage,reportUnknownArgumentType]
super().__init__(
middleware=middleware,
@@ -559,21 +556,22 @@ class OllamaChatClient(
resp.append(fcc)
return resp
def _prepare_tools_for_ollama(self, tools: list[ToolProtocol | MutableMapping[str, Any]]) -> list[dict[str, Any]]:
chat_tools: list[dict[str, Any]] = []
def _prepare_tools_for_ollama(self, tools: list[Any]) -> list[Any]:
"""Prepare tools for the Ollama API.
Converts FunctionTool to JSON schema format. All other tools pass through unchanged.
Args:
tools: List of tools to prepare.
Returns:
List of tool definitions ready for the Ollama API.
"""
chat_tools: list[Any] = []
for tool in tools:
if isinstance(tool, ToolProtocol):
match tool:
case FunctionTool():
chat_tools.append(tool.to_json_schema_spec())
case HostedWebSearchTool():
raise ServiceInvalidRequestError("HostedWebSearchTool is not supported by the Ollama client.")
case _:
raise ServiceInvalidRequestError(
"Unsupported tool type '"
f"{type(tool).__name__}"
"' for Ollama client. Supported tool types: FunctionTool."
)
if isinstance(tool, FunctionTool):
chat_tools.append(tool.to_json_schema_spec())
else:
chat_tools.append(tool if isinstance(tool, dict) else dict(tool))
# Pass through all other tools unchanged
chat_tools.append(tool)
return chat_tools
@@ -10,7 +10,6 @@ from agent_framework import (
BaseChatClient,
ChatResponseUpdate,
Content,
HostedWebSearchTool,
Message,
chat_middleware,
tool,
@@ -384,27 +383,30 @@ async def test_cmc_streaming_with_tool_call(
assert text_result.text == "test"
async def test_cmc_with_hosted_tool_call(
@patch.object(AsyncClient, "chat", new_callable=AsyncMock)
async def test_cmc_with_dict_tool_passthrough(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[Message],
mock_chat_completion_response: OllamaChatResponse,
) -> None:
with pytest.raises(ServiceInvalidRequestError):
additional_properties = {
"user_location": {
"country": "US",
"city": "Seattle",
}
}
"""Test that dict-based tools are passed through to Ollama."""
mock_chat.return_value = mock_chat_completion_response
chat_history.append(Message(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
await ollama_client.get_response(
messages=chat_history,
options={
"tools": [{"type": "function", "function": {"name": "custom_tool", "parameters": {}}}],
},
)
ollama_client = OllamaChatClient()
await ollama_client.get_response(
messages=chat_history,
options={
"tools": HostedWebSearchTool(additional_properties=additional_properties),
},
)
# Verify the tool was passed through to the Ollama client
mock_chat.assert_called_once()
call_kwargs = mock_chat.call_args.kwargs
assert "tools" in call_kwargs
assert call_kwargs["tools"] == [{"type": "function", "function": {"name": "custom_tool", "parameters": {}}}]
@patch.object(AsyncClient, "chat", new_callable=AsyncMock)