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
@@ -45,7 +45,7 @@ Once comfortable with these, explore the rest of the samples below.
| Workflow as Agent (Reflection Pattern) | [agents/workflow_as_agent_reflection_pattern.py](./agents/workflow_as_agent_reflection_pattern.py) | Wrap a workflow so it can behave like an agent (reflection pattern) |
| Workflow as Agent + HITL | [agents/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability |
| Workflow as Agent with Thread | [agents/workflow_as_agent_with_thread.py](./agents/workflow_as_agent_with_thread.py) | Use AgentThread to maintain conversation history across workflow-as-agent invocations |
| Workflow as Agent kwargs | [agents/workflow_as_agent_kwargs.py](./agents/workflow_as_agent_kwargs.py) | Pass custom context (data, user tokens) via kwargs through workflow.as_agent() to @ai_function tools |
| Workflow as Agent kwargs | [agents/workflow_as_agent_kwargs.py](./agents/workflow_as_agent_kwargs.py) | Pass custom context (data, user tokens) via kwargs through workflow.as_agent() to @tool functions |
| Handoff Workflow as Agent | [agents/handoff_workflow_as_agent.py](./agents/handoff_workflow_as_agent.py) | Use a HandoffBuilder workflow as an agent with HITL via FunctionCallContent/FunctionResultContent |
### checkpoint
@@ -91,7 +91,7 @@ Once comfortable with these, explore the rest of the samples below.
### tool-approval
Tool approval samples demonstrate using `@ai_function(approval_mode="always_require")` to gate sensitive tool executions with human approval. These work with the high-level builder APIs.
Tool approval samples demonstrate using `@tool(approval_mode="always_require")` to gate sensitive tool executions with human approval. These work with the high-level builder APIs.
| Sample | File | Concepts |
|---|---|---|
@@ -148,7 +148,7 @@ to configure which agents can route to which others with a fluent, type-safe API
| Sample | File | Concepts |
|---|---|---|
| Shared States | [state-management/shared_states_with_agents.py](./state-management/shared_states_with_agents.py) | Store in shared state once and later reuse across agents |
| Workflow Kwargs (Custom Context) | [state-management/workflow_kwargs.py](./state-management/workflow_kwargs.py) | Pass custom context (data, user tokens) via kwargs to `@ai_function` tools |
| Workflow Kwargs (Custom Context) | [state-management/workflow_kwargs.py](./state-management/workflow_kwargs.py) | Pass custom context (data, user tokens) via kwargs to `@tool` tools |
### visualization
@@ -8,6 +8,7 @@ from agent_framework import (
WorkflowContext,
executor,
handler,
tool,
)
from typing_extensions import Never
@@ -13,6 +13,7 @@ from agent_framework import (
WorkflowRunState,
WorkflowStatusEvent,
handler,
tool,
)
from agent_framework._workflows._events import WorkflowOutputEvent
from agent_framework.azure import AzureOpenAIChatClient
@@ -11,6 +11,7 @@ from agent_framework import (
WorkflowOutputEvent,
executor,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowContext,
WorkflowOutputEvent,
executor,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -22,6 +22,7 @@ from agent_framework import (
WorkflowOutputEvent,
handler,
response_handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -49,6 +50,8 @@ Prerequisites:
- Authentication via azure-identity. Run `az login` before executing.
"""
# 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 fetch_product_brief(
product_name: Annotated[str, Field(description="Product name to look up.")],
@@ -65,6 +68,7 @@ def fetch_product_brief(
}
return briefs.get(product_name.lower(), f"No stored brief for '{product_name}'.")
@tool(approval_mode="never_require")
def get_brand_voice_profile(
voice_name: Annotated[str, Field(description="Brand or campaign voice to emulate.")],
@@ -9,6 +9,7 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -13,7 +13,7 @@ from agent_framework import (
HandoffBuilder,
Role,
WorkflowAgent,
ai_function,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -38,19 +38,20 @@ Key Concepts:
"""
@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")
def process_refund(order_number: Annotated[str, "Order number to process refund for"]) -> str:
"""Simulated function to process a refund for a given order number."""
return f"Refund processed successfully for order {order_number}."
@ai_function
@tool(approval_mode="never_require")
def check_order_status(order_number: Annotated[str, "Order number to check status for"]) -> str:
"""Simulated function to check the status of a given order number."""
return f"Order {order_number} is currently being processed and will ship in 2 business days."
@ai_function
@tool(approval_mode="never_require")
def process_return(order_number: Annotated[str, "Order number to process return for"]) -> str:
"""Simulated function to process a return for a given order number."""
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
@@ -6,6 +6,7 @@ from agent_framework import (
ChatAgent,
HostedCodeInterpreterTool,
MagenticBuilder,
tool,
)
from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient
@@ -11,6 +11,7 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -26,6 +26,7 @@ from agent_framework import ( # noqa: E402
WorkflowContext,
handler,
response_handler,
tool,
)
from getting_started.workflows.agents.workflow_as_agent_reflection_pattern import ( # noqa: E402
ReviewRequest,
@@ -4,22 +4,22 @@ import asyncio
import json
from typing import Annotated, Any
from agent_framework import SequentialBuilder, ai_function
from agent_framework import SequentialBuilder, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
"""
Sample: Workflow as Agent with kwargs Propagation to @ai_function Tools
Sample: Workflow as Agent with kwargs Propagation to @tool Tools
This sample demonstrates how to flow custom context (skill data, user tokens, etc.)
through a workflow exposed via .as_agent() to @ai_function tools using the **kwargs pattern.
through a workflow exposed via .as_agent() to @tool functions using the **kwargs pattern.
Key Concepts:
- Build a workflow using SequentialBuilder (or any builder pattern)
- Expose the workflow as a reusable agent via workflow.as_agent()
- Pass custom context as kwargs when invoking workflow_agent.run() or run_stream()
- kwargs are stored in SharedState and propagated to all agent invocations
- @ai_function tools receive kwargs via **kwargs parameter
- @tool functions receive kwargs via **kwargs parameter
When to use workflow.as_agent():
- To treat an entire workflow orchestration as a single agent
@@ -32,7 +32,8 @@ Prerequisites:
# Define tools that accept custom context via **kwargs
@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")
def get_user_data(
query: Annotated[str, Field(description="What user data to retrieve")],
**kwargs: Any,
@@ -49,7 +50,7 @@ def get_user_data(
return f"Retrieved data for user {user_name} with {access_level} access: {query}"
@ai_function
@tool(approval_mode="never_require")
def call_api(
endpoint_name: Annotated[str, Field(description="Name of the API endpoint to call")],
**kwargs: Any,
@@ -95,7 +96,7 @@ async def main() -> None:
# Expose the workflow as an agent using .as_agent()
workflow_agent = workflow.as_agent(name="WorkflowAgent")
# Define custom context that will flow to ai_functions via kwargs
# Define custom context that will flow to tools via kwargs
custom_data = {
"api_config": {
"base_url": "https://api.example.com",
@@ -119,7 +120,7 @@ async def main() -> None:
print("Workflow Agent Execution (watch for [tool_name] logs showing kwargs received):")
print("-" * 70)
# Run workflow agent with kwargs - these will flow through to ai_functions
# Run workflow agent with kwargs - these will flow through to tools
# Note: kwargs are passed to workflow_agent.run_stream() just like workflow.run_stream()
print("\n===== Streaming Response =====")
async for update in workflow_agent.run_stream(
@@ -15,6 +15,7 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
handler,
tool,
)
from agent_framework.openai import OpenAIChatClient
from pydantic import BaseModel
@@ -26,6 +26,7 @@ from agent_framework import (
get_checkpoint_summary,
handler,
response_handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -37,6 +37,7 @@ from agent_framework import (
WorkflowContext,
WorkflowOutputEvent,
handler,
tool,
)
@@ -17,7 +17,7 @@ from agent_framework import (
Workflow,
WorkflowOutputEvent,
WorkflowStatusEvent,
ai_function,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -51,7 +51,7 @@ CHECKPOINT_DIR = Path(__file__).parent / "tmp" / "handoff_checkpoints"
CHECKPOINT_DIR.mkdir(parents=True, exist_ok=True)
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
def submit_refund(refund_description: str, amount: str, order_id: str) -> str:
"""Capture a refund request for manual review before processing."""
return f"refund recorded for order {order_id} (amount: {amount}) with details: {refund_description}"
@@ -24,6 +24,7 @@ from agent_framework import (
WorkflowStatusEvent,
handler,
response_handler,
tool,
)
CHECKPOINT_DIR = Path(__file__).with_suffix("").parent / "tmp" / "sub_workflow_checkpoints"
@@ -31,6 +31,7 @@ from agent_framework import (
ChatMessageStore,
InMemoryCheckpointStorage,
SequentialBuilder,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -10,6 +10,7 @@ from agent_framework import (
WorkflowContext,
WorkflowExecutor,
handler,
tool,
)
from typing_extensions import Never
@@ -9,7 +9,7 @@ from agent_framework import (
SequentialBuilder,
WorkflowExecutor,
WorkflowOutputEvent,
ai_function,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -32,7 +32,8 @@ Prerequisites:
# Define tools that access custom context via **kwargs
@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")
def get_authenticated_data(
resource: Annotated[str, "The resource to fetch"],
**kwargs: Any,
@@ -48,7 +49,7 @@ def get_authenticated_data(
return f"Fetched '{resource}' for user {user_name} ({access_level} access)"
@ai_function
@tool(approval_mode="never_require")
def call_configured_service(
service_name: Annotated[str, "Name of the service to call"],
**kwargs: Any,
@@ -16,6 +16,7 @@ from agent_framework import (
WorkflowExecutor,
handler,
response_handler,
tool,
)
from typing_extensions import Never
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowOutputEvent,
handler,
response_handler,
tool,
)
from typing_extensions import Never
@@ -13,6 +13,7 @@ from agent_framework import ( # Core chat primitives used to build requests
WorkflowBuilder, # Fluent builder for wiring executors and edges
WorkflowContext, # Per-run context and event bus
executor, # Decorator to declare a Python function as a workflow executor
tool,
)
from agent_framework.azure import AzureOpenAIChatClient # Thin client wrapper for Azure OpenAI chat models
from azure.identity import AzureCliCredential # Uses your az CLI login for credentials
@@ -19,6 +19,7 @@ from agent_framework import (
WorkflowEvent,
WorkflowOutputEvent,
executor,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -9,6 +9,7 @@ from agent_framework import (
WorkflowContext,
WorkflowOutputEvent,
handler,
tool,
)
from typing_extensions import Never
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -17,6 +17,7 @@ from agent_framework import ( # Core chat primitives used to form LLM requests
WorkflowBuilder, # Fluent builder for assembling the graph
WorkflowContext, # Per-run context and event bus
executor, # Decorator to turn a function into a workflow executor
tool,
)
from agent_framework.azure import AzureOpenAIChatClient # Thin client for Azure OpenAI chat models
from azure.identity import AzureCliCredential # Uses your az CLI login for credentials
@@ -11,6 +11,7 @@ from pathlib import Path
from typing import Annotated, Any
from agent_framework import FileCheckpointStorage, RequestInfoEvent, WorkflowOutputEvent
from agent_framework import tool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_declarative import ExternalInputRequest, ExternalInputResponse, WorkflowFactory
from azure.identity import AzureCliCredential
@@ -37,17 +38,18 @@ MENU_ITEMS = [
MenuItem(category="Drink", name="Soda", price=1.95, is_special=False),
]
# 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_menu() -> list[dict[str, Any]]:
"""Get all menu items."""
return [{"category": i.category, "name": i.name, "price": i.price} for i in MENU_ITEMS]
@tool(approval_mode="never_require")
def get_specials() -> list[dict[str, Any]]:
"""Get today's specials."""
return [{"category": i.category, "name": i.name, "price": i.price} for i in MENU_ITEMS if i.is_special]
@tool(approval_mode="never_require")
def get_item_price(name: Annotated[str, Field(description="Menu item name")]) -> str:
"""Get price of a menu item."""
for item in MENU_ITEMS:
@@ -14,7 +14,7 @@ from agent_framework import (
FunctionApprovalResponseContent,
WorkflowBuilder,
WorkflowContext,
ai_function,
tool,
executor,
handler,
)
@@ -53,14 +53,15 @@ Prerequisites:
"""
@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")
def get_current_date() -> str:
"""Get the current date in YYYY-MM-DD format."""
# For demonstration purposes, we return a fixed date.
return "2025-11-07"
@ai_function
@tool(approval_mode="never_require")
def get_team_members_email_addresses() -> list[dict[str, str]]:
"""Get the email addresses of team members."""
# In a real implementation, this might query a database or directory service.
@@ -92,7 +93,7 @@ def get_team_members_email_addresses() -> list[dict[str, str]]:
]
@ai_function
@tool(approval_mode="never_require")
def get_my_information() -> dict[str, str]:
"""Get my personal information."""
return {
@@ -103,7 +104,7 @@ def get_my_information() -> dict[str, str]:
}
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
async def read_historical_email_data(
email_address: Annotated[str, "The email address to read historical data from"],
start_date: Annotated[str, "The start date in YYYY-MM-DD format"],
@@ -165,7 +166,7 @@ async def read_historical_email_data(
return [email for email in emails if start_date <= email["date"] <= end_date]
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
async def send_email(
to: Annotated[str, "The recipient email address"],
subject: Annotated[str, "The email subject"],
@@ -33,6 +33,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework._workflows._agent_executor import AgentExecutorResponse
from agent_framework.azure import AzureOpenAIChatClient
@@ -35,6 +35,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -18,6 +18,7 @@ from agent_framework import (
WorkflowStatusEvent, # Event emitted on run state changes
handler,
response_handler, # Decorator to expose an Executor method as a step
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -32,6 +32,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -11,6 +11,7 @@ from agent_framework import (
WorkflowContext,
WorkflowOutputEvent,
handler,
tool,
)
from typing_extensions import Never
@@ -12,6 +12,7 @@ from agent_framework import (
Executor,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -12,6 +12,7 @@ from agent_framework import (
Workflow,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -9,6 +9,7 @@ from agent_framework import (
GroupChatBuilder,
Role,
WorkflowOutputEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -11,6 +11,7 @@ from agent_framework import (
GroupChatBuilder,
Role,
WorkflowOutputEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -9,6 +9,7 @@ from agent_framework import (
GroupChatBuilder,
GroupChatState,
WorkflowOutputEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowEvent,
WorkflowOutputEvent,
resolve_agent_id,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -18,7 +18,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
ai_function,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -47,19 +47,20 @@ Key Concepts:
"""
@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")
def process_refund(order_number: Annotated[str, "Order number to process refund for"]) -> str:
"""Simulated function to process a refund for a given order number."""
return f"Refund processed successfully for order {order_number}."
@ai_function
@tool(approval_mode="never_require")
def check_order_status(order_number: Annotated[str, "Order number to check status for"]) -> str:
"""Simulated function to check the status of a given order number."""
return f"Order {order_number} is currently being processed and will ship in 2 business days."
@ai_function
@tool(approval_mode="never_require")
def process_return(order_number: Annotated[str, "Order number to process return for"]) -> str:
"""Simulated function to process a return for a given order number."""
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
@@ -16,7 +16,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
ai_function,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -38,19 +38,20 @@ Key Concepts:
"""
@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")
def process_refund(order_number: Annotated[str, "Order number to process refund for"]) -> str:
"""Simulated function to process a refund for a given order number."""
return f"Refund processed successfully for order {order_number}."
@ai_function
@tool(approval_mode="never_require")
def check_order_status(order_number: Annotated[str, "Order number to check status for"]) -> str:
"""Simulated function to check the status of a given order number."""
return f"Order {order_number} is currently being processed and will ship in 2 business days."
@ai_function
@tool(approval_mode="never_require")
def process_return(order_number: Annotated[str, "Order number to process return for"]) -> str:
"""Simulated function to process a return for a given order number."""
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
@@ -41,6 +41,7 @@ from agent_framework import (
WorkflowEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from azure.identity.aio import AzureCliCredential
@@ -15,6 +15,7 @@ from agent_framework import (
MagenticOrchestratorEvent,
MagenticProgressLedger,
WorkflowOutputEvent,
tool,
)
from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient
@@ -16,6 +16,7 @@ from agent_framework import (
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity._credentials import AzureCliCredential
@@ -12,6 +12,7 @@ from agent_framework import (
MagenticPlanReviewRequest,
RequestInfoEvent,
WorkflowOutputEvent,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -11,6 +11,7 @@ from agent_framework import (
SequentialBuilder,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -11,6 +11,7 @@ from agent_framework import (
Workflow,
WorkflowContext,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -16,6 +16,7 @@ from agent_framework import ( # Core chat primitives to build LLM requests
WorkflowContext, # Per run context and event bus
WorkflowOutputEvent, # Event emitted when workflow yields output
handler, # Decorator to mark an Executor method as invokable
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential # Uses your az CLI login for credentials
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowOutputEvent, # Event emitted when workflow yields output
WorkflowViz, # Utility to visualize a workflow graph
handler, # Decorator to expose an Executor method as a step
tool,
)
from typing_extensions import Never
@@ -15,6 +15,7 @@ from agent_framework import (
WorkflowBuilder,
WorkflowContext,
executor,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -4,20 +4,20 @@ import asyncio
import json
from typing import Annotated, Any
from agent_framework import ChatMessage, SequentialBuilder, WorkflowOutputEvent, ai_function
from agent_framework import ChatMessage, SequentialBuilder, WorkflowOutputEvent, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
"""
Sample: Workflow kwargs Flow to @ai_function Tools
Sample: Workflow kwargs Flow to @tool Tools
This sample demonstrates how to flow custom context (skill data, user tokens, etc.)
through any workflow pattern to @ai_function tools using the **kwargs pattern.
through any workflow pattern to @tool functions using the **kwargs pattern.
Key Concepts:
- Pass custom context as kwargs when invoking workflow.run_stream() or workflow.run()
- kwargs are stored in SharedState and passed to all agent invocations
- @ai_function tools receive kwargs via **kwargs parameter
- @tool functions receive kwargs via **kwargs parameter
- Works with Sequential, Concurrent, GroupChat, Handoff, and Magentic patterns
Prerequisites:
@@ -26,7 +26,8 @@ Prerequisites:
# Define tools that accept custom context via **kwargs
@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")
def get_user_data(
query: Annotated[str, Field(description="What user data to retrieve")],
**kwargs: Any,
@@ -43,7 +44,7 @@ def get_user_data(
return f"Retrieved data for user {user_name} with {access_level} access: {query}"
@ai_function
@tool(approval_mode="never_require")
def call_api(
endpoint_name: Annotated[str, Field(description="Name of the API endpoint to call")],
**kwargs: Any,
@@ -86,7 +87,7 @@ async def main() -> None:
# Build a simple sequential workflow
workflow = SequentialBuilder().participants([agent]).build()
# Define custom context that will flow to ai_functions via kwargs
# Define custom context that will flow to tools via kwargs
custom_data = {
"api_config": {
"base_url": "https://api.example.com",
@@ -110,7 +111,7 @@ async def main() -> None:
print("Workflow Execution (watch for [tool_name] logs showing kwargs received):")
print("-" * 70)
# Run workflow with kwargs - these will flow through to ai_functions
# Run workflow with kwargs - these will flow through to tools
async for event in workflow.run_stream(
"Please get my user data and then call the users API endpoint.",
custom_data=custom_data,
@@ -10,7 +10,7 @@ from agent_framework import (
FunctionApprovalResponseContent,
RequestInfoEvent,
WorkflowOutputEvent,
ai_function,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -45,7 +45,8 @@ Prerequisites:
# 1. Define market data tools (no approval required)
@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")
def get_stock_price(symbol: Annotated[str, "The stock ticker symbol"]) -> str:
"""Get the current stock price for a given symbol."""
# Mock data for demonstration
@@ -54,7 +55,7 @@ def get_stock_price(symbol: Annotated[str, "The stock ticker symbol"]) -> str:
return f"{symbol.upper()}: ${price:.2f}"
@ai_function
@tool(approval_mode="never_require")
def get_market_sentiment(symbol: Annotated[str, "The stock ticker symbol"]) -> str:
"""Get market sentiment analysis for a stock."""
# Mock sentiment data
@@ -68,7 +69,7 @@ def get_market_sentiment(symbol: Annotated[str, "The stock ticker symbol"]) -> s
# 2. Define trading tools (approval required)
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
def execute_trade(
symbol: Annotated[str, "The stock ticker symbol"],
action: Annotated[str, "Either 'buy' or 'sell'"],
@@ -78,7 +79,7 @@ def execute_trade(
return f"Trade executed: {action.upper()} {quantity} shares of {symbol.upper()}"
@ai_function
@tool(approval_mode="never_require")
def get_portfolio_balance() -> str:
"""Get current portfolio balance and available funds."""
return "Portfolio: $50,000 invested, $10,000 cash available. Holdings: AAPL, GOOGL, MSFT."
@@ -10,7 +10,7 @@ from agent_framework import (
GroupChatRequestSentEvent,
GroupChatState,
RequestInfoEvent,
ai_function,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -44,19 +44,20 @@ Prerequisites:
# 1. Define tools for different agents
@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")
def run_tests(test_suite: Annotated[str, "Name of the test suite to run"]) -> str:
"""Run automated tests for the application."""
return f"Test suite '{test_suite}' completed: 47 passed, 0 failed, 0 skipped"
@ai_function
@tool(approval_mode="never_require")
def check_staging_status() -> str:
"""Check the current status of the staging environment."""
return "Staging environment: Healthy, Version 2.3.0 deployed, All services running"
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
def deploy_to_production(
version: Annotated[str, "The version to deploy"],
components: Annotated[str, "Comma-separated list of components to deploy"],
@@ -65,7 +66,7 @@ def deploy_to_production(
return f"Production deployment complete: Version {version}, Components: {components}"
@ai_function
@tool(approval_mode="never_require")
def create_rollback_plan(version: Annotated[str, "The version being deployed"]) -> str:
"""Create a rollback plan for the deployment."""
return (
@@ -9,7 +9,7 @@ from agent_framework import (
RequestInfoEvent,
SequentialBuilder,
WorkflowOutputEvent,
ai_function,
tool,
)
from agent_framework.openai import OpenAIChatClient
@@ -17,7 +17,7 @@ from agent_framework.openai import OpenAIChatClient
Sample: Sequential Workflow with Tool Approval Requests
This sample demonstrates how to use SequentialBuilder with tools that require human
approval before execution. The approval flow uses the existing @ai_function decorator
approval before execution. The approval flow uses the existing @tool decorator
with approval_mode="always_require" to trigger human-in-the-loop interactions.
This sample works as follows:
@@ -33,7 +33,7 @@ Show how tool call approvals integrate seamlessly with SequentialBuilder without
requiring any additional builder configuration.
Demonstrate:
- Using @ai_function(approval_mode="always_require") for sensitive operations.
- Using @tool(approval_mode="always_require") for sensitive operations.
- Handling RequestInfoEvent with FunctionApprovalRequestContent in sequential workflows.
- Resuming workflow execution after approval via send_responses_streaming.
@@ -44,7 +44,7 @@ Prerequisites:
# 1. Define tools - one requiring approval, one that doesn't
@ai_function(approval_mode="always_require")
@tool(approval_mode="always_require")
def execute_database_query(
query: Annotated[str, "The SQL query to execute against the production database"],
) -> str:
@@ -53,7 +53,8 @@ def execute_database_query(
return f"Query executed successfully. Results: 3 rows affected by '{query}'"
@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")
def get_database_schema() -> str:
"""Get the current database schema. Does not require approval."""
return """
@@ -14,6 +14,7 @@ from agent_framework import (
WorkflowContext,
WorkflowViz,
handler,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential