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
@@ -19,6 +19,7 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExport
from opentelemetry.semconv._incubating.attributes.service_attributes import SERVICE_NAME
from opentelemetry.trace import set_tracer_provider
from pydantic import Field
from agent_framework import tool
"""
This sample shows how to manually configure to send traces, logs, and metrics to the console,
@@ -64,7 +65,8 @@ def setup_metrics():
# Sets the global default meter provider
set_meter_provider(meter_provider)
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -9,6 +9,7 @@ from agent_framework.openai import OpenAIResponsesClient
from opentelemetry.trace import SpanKind
from opentelemetry.trace.span import format_trace_id
from pydantic import Field
from agent_framework import tool
if TYPE_CHECKING:
from agent_framework import ChatClientProtocol
@@ -38,7 +39,8 @@ You can also set the environment variables instead of passing them as CLI argume
"""
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -5,6 +5,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework.observability import configure_otel_providers, get_tracer
from agent_framework.openai import OpenAIChatClient
from opentelemetry.trace import SpanKind
@@ -16,7 +17,8 @@ This sample shows how you can observe an agent in Agent Framework by using the
same observability setup 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -8,6 +8,7 @@ from typing import Annotated
import dotenv
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework.observability import create_resource, enable_instrumentation, get_tracer
from agent_framework.openai import OpenAIResponsesClient
from azure.ai.projects.aio import AIProjectClient
@@ -31,7 +32,8 @@ dotenv.load_dotenv()
logger = logging.getLogger(__name__)
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -7,6 +7,7 @@ from typing import Annotated
import dotenv
from agent_framework import ChatAgent
from agent_framework import tool
from agent_framework.azure import AzureAIClient
from agent_framework.observability import get_tracer
from azure.ai.projects.aio import AIProjectClient
@@ -28,7 +29,8 @@ for this sample to work.
# For loading the `AZURE_AI_PROJECT_ENDPOINT` environment variable
dotenv.load_dotenv()
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -6,7 +6,7 @@ from contextlib import suppress
from random import randint
from typing import TYPE_CHECKING, Annotated, Literal
from agent_framework import ai_function
from agent_framework import tool
from agent_framework.observability import configure_otel_providers, get_tracer
from agent_framework.openai import OpenAIResponsesClient
from opentelemetry import trace
@@ -28,9 +28,11 @@ output traces, logs, and metrics to the console.
"""
# Define the scenarios that can be run to show the telemetry data collected by the SDK
SCENARIOS = ["chat_client", "chat_client_stream", "ai_function", "all"]
SCENARIOS = ["chat_client", "chat_client_stream", "tool", "all"]
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -78,7 +80,7 @@ async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) ->
print(f"Assistant: {response}")
async def run_ai_function() -> None:
async def run_tool() -> None:
"""Run a AI function.
This function runs a AI function and prints the output.
@@ -90,12 +92,12 @@ async def run_ai_function() -> None:
"""
with get_tracer().start_as_current_span("Scenario: AI Function", kind=trace.SpanKind.CLIENT):
print("Running scenario: AI Function")
func = ai_function(get_weather)
func = tool(get_weather)
weather = await func.invoke(location="Amsterdam")
print(f"Weather in Amsterdam:\n{weather}")
async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_function", "all"] = "all"):
async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "all"] = "all"):
"""Run the selected scenario(s)."""
# This will enable tracing and create the necessary tracing, logging and metrics providers
@@ -108,9 +110,9 @@ async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_functi
client = OpenAIResponsesClient()
# Scenarios where telemetry is collected in the SDK, from the most basic to the most complex.
if scenario == "ai_function" or scenario == "all":
if scenario == "tool" or scenario == "all":
with suppress(Exception):
await run_ai_function()
await run_tool()
if scenario == "chat_client_stream" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=True)
@@ -6,7 +6,7 @@ from contextlib import suppress
from random import randint
from typing import TYPE_CHECKING, Annotated, Literal
from agent_framework import ai_function, setup_logging
from agent_framework import tool, setup_logging
from agent_framework.observability import configure_otel_providers, get_tracer
from agent_framework.openai import OpenAIResponsesClient
from opentelemetry import trace
@@ -28,9 +28,11 @@ Use this approach when you need custom exporter configuration beyond what enviro
"""
# Define the scenarios that can be run to show the telemetry data collected by the SDK
SCENARIOS = ["chat_client", "chat_client_stream", "ai_function", "all"]
SCENARIOS = ["chat_client", "chat_client_stream", "tool", "all"]
# 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 get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -78,7 +80,7 @@ async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) ->
print(f"Assistant: {response}")
async def run_ai_function() -> None:
async def run_tool() -> None:
"""Run a AI function.
This function runs a AI function and prints the output.
@@ -90,12 +92,12 @@ async def run_ai_function() -> None:
"""
with get_tracer().start_as_current_span("Scenario: AI Function", kind=trace.SpanKind.CLIENT):
print("Running scenario: AI Function")
func = ai_function(get_weather)
func = tool(get_weather)
weather = await func.invoke(location="Amsterdam")
print(f"Weather in Amsterdam:\n{weather}")
async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_function", "all"] = "all"):
async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "all"] = "all"):
"""Run the selected scenario(s)."""
# Setup the logging with the more complete format
@@ -137,9 +139,9 @@ async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_functi
client = OpenAIResponsesClient()
# Scenarios where telemetry is collected in the SDK, from the most basic to the most complex.
if scenario == "ai_function" or scenario == "all":
if scenario == "tool" or scenario == "all":
with suppress(Exception):
await run_ai_function()
await run_tool()
if scenario == "chat_client_stream" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=True)
@@ -8,6 +8,7 @@ from agent_framework import (
WorkflowContext,
WorkflowOutputEvent,
handler,
tool,
)
from agent_framework.observability import configure_otel_providers, get_tracer
from opentelemetry.trace import SpanKind