Files
agent-framework/python/samples/getting_started/tools
T
Eduard van Valkenburg 838a7fd61d Python: [BREAKING] Types API Review improvements (#3647)
* Replace Role and FinishReason classes with NewType + Literal

- Remove EnumLike metaclass from _types.py
- Replace Role class with NewType('Role', str) + RoleLiteral
- Replace FinishReason class with NewType('FinishReason', str) + FinishReasonLiteral
- Update all usages across codebase to use string literals
- Remove .value access patterns (direct string comparison now works)
- Add backward compatibility for legacy dict serialization format
- Update tests to reflect new string-based types

Addresses #3591, #3615

* Simplify ChatResponse and AgentResponse type hints (#3592)

- Remove overloads from ChatResponse.__init__
- Remove text parameter from ChatResponse.__init__
- Remove | dict[str, Any] from finish_reason and usage_details params
- Remove **kwargs from AgentResponse.__init__
- Both now accept ChatMessage | Sequence[ChatMessage] | None for messages
- Update docstrings and examples to reflect changes
- Fix tests that were using removed kwargs
- Fix Role type hint usage in ag-ui utils

* Remove text parameter from ChatResponseUpdate and AgentResponseUpdate (#3597)

- Remove text parameter from ChatResponseUpdate.__init__
- Remove text parameter from AgentResponseUpdate.__init__
- Remove **kwargs from both update classes
- Simplify contents parameter type to Sequence[Content] | None
- Update all usages to use contents=[Content.from_text(...)] pattern
- Fix imports in test files
- Update docstrings and examples

* Rename from_chat_response_updates to from_updates (#3593)

- ChatResponse.from_chat_response_updates โ†’ ChatResponse.from_updates
- ChatResponse.from_chat_response_generator โ†’ ChatResponse.from_update_generator
- AgentResponse.from_agent_run_response_updates โ†’ AgentResponse.from_updates

* Remove try_parse_value method from ChatResponse and AgentResponse (#3595)

- Remove try_parse_value method from ChatResponse
- Remove try_parse_value method from AgentResponse
- Remove try_parse_value calls from from_updates and from_update_generator methods
- Update samples to use try/except with response.value instead
- Update tests to use response.value pattern
- Users should now use response.value with try/except for safe parsing

* Add agent_id to AgentResponse and clarify author_name documentation (#3596)

- Add agent_id parameter to AgentResponse class
- Document that author_name is on ChatMessage objects, not responses
- Update ChatResponse docstring with author_name note
- Update AgentResponse docstring with author_name note

* Simplify ChatMessage.__init__ signature (#3618)

- Make contents a positional argument accepting Sequence[Content | str]
- Auto-convert strings in contents to TextContent
- Remove overloads, keep text kwarg for backward compatibility with serialization
- Update _parse_content_list to handle string items
- Update all usages across codebase to use new format: ChatMessage("role", ["text"])

* Allow Content as input on run and get_response

- Update prepare_messages and normalize_messages to accept Content
- Update type signatures in _agents.py and _clients.py
- Add tests for Content input handling

* Fix ChatMessage usage across packages and samples

Update all remaining ChatMessage(role=..., text=...) to use new
ChatMessage('role', ['text']) signature.

* Fix Role string usage and response format parsing

- Fix redis provider: remove .value access on string literals
- Fix durabletask ensure_response_format: set _response_format before accessing .value

* Fix ollama .value and ai_model_id issues, handle None in content list

- Fix ollama _chat_client: remove .value on string literals
- Fix ollama _chat_client: rename ai_model_id to model_id
- Fix _parse_content_list: skip None values gracefully

* Fix A2AAgent type signature to include Content

* Fix Role/FinishReason NewType dict annotations and improve test coverage to 95%

* Fix mypy errors for Role/FinishReason NewType usage

* Fix Role.TOOL and Role.ASSISTANT usage in _orchestrator_helpers.py

* Fix Role NewType usage in durabletask _models.py
838a7fd61d ยท 2026-02-04 10:13:23 +00:00
History
..

Tools Examples

This folder contains examples demonstrating how to use local tools with the Agent Framework. Local tools allow agents to interact with external systems, perform computations, and execute custom logic.

Note: Several examples set approval_mode="never_require" to keep the samples concise. For production scenarios, keep approval_mode="always_require" unless you are confident in the tool behavior and approval flow. See function_tool_with_approval.py and function_tool_with_approval_and_threads.py for end-to-end approval handling.

Examples

File Description
function_tool_declaration_only.py Demonstrates how to create function declarations without implementations. Useful for testing agent reasoning about tool usage or when tools are defined elsewhere. Shows how agents request tool calls even when the tool won't be executed.
function_tool_from_dict_with_dependency_injection.py Shows how to create local tools from dictionary definitions using dependency injection. The function implementation is injected at runtime during deserialization, enabling dynamic tool creation and configuration. Note: This serialization/deserialization feature is in active development.
function_tool_recover_from_failures.py Demonstrates graceful error handling when tools raise exceptions. Shows how agents receive error information and can recover from failures, deciding whether to retry or respond differently based on the exception.
function_tool_with_approval.py Shows how to implement user approval workflows for function calls without using threads. Demonstrates both streaming and non-streaming approval patterns where users can approve or reject function executions before they run.
function_tool_with_approval_and_threads.py Demonstrates tool approval workflows using threads for automatic conversation history management. Shows how threads simplify approval workflows by automatically storing and retrieving conversation context. Includes both approval and rejection examples.
function_tool_with_kwargs.py Demonstrates how to inject custom arguments (context) into a local tool from the agent's run method. Useful for passing runtime information like access tokens or user IDs that the tool needs but the model shouldn't see.
function_tool_with_thread_injection.py Shows how to access the current thread object inside a local tool via **kwargs.
function_tool_with_max_exceptions.py Shows how to limit the number of times a tool can fail with exceptions using max_invocation_exceptions. Useful for preventing expensive tools from being called repeatedly when they keep failing.
function_tool_with_max_invocations.py Demonstrates limiting the total number of times a tool can be invoked using max_invocations. Useful for rate-limiting expensive operations or ensuring tools are only called a specific number of times per conversation.
tool_in_class.py Shows how to use the tool decorator with class methods to create stateful tools. Demonstrates how class state can control tool behavior dynamically, allowing you to adjust tool functionality at runtime by modifying class properties.

Key Concepts

Local Tool Features

  • Function Declarations: Define tool schemas without implementations for testing or external tools
  • Dependency Injection: Create tools from configurations with runtime-injected implementations
  • Error Handling: Gracefully handle and recover from tool execution failures
  • Approval Workflows: Require user approval before executing sensitive or important operations
  • Invocation Limits: Control how many times tools can be called or fail
  • Stateful Tools: Use class methods as tools to maintain state and dynamically control behavior

Common Patterns

Basic Tool Definition

from agent_framework import tool
from typing import Annotated

@tool(approval_mode="never_require")
def my_tool(param: Annotated[str, "Description"]) -> str:
    """Tool description for the AI."""
    return f"Result: {param}"

Tool with Approval

@tool(approval_mode="always_require")
def sensitive_operation(data: Annotated[str, "Data to process"]) -> str:
    """This requires user approval before execution."""
    return f"Processed: {data}"

Tool with Invocation Limits

@tool(max_invocations=3)
def limited_tool() -> str:
    """Can only be called 3 times total."""
    return "Result"

@tool(max_invocation_exceptions=2)
def fragile_tool() -> str:
    """Can only fail 2 times before being disabled."""
    return "Result"

Stateful Tools with Classes

class MyTools:
    def __init__(self, mode: str = "normal"):
        self.mode = mode

    def process(self, data: Annotated[str, "Data to process"]) -> str:
        """Process data based on current mode."""
        if self.mode == "safe":
            return f"Safely processed: {data}"
        return f"Processed: {data}"

# Create instance and use methods as tools
tools = MyTools(mode="safe")
agent = client.as_agent(tools=tools.process)

# Change behavior dynamically
tools.mode = "normal"

Error Handling

When tools raise exceptions:

  1. The exception is captured and sent to the agent as a function result
  2. The agent receives the error message and can reason about what went wrong
  3. The agent can retry with different parameters, use alternative tools, or explain the issue to the user
  4. With invocation limits, tools can be disabled after repeated failures

Approval Workflows

Two approaches for handling approvals:

  1. Without Threads: Manually manage conversation context, including the query, approval request, and response in each iteration
  2. With Threads: Thread automatically manages conversation history, simplifying the approval workflow

Usage Tips

  • Use declaration-only functions when you want to test agent reasoning without execution
  • Use dependency injection for dynamic tool configuration and plugin architectures
  • Implement approval workflows for operations that modify data, spend money, or require human oversight
  • Set invocation limits to prevent runaway costs or infinite loops with expensive tools
  • Handle exceptions gracefully to create robust agents that can recover from failures
  • Use class-based tools when you need to maintain state or dynamically adjust tool behavior at runtime

Running the Examples

Each example is a standalone Python script that can be run directly:

uv run python function_tool_with_approval.py

Make sure you have the necessary environment variables configured (like OPENAI_API_KEY or Azure credentials) before running the examples.