Python: [BREAKING] changed AIFunction to FunctionTool and @ai_function to @tool (#3413)

* changed AIFunction to FunctionTool and @ai_function to @tool

* test and mypy fixes

* mypy fix

* switch function tool to always_require

* fix noop

* fix github copilot imports

* test fixes

* fix ollama test

* fixes for tests

* fix tests

* reverted change to always_require and extended timeout

* fix test
This commit is contained in:
Eduard van Valkenburg
2026-01-28 15:53:53 +01:00
committed by GitHub
Unverified
parent 15b43f2abe
commit a7d924a7d2
255 changed files with 1202 additions and 1290 deletions
@@ -12,12 +12,12 @@ from typing import TYPE_CHECKING, Any, Generic, cast
import httpx
from agent_framework import (
AIFunction,
BaseChatClient,
ChatMessage,
ChatResponse,
ChatResponseUpdate,
Content,
FunctionTool,
use_chat_middleware,
use_function_invocation,
)
@@ -239,7 +239,7 @@ class AGUIChatClient(BaseChatClient[TAGUIChatOptions], Generic[TAGUIChatOptions]
if any(getattr(tool, "name", None) == tool_name for tool in config.additional_tools):
return
placeholder: AIFunction[Any, Any] = AIFunction(
placeholder: FunctionTool[Any, Any] = FunctionTool(
name=tool_name,
description="Server-managed tool placeholder (AG-UI)",
func=None,
@@ -10,7 +10,7 @@ from dataclasses import asdict, is_dataclass
from datetime import date, datetime
from typing import Any
from agent_framework import AgentResponseUpdate, AIFunction, ChatResponseUpdate, Role, ToolProtocol
from agent_framework import AgentResponseUpdate, ChatResponseUpdate, FunctionTool, Role, ToolProtocol
# Role mapping constants
AGUI_TO_FRAMEWORK_ROLE: dict[str, Role] = {
@@ -160,10 +160,10 @@ def make_json_safe(obj: Any) -> Any: # noqa: ANN401
def convert_agui_tools_to_agent_framework(
agui_tools: list[dict[str, Any]] | None,
) -> list[AIFunction[Any, Any]] | None:
"""Convert AG-UI tool definitions to Agent Framework AIFunction declarations.
) -> list[FunctionTool[Any, Any]] | None:
"""Convert AG-UI tool definitions to Agent Framework FunctionTool declarations.
Creates declaration-only AIFunction instances (no executable implementation).
Creates declaration-only FunctionTool instances (no executable implementation).
These are used to tell the LLM about available tools. The actual execution
happens on the client side via @use_function_invocation.
@@ -174,18 +174,18 @@ def convert_agui_tools_to_agent_framework(
agui_tools: List of AG-UI tool definitions with name, description, parameters
Returns:
List of AIFunction declarations, or None if no tools provided
List of FunctionTool declarations, or None if no tools provided
"""
if not agui_tools:
return None
result: list[AIFunction[Any, Any]] = []
result: list[FunctionTool[Any, Any]] = []
for tool_def in agui_tools:
# Create declaration-only AIFunction (func=None means no implementation)
# Create declaration-only FunctionTool (func=None means no implementation)
# When func=None, the declaration_only property returns True,
# which tells @use_function_invocation to return the function call
# without executing it (so it can be sent back to the client)
func: AIFunction[Any, Any] = AIFunction(
func: FunctionTool[Any, Any] = FunctionTool(
name=tool_def.get("name", ""),
description=tool_def.get("description", ""),
func=None, # CRITICAL: Makes declaration_only=True
@@ -229,24 +229,24 @@ def convert_tools_to_agui_format(
results: list[dict[str, Any]] = []
for tool in tool_list:
if isinstance(tool, dict):
for tool_item in tool_list:
if isinstance(tool_item, dict):
# Already in dict format, pass through
results.append(tool) # type: ignore[arg-type]
elif isinstance(tool, AIFunction):
# Convert AIFunction to AG-UI tool format
results.append(tool_item) # type: ignore[arg-type]
elif isinstance(tool_item, FunctionTool):
# Convert FunctionTool to AG-UI tool format
results.append(
{
"name": tool.name,
"description": tool.description,
"parameters": tool.parameters(),
"name": tool_item.name,
"description": tool_item.description,
"parameters": tool_item.parameters(),
}
)
elif callable(tool):
# Convert callable to AIFunction first, then to AG-UI format
from agent_framework import ai_function
elif callable(tool_item):
# Convert callable to FunctionTool first, then to AG-UI format
from agent_framework import tool
ai_func = ai_function(tool)
ai_func = tool(tool_item)
results.append(
{
"name": ai_func.name,
@@ -254,11 +254,11 @@ def convert_tools_to_agui_format(
"parameters": ai_func.parameters(),
}
)
elif isinstance(tool, ToolProtocol):
elif isinstance(tool_item, ToolProtocol):
# Handle other ToolProtocol implementations
# For now, we'll skip non-AIFunction tools as they may not have
# For now, we'll skip non-FunctionTool instances as they may not have
# the parameters() method. This matches .NET behavior which only
# converts AIFunctionDeclaration instances.
# converts FunctionToolDeclaration instances.
continue
return results if results else None