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
@@ -11,6 +11,7 @@ from agent_framework import (
AgentResponse,
AgentRunContext,
FunctionInvocationContext,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -33,6 +34,8 @@ The example shows:
Execution order: Agent middleware (outermost) -> Run middleware (innermost) -> Agent execution
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -12,6 +12,7 @@ from agent_framework import (
ChatResponse,
Role,
chat_middleware,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -35,6 +36,8 @@ The example covers:
- Middleware registration at run level (applies to specific run only)
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -14,6 +14,7 @@ from agent_framework import (
FunctionInvocationContext,
FunctionMiddleware,
Role,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -33,6 +34,8 @@ This approach is useful when you need stateful middleware or complex logic that
from object-oriented design patterns.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -6,6 +6,7 @@ import datetime
from agent_framework import (
agent_middleware,
function_middleware,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -40,6 +41,8 @@ Key benefits of decorator approach:
- Prevents type mismatches
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_current_time() -> str:
"""Get the current time."""
@@ -5,6 +5,7 @@ from collections.abc import Awaitable, Callable
from typing import Annotated
from agent_framework import FunctionInvocationContext
from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -23,7 +24,8 @@ The middleware catches TimeoutError from an unstable data service and replaces i
a helpful message for the user, preventing raw exceptions from reaching the end user.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def unstable_data_service(
query: Annotated[str, Field(description="The data query to execute.")],
) -> str:
@@ -9,6 +9,7 @@ from typing import Annotated
from agent_framework import (
AgentRunContext,
FunctionInvocationContext,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -29,6 +30,8 @@ lightweight approach compared to class-based middleware. Both agent and function
can be implemented as async functions that accept context and next parameters.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -11,6 +11,7 @@ from agent_framework import (
AgentRunContext,
ChatMessage,
Role,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -28,6 +29,8 @@ The example includes:
This is useful for implementing security checks, rate limiting, or early exit conditions.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -12,6 +12,7 @@ from agent_framework import (
ChatMessage,
Role,
TextContent,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -34,6 +35,8 @@ then replaces its result with a custom "perfect weather" message. For streaming
it creates a custom async generator that yields the override message in chunks.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -4,7 +4,7 @@ import asyncio
from collections.abc import Awaitable, Callable
from typing import Annotated
from agent_framework import FunctionInvocationContext, ai_function, function_middleware
from agent_framework import FunctionInvocationContext, tool, function_middleware
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
@@ -81,7 +81,8 @@ class SessionContextContainer:
runtime_context = SessionContextContainer()
@ai_function
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
async def send_email(
to: Annotated[str, Field(description="Recipient email address")],
subject: Annotated[str, Field(description="Email subject line")],
@@ -112,7 +113,7 @@ async def send_email(
return f"Email sent to {to} from user {user_id} (tenant: {tenant}). Subject: '{subject}'"
@ai_function
@tool(approval_mode="never_require")
async def send_notification(
message: Annotated[str, Field(description="Notification message to send")],
priority: Annotated[str, Field(description="Priority level: low, medium, high")] = "medium",
@@ -241,7 +242,7 @@ async def pattern_1_single_agent_with_closure() -> None:
# Create tools for sub-agents (these will use kwargs propagation)
@ai_function
@tool(approval_mode="never_require")
async def send_email_v2(
to: Annotated[str, Field(description="Recipient email")],
subject: Annotated[str, Field(description="Subject")],
@@ -253,7 +254,7 @@ async def send_email_v2(
return f"Email sent to {to} with subject '{subject}'"
@ai_function
@tool(approval_mode="never_require")
async def send_sms(
phone: Annotated[str, Field(description="Phone number")],
message: Annotated[str, Field(description="SMS message")],
@@ -377,7 +378,7 @@ class AuthContextMiddleware:
await next(context)
@ai_function
@tool(approval_mode="never_require")
async def protected_operation(operation: Annotated[str, Field(description="Operation to perform")]) -> str:
"""Protected operation that requires authentication."""
return f"Executed protected operation: {operation}"
@@ -7,6 +7,7 @@ from typing import Annotated
from agent_framework import (
FunctionInvocationContext,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -25,6 +26,8 @@ The example includes:
This approach shows how middleware can work together by sharing state within the same class instance.
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
@@ -33,6 +36,7 @@ def get_weather(
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
@tool(approval_mode="never_require")
def get_time(
timezone: Annotated[str, Field(description="The timezone to get the time for.")] = "UTC",
@@ -7,6 +7,7 @@ from typing import Annotated
from agent_framework import (
AgentRunContext,
ChatMessageStore,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -30,6 +31,8 @@ Key behaviors demonstrated:
4. After next(): thread contains full conversation history (all previous + current messages)
"""
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],