Python: [BREAKING] Simplify API: ChatAgent -> Agent, ChatMessage -> Message (#3747)

* [BREAKING] Rename ChatAgent -> Agent, ChatMessage -> Message, ChatClientProtocol -> SupportsChatGetResponse

Simplify the public API by removing redundant 'Chat' prefix from core types:
- ChatAgent -> Agent
- RawChatAgent -> RawAgent
- ChatMessage -> Message
- ChatClientProtocol -> SupportsChatGetResponse

Also renamed internal WorkflowMessage (was Message in _runner_context) to avoid collision.

No backward compatibility aliases - this is a clean breaking change.

* [BREAKING] Rename Agent chat_client parameter to client

* Fix rebase issues: WorkflowMessage references and broken markdown links

* Fix formatting and lint issues from code quality checks

* Fix import ordering in workflow sample files

* fixed rebase

* Fix test failures: use WorkflowMessage and A2AMessage after ChatMessage→Message rename

- Replace Message(data=..., source_id=...) with WorkflowMessage(...) in workflow tests
- Fix isinstance check in A2A agent to use A2AMessage instead of Message
- Fix import in test_workflow_observability.py (Message→WorkflowMessage)

* Fix lint, fmt, and sample errors after ChatMessage→Message rename

- Auto-fix 70+ ruff lint issues across samples (ChatMessage→Message refs)
- Fix HostedVectorStoreContent→Content.from_hosted_vector_store in file search sample
- Fix _normalize_messages→normalize_messages in custom agent sample
- Fix context.terminate→raise MiddlewareTermination in middleware samples
- Fix with_update_hook→with_transform_hook in override middleware sample
- Add TOptions_co import back to custom_chat_client sample
- Add noqa for FastAPI File() default in chatkit sample
- Fix B023 loop variable capture in weather agent sample

* fix: update Agent constructor calls from chat_client to client in declaration-only tool tests

* fix: add register_cleanup to devui lazy-loading proxy and type stub

* fixed tests and updated new pieces

* fix agui typevar

* fix merge errors

* fix merge conflicts

* fiux merge

* Remove unused links

---------

Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
Eduard van Valkenburg
2026-02-10 23:04:32 +00:00
committed by GitHub
co-authored by Evan Mattson
parent a4c9e43afb
commit 0521f5bed8
418 changed files with 5385 additions and 5389 deletions
@@ -23,7 +23,7 @@ This sample demonstrates the three main methods of AzureAIProjectAgentProvider:
It also shows how to use a single provider instance to spawn multiple agents
with different configurations, which is efficient for multi-agent scenarios.
Each method returns a ChatAgent that can be used for conversations.
Each method returns a Agent that can be used for conversations.
"""
@@ -41,7 +41,7 @@ async def create_agent_example() -> None:
"""Example of using provider.create_agent() to create a new agent.
This method creates a new agent version on the Azure AI service and returns
a ChatAgent. Use this when you want to create a fresh agent with
a Agent. Use this when you want to create a fresh agent with
specific configuration.
"""
print("=== provider.create_agent() Example ===")
@@ -199,7 +199,7 @@ async def multiple_agents_example() -> None:
async def as_agent_example() -> None:
"""Example of using provider.as_agent() to wrap an SDK object without HTTP calls.
This method wraps an existing AgentVersionDetails into a ChatAgent without
This method wraps an existing AgentVersionDetails into a Agent without
making additional HTTP calls. Use this when you already have the full
AgentVersionDetails from a previous SDK operation.
"""
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureAIClient
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import AzureCliCredential
@@ -23,8 +23,8 @@ async def main() -> None:
# Endpoint here should be application endpoint with format:
# /api/projects/<project-name>/applications/<application-name>/protocols
AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project_client,
ChatAgent(
chat_client=AzureAIClient(
Agent(
client=AzureAIClient(
project_client=project_client,
),
) as agent,
@@ -5,9 +5,9 @@ import tempfile
from pathlib import Path
from agent_framework import (
Agent,
AgentResponseUpdate,
Annotation,
ChatAgent,
Content,
HostedCodeInterpreterTool,
)
@@ -33,7 +33,7 @@ QUERY = (
)
async def download_container_files(file_contents: list[Annotation | Content], agent: ChatAgent) -> list[Path]:
async def download_container_files(file_contents: list[Annotation | Content], agent: Agent) -> list[Path]:
"""Download container files using the OpenAI containers API.
Code interpreter generates files in containers, which require both file_id
@@ -45,7 +45,7 @@ async def download_container_files(file_contents: list[Annotation | Content], ag
Args:
file_contents: List of Annotation or Content objects
containing file_id and container_id.
agent: The ChatAgent instance with access to the AzureAIClient.
agent: The Agent instance with access to the AzureAIClient.
Returns:
List of Path objects for successfully downloaded files.
@@ -61,7 +61,7 @@ async def download_container_files(file_contents: list[Annotation | Content], ag
print(f"\nDownloading {len(file_contents)} container file(s) to {output_dir.absolute()}...")
# Access the OpenAI client from AzureAIClient
openai_client = agent.chat_client.client # type: ignore[attr-defined]
openai_client = agent.client.client # type: ignore[attr-defined]
downloaded_files: list[Path] = []
@@ -139,7 +139,7 @@ async def non_streaming_example() -> None:
# Check for annotations in the response
annotations_found: list[Annotation] = []
# AgentResponse has messages property, which contains ChatMessage objects
# AgentResponse has messages property, which contains Message objects
for message in result.messages:
for content in message.contents:
if content.type == "text" and content.annotations:
@@ -44,7 +44,7 @@ async def non_streaming_example() -> None:
# Check for annotations in the response
annotations_found: list[str] = []
# AgentResponse has messages property, which contains ChatMessage objects
# AgentResponse has messages property, which contains Message objects
for message in result.messages:
for content in message.contents:
if content.type == "text" and content.annotations:
@@ -36,7 +36,7 @@ async def using_provider_get_agent() -> None:
)
try:
# Get newly created agent as ChatAgent by using provider.get_agent()
# Get newly created agent as Agent by using provider.get_agent()
provider = AzureAIProjectAgentProvider(project_client=project_client)
agent = await provider.get_agent(name=azure_ai_agent.name)
@@ -3,7 +3,7 @@
import asyncio
from typing import Any
from agent_framework import AgentResponse, AgentThread, ChatMessage, HostedMCPTool, SupportsAgentRun
from agent_framework import AgentResponse, AgentThread, HostedMCPTool, Message, SupportsAgentRun
from agent_framework.azure import AzureAIProjectAgentProvider
from azure.identity.aio import AzureCliCredential
@@ -25,10 +25,10 @@ async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun")
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage("assistant", [user_input_needed]))
new_inputs.append(Message("assistant", [user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage("user", [user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
Message("user", [user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs, store=False)
@@ -48,7 +48,7 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user",
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
)
@@ -8,7 +8,7 @@ All examples in this folder use the `AzureAIAgentsProvider` class which provides
- **`create_agent()`** - Create a new agent on the Azure AI service
- **`get_agent()`** - Retrieve an existing agent by ID or from a pre-fetched Agent object
- **`as_agent()`** - Wrap an SDK Agent object as a ChatAgent without HTTP calls
- **`as_agent()`** - Wrap an SDK Agent object as a Agent without HTTP calls
```python
from agent_framework.azure import AzureAIAgentsProvider
@@ -17,7 +17,7 @@ servers, including user approval workflows for function call security.
async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", thread: "AgentThread") -> AgentResponse:
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query, thread=thread, store=True)
while len(result.user_input_requests) > 0:
@@ -29,7 +29,7 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user",
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
)
@@ -51,7 +51,7 @@ async def mcp_tools_on_agent_level() -> None:
print("=== Tools Defined on Agent Level ===")
# Tools are provided when creating the agent
# The ChatAgent will connect to the MCP server through its context manager
# The Agent will connect to the MCP server through its context manager
# and discover tools at runtime
async with (
AzureCliCredential() as credential,
@@ -45,7 +45,7 @@ def get_time() -> str:
async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", thread: "AgentThread"):
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query, thread=thread, store=True)
while len(result.user_input_requests) > 0:
@@ -57,7 +57,7 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user",
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
)
@@ -6,17 +6,17 @@ This folder contains examples demonstrating different ways to create and use age
| File | Description |
|------|-------------|
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `Agent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `Agent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `Agent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
| [`azure_responses_client_code_interpreter_files.py`](azure_responses_client_code_interpreter_files.py) | Demonstrates using HostedCodeInterpreterTool with file uploads for data analysis. Shows how to create, upload, and analyze CSV files using Python code execution with Azure OpenAI Responses. |
| [`azure_responses_client_image_analysis.py`](azure_responses_client_image_analysis.py) | Shows how to use Azure OpenAI Responses for image analysis and vision tasks. Demonstrates multi-modal messages combining text and image content using remote URLs. |
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import AgentResponseUpdate, ChatAgent, ChatResponseUpdate, HostedCodeInterpreterTool
from agent_framework import Agent, AgentResponseUpdate, ChatResponseUpdate, HostedCodeInterpreterTool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from openai.types.beta.threads.runs import (
@@ -46,8 +46,8 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
) as agent:
@@ -5,7 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential, get_bearer_token_provider
from openai import AsyncAzureOpenAI
@@ -46,8 +46,8 @@ async def main() -> None:
)
try:
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(async_client=client, assistant_id=created_assistant.id),
async with Agent(
client=AzureOpenAIAssistantsClient(async_client=client, assistant_id=created_assistant.id),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -43,8 +43,8 @@ async def tools_on_agent_level() -> None:
# The agent can use these tools for any query during its lifetime
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
) as agent:
@@ -74,8 +74,8 @@ async def tools_on_run_level() -> None:
# Agent created without tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
) as agent:
@@ -105,8 +105,8 @@ async def mixed_tools_example() -> None:
# Agent created with some base tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
) as agent:
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework import Agent, AgentThread, tool
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -33,8 +33,8 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -59,8 +59,8 @@ async def example_with_thread_persistence() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -97,8 +97,8 @@ async def example_with_existing_thread_id() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -117,8 +117,8 @@ async def example_with_existing_thread_id() -> None:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
# Create a new agent instance but use the existing thread ID
async with ChatAgent(
chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
async with Agent(
client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -43,8 +43,8 @@ async def tools_on_agent_level() -> None:
# The agent can use these tools for any query during its lifetime
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -75,8 +75,8 @@ async def tools_on_run_level() -> None:
# Agent created without tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -107,8 +107,8 @@ async def mixed_tools_example() -> None:
# Agent created with some base tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, ChatMessageStore, tool
from agent_framework import Agent, AgentThread, ChatMessageStore, tool
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -33,8 +33,8 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -60,8 +60,8 @@ async def example_with_thread_persistence() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -95,8 +95,8 @@ async def example_with_existing_thread_messages() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -117,8 +117,8 @@ async def example_with_existing_thread_messages() -> None:
print("\n--- Continuing with the same thread in a new agent instance ---")
# Create a new agent instance but use the existing thread with its message history
new_agent = ChatAgent(
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
new_agent = Agent(
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -4,7 +4,7 @@ import asyncio
import os
import tempfile
from agent_framework import ChatAgent, HostedCodeInterpreterTool
from agent_framework import Agent, HostedCodeInterpreterTool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from openai import AsyncAzureOpenAI
@@ -76,8 +76,8 @@ async def main() -> None:
temp_file_path, file_id = await create_sample_file_and_upload(openai_client)
# Create agent using Azure OpenAI Responses client
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=credential),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=credential),
instructions="You are a helpful assistant that can analyze data files using Python code.",
tools=HostedCodeInterpreterTool(inputs=[{"file_id": file_id}]),
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
@@ -24,7 +24,7 @@ async def main():
)
# 2. Create a simple message with both text and image content
user_message = ChatMessage(
user_message = Message(
role="user",
contents=[
Content.from_text(text="What do you see in this image?"),
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, ChatResponse, HostedCodeInterpreterTool
from agent_framework import Agent, ChatResponse, HostedCodeInterpreterTool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from openai.types.responses.response import Response as OpenAIResponse
@@ -22,8 +22,8 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, Content, HostedFileSearchTool
from agent_framework import Agent, Content, HostedFileSearchTool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
@@ -53,8 +53,8 @@ async def main() -> None:
file_id, vector_store = await create_vector_store(client)
agent = ChatAgent(
chat_client=client,
agent = Agent(
client=client,
instructions="You are a helpful assistant that can search through files to find information.",
tools=[HostedFileSearchTool(inputs=vector_store)],
)
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -43,8 +43,8 @@ async def tools_on_agent_level() -> None:
# The agent can use these tools for any query during its lifetime
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -75,8 +75,8 @@ async def tools_on_run_level() -> None:
# Agent created without tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -107,8 +107,8 @@ async def mixed_tools_example() -> None:
# Agent created with some base tools
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -3,7 +3,7 @@
import asyncio
from typing import TYPE_CHECKING, Any
from agent_framework import ChatAgent, HostedMCPTool
from agent_framework import Agent, HostedMCPTool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun"):
"""When we don't have a thread, we need to ensure we return with the input, approval request and approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query)
while len(result.user_input_requests) > 0:
@@ -30,10 +30,10 @@ async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun")
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed]))
new_inputs.append(Message(role="assistant", contents=[user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
Message(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs)
@@ -42,7 +42,7 @@ async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun")
async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", thread: "AgentThread"):
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query, thread=thread, store=True)
while len(result.user_input_requests) > 0:
@@ -54,7 +54,7 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user",
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
)
@@ -65,13 +65,13 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
async def handle_approvals_with_thread_streaming(query: str, agent: "SupportsAgentRun", thread: "AgentThread"):
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
new_input: list[ChatMessage] = []
new_input: list[Message] = []
new_input_added = True
while new_input_added:
new_input_added = False
new_input.append(ChatMessage(role="user", text=query))
new_input.append(Message(role="user", text=query))
async for update in agent.run(new_input, thread=thread, options={"store": True}, stream=True):
if update.user_input_requests:
for user_input_needed in update.user_input_requests:
@@ -81,7 +81,7 @@ async def handle_approvals_with_thread_streaming(query: str, agent: "SupportsAge
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")]
)
)
@@ -96,8 +96,8 @@ async def run_hosted_mcp_without_thread_and_specific_approval() -> None:
credential = AzureCliCredential()
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=AzureOpenAIResponsesClient(
async with Agent(
client=AzureOpenAIResponsesClient(
credential=credential,
),
name="DocsAgent",
@@ -129,8 +129,8 @@ async def run_hosted_mcp_without_approval() -> None:
credential = AzureCliCredential()
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=AzureOpenAIResponsesClient(
async with Agent(
client=AzureOpenAIResponsesClient(
credential=credential,
),
name="DocsAgent",
@@ -163,8 +163,8 @@ async def run_hosted_mcp_with_thread() -> None:
credential = AzureCliCredential()
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=AzureOpenAIResponsesClient(
async with Agent(
client=AzureOpenAIResponsesClient(
credential=credential,
),
name="DocsAgent",
@@ -196,8 +196,8 @@ async def run_hosted_mcp_with_thread_streaming() -> None:
credential = AzureCliCredential()
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=AzureOpenAIResponsesClient(
async with Agent(
client=AzureOpenAIResponsesClient(
credential=credential,
),
name="DocsAgent",
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
@@ -37,7 +37,7 @@ async def main():
credential=credential,
)
agent: ChatAgent = responses_client.as_agent(
agent: Agent = responses_client.as_agent(
name="DocsAgent",
instructions=("You are a helpful assistant that can help with Microsoft documentation questions."),
)
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework import Agent, AgentThread, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -33,8 +33,8 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -62,8 +62,8 @@ async def example_with_thread_persistence_in_memory() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -103,8 +103,8 @@ async def example_with_existing_thread_id() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -125,8 +125,8 @@ async def example_with_existing_thread_id() -> None:
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
agent = ChatAgent(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
agent = Agent(
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -7,7 +7,7 @@ This folder contains examples demonstrating how to implement custom agents and c
| File | Description |
|------|-------------|
| [`custom_agent.py`](custom_agent.py) | Shows how to create custom agents by extending the `BaseAgent` class. Demonstrates the `EchoAgent` implementation with both streaming and non-streaming responses, proper thread management, and message history handling. |
| [`custom_chat_client.py`](../../chat_client/custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `ChatAgent` using the `as_agent()` method. |
| [`custom_chat_client.py`](../../chat_client/custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `Agent` using the `as_agent()` method. |
## Key Takeaways
@@ -20,7 +20,7 @@ This folder contains examples demonstrating how to implement custom agents and c
### Custom Chat Clients
- Custom chat clients allow you to integrate any backend service or create new LLM providers
- You must implement `_inner_get_response()` with a stream parameter to handle both streaming and non-streaming responses
- Custom chat clients can be used with `ChatAgent` to leverage all agent framework features
- Custom chat clients can be used with `Agent` to leverage all agent framework features
- Use the `as_agent()` method to easily create agents from your custom chat clients
Both approaches allow you to extend the framework for your specific use cases while maintaining compatibility with the broader Agent Framework ecosystem.
@@ -9,8 +9,8 @@ from agent_framework import (
AgentResponseUpdate,
AgentThread,
BaseAgent,
ChatMessage,
Content,
Message,
Role,
normalize_messages,
)
@@ -57,7 +57,7 @@ class EchoAgent(BaseAgent):
def run(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
messages: str | Message | list[str] | list[Message] | None = None,
*,
stream: bool = False,
thread: AgentThread | None = None,
@@ -81,7 +81,7 @@ class EchoAgent(BaseAgent):
async def _run(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
messages: str | Message | list[str] | list[Message] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
@@ -91,11 +91,9 @@ class EchoAgent(BaseAgent):
normalized_messages = normalize_messages(messages)
if not normalized_messages:
response_message = ChatMessage(
response_message = Message(
role=Role.ASSISTANT,
contents=[
Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")
],
contents=[Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")],
)
else:
# For simplicity, echo the last user message
@@ -105,7 +103,7 @@ class EchoAgent(BaseAgent):
else:
echo_text = f"{self.echo_prefix}[Non-text message received]"
response_message = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text=echo_text)])
response_message = Message(role=Role.ASSISTANT, contents=[Content.from_text(text=echo_text)])
# Notify the thread of new messages if provided
if thread is not None:
@@ -115,7 +113,7 @@ class EchoAgent(BaseAgent):
async def _run_stream(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
messages: str | Message | list[str] | list[Message] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
@@ -150,7 +148,7 @@ class EchoAgent(BaseAgent):
# Notify the thread of the complete response if provided
if thread is not None:
complete_response = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text=response_text)])
complete_response = Message(role=Role.ASSISTANT, contents=[Content.from_text(text=response_text)])
await self._notify_thread_of_new_messages(thread, normalized_messages, complete_response)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.ollama import OllamaChatClient
"""
@@ -32,7 +32,7 @@ async def test_image() -> None:
image_uri = create_sample_image()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What's in this image?"),
@@ -15,14 +15,14 @@ This folder contains examples demonstrating different ways to create and use age
| [`openai_assistants_with_function_tools.py`](openai_assistants_with_function_tools.py) | Function tools with `OpenAIAssistantProvider` at both agent-level and query-level. |
| [`openai_assistants_with_response_format.py`](openai_assistants_with_response_format.py) | Structured outputs with `OpenAIAssistantProvider` using Pydantic models. |
| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Thread management with `OpenAIAssistantProvider` for conversation context persistence. |
| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. |
| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `Agent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. |
| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. |
| [`openai_chat_client_with_function_tools.py`](openai_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`openai_chat_client_with_local_mcp.py`](openai_chat_client_with_local_mcp.py) | Shows how to integrate OpenAI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. |
| [`openai_chat_client_with_thread.py`](openai_chat_client_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. |
| [`openai_chat_client_with_runtime_json_schema.py`](openai_chat_client_with_runtime_json_schema.py) | Shows how to supply a runtime JSON Schema via `additional_chat_options` for structured output without defining a Pydantic model. |
| [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. |
| [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `Agent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. |
| [`openai_responses_client_image_analysis.py`](openai_responses_client_image_analysis.py) | Demonstrates how to use vision capabilities with agents to analyze images. |
| [`openai_responses_client_image_generation.py`](openai_responses_client_image_generation.py) | Demonstrates how to use image generation capabilities with OpenAI agents to create images based on text descriptions. Requires PIL (Pillow) for image display. |
| [`openai_responses_client_reasoning.py`](openai_responses_client_reasoning.py) | Demonstrates how to use reasoning capabilities with OpenAI agents, showing how the agent can provide detailed reasoning for its responses. |
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
@@ -40,8 +40,8 @@ async def tools_on_agent_level() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -70,8 +70,8 @@ async def tools_on_run_level() -> None:
print("=== Tools Passed to Run Method ===")
# Agent created without tools
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -100,8 +100,8 @@ async def mixed_tools_example() -> None:
print("=== Mixed Tools Example (Agent + Run Method) ===")
# Agent created with some base tools
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIChatClient
"""
@@ -29,8 +29,8 @@ async def mcp_tools_on_run_level() -> None:
name="Microsoft Learn MCP",
url="https://learn.microsoft.com/api/mcp",
) as mcp_server,
ChatAgent(
chat_client=OpenAIChatClient(),
Agent(
client=OpenAIChatClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
) as agent,
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, ChatMessageStore, tool
from agent_framework import Agent, AgentThread, ChatMessageStore, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field
@@ -30,8 +30,8 @@ async def example_with_automatic_thread_creation() -> None:
"""Example showing automatic thread creation (service-managed thread)."""
print("=== Automatic Thread Creation Example ===")
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -55,8 +55,8 @@ async def example_with_thread_persistence() -> None:
print("=== Thread Persistence Example ===")
print("Using the same thread across multiple conversations to maintain context.\n")
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -88,8 +88,8 @@ async def example_with_existing_thread_messages() -> None:
"""Example showing how to work with existing thread messages for OpenAI."""
print("=== Existing Thread Messages Example ===")
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -110,8 +110,8 @@ async def example_with_existing_thread_messages() -> None:
print("\n--- Continuing with the same thread in a new agent instance ---")
# Create a new agent instance but use the existing thread with its message history
new_agent = ChatAgent(
chat_client=OpenAIChatClient(),
new_agent = Agent(
client=OpenAIChatClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, HostedWebSearchTool
from agent_framework import Agent, HostedWebSearchTool
from agent_framework.openai import OpenAIChatClient
"""
@@ -22,8 +22,8 @@ async def main() -> None:
}
}
agent = ChatAgent(
chat_client=OpenAIChatClient(model_id="gpt-4o-search-preview"),
agent = Agent(
client=OpenAIChatClient(model_id="gpt-4o-search-preview"),
instructions="You are a helpful assistant that can search the web for current information.",
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
)
@@ -6,11 +6,12 @@ from random import randint
from typing import Annotated
from agent_framework import (
ChatAgent,
Agent,
ChatContext,
ChatMessage,
ChatResponse,
Message,
MiddlewareTermination,
Role,
chat_middleware,
tool,
)
@@ -46,8 +47,8 @@ async def security_and_override_middleware(
# Override the response instead of calling AI
context.result = ChatResponse(
messages=[
ChatMessage(
role="assistant",
Message(
role=Role.ASSISTANT,
text="I cannot process requests containing sensitive information. "
"Please rephrase your question without including passwords, secrets, or other "
"sensitive data.",
@@ -55,8 +56,8 @@ async def security_and_override_middleware(
]
)
# Set terminate flag to stop execution
raise MiddlewareTermination
# Terminate middleware execution with the blocked response
raise MiddlewareTermination(result=context.result)
# Continue to next middleware or AI execution
await call_next(context)
@@ -79,8 +80,8 @@ async def non_streaming_example() -> None:
"""Example of non-streaming response (get the complete result at once)."""
print("=== Non-streaming Response Example ===")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -95,8 +96,8 @@ async def streaming_example() -> None:
"""Example of streaming response (get results as they are generated)."""
print("=== Streaming Response Example ===")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(
agent = Agent(
client=OpenAIResponsesClient(
middleware=[security_and_override_middleware],
),
instructions="You are a helpful weather agent.",
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.openai import OpenAIResponsesClient
"""
@@ -23,7 +23,7 @@ async def main():
)
# 2. Create a simple message with both text and image content
user_message = ChatMessage(
user_message = Message(
role="user",
contents=[
Content.from_text(text="What do you see in this image?"),
@@ -3,7 +3,7 @@
import asyncio
from agent_framework import (
ChatAgent,
Agent,
HostedCodeInterpreterTool,
)
from agent_framework.openai import OpenAIResponsesClient
@@ -20,8 +20,8 @@ async def main() -> None:
"""Example showing how to use the HostedCodeInterpreterTool with OpenAI Responses."""
print("=== OpenAI Responses Agent with Code Interpreter Example ===")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
)
@@ -4,7 +4,7 @@ import asyncio
import os
import tempfile
from agent_framework import ChatAgent, HostedCodeInterpreterTool
from agent_framework import Agent, HostedCodeInterpreterTool
from agent_framework.openai import OpenAIResponsesClient
from openai import AsyncOpenAI
@@ -66,8 +66,8 @@ async def main() -> None:
temp_file_path, file_id = await create_sample_file_and_upload(openai_client)
# Create agent using OpenAI Responses client
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful assistant that can analyze data files using Python code.",
tools=HostedCodeInterpreterTool(inputs=[{"file_id": file_id}]),
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, Content, HostedFileSearchTool
from agent_framework import Agent, Content, HostedFileSearchTool
from agent_framework.openai import OpenAIResponsesClient
"""
@@ -47,8 +47,8 @@ async def main() -> None:
print(f"User: {message}")
file_id, vector_store = await create_vector_store(client)
agent = ChatAgent(
chat_client=client,
agent = Agent(
client=client,
instructions="You are a helpful assistant that can search through files to find information.",
tools=[HostedFileSearchTool(inputs=vector_store)],
)
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
@@ -40,8 +40,8 @@ async def tools_on_agent_level() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -70,8 +70,8 @@ async def tools_on_run_level() -> None:
print("=== Tools Passed to Run Method ===")
# Agent created without tools
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -100,8 +100,8 @@ async def mixed_tools_example() -> None:
print("=== Mixed Tools Example (Agent + Run Method) ===")
# Agent created with some base tools
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -3,7 +3,7 @@
import asyncio
from typing import TYPE_CHECKING, Any
from agent_framework import ChatAgent, HostedMCPTool
from agent_framework import Agent, HostedMCPTool
from agent_framework.openai import OpenAIResponsesClient
"""
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun"):
"""When we don't have a thread, we need to ensure we return with the input, approval request and approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query)
while len(result.user_input_requests) > 0:
@@ -29,10 +29,10 @@ async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun")
f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}"
f" with arguments: {user_input_needed.function_call.arguments}"
)
new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed]))
new_inputs.append(Message(role="assistant", contents=[user_input_needed]))
user_approval = input("Approve function call? (y/n): ")
new_inputs.append(
ChatMessage(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
Message(role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")])
)
result = await agent.run(new_inputs)
@@ -41,7 +41,7 @@ async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun")
async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", thread: "AgentThread"):
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
result = await agent.run(query, thread=thread, store=True)
while len(result.user_input_requests) > 0:
@@ -53,7 +53,7 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user",
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
)
@@ -64,13 +64,13 @@ async def handle_approvals_with_thread(query: str, agent: "SupportsAgentRun", th
async def handle_approvals_with_thread_streaming(query: str, agent: "SupportsAgentRun", thread: "AgentThread"):
"""Here we let the thread deal with the previous responses, and we just rerun with the approval."""
from agent_framework import ChatMessage
from agent_framework import Message
new_input: list[ChatMessage] = []
new_input: list[Message] = []
new_input_added = True
while new_input_added:
new_input_added = False
new_input.append(ChatMessage(role="user", text=query))
new_input.append(Message(role="user", text=query))
async for update in agent.run(new_input, thread=thread, stream=True, options={"store": True}):
if update.user_input_requests:
for user_input_needed in update.user_input_requests:
@@ -80,7 +80,7 @@ async def handle_approvals_with_thread_streaming(query: str, agent: "SupportsAge
)
user_approval = input("Approve function call? (y/n): ")
new_input.append(
ChatMessage(
Message(
role="user", contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")]
)
)
@@ -95,8 +95,8 @@ async def run_hosted_mcp_without_thread_and_specific_approval() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=HostedMCPTool(
@@ -126,8 +126,8 @@ async def run_hosted_mcp_without_approval() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=HostedMCPTool(
@@ -158,8 +158,8 @@ async def run_hosted_mcp_with_thread() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=HostedMCPTool(
@@ -189,8 +189,8 @@ async def run_hosted_mcp_with_thread_streaming() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=HostedMCPTool(
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIResponsesClient
"""
@@ -22,8 +22,8 @@ async def streaming_with_mcp(show_raw_stream: bool = False) -> None:
print("=== Tools Defined on Agent Level ===")
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=MCPStreamableHTTPTool( # Tools defined at agent creation
@@ -60,8 +60,8 @@ async def run_with_mcp() -> None:
# Tools are provided when creating the agent
# The agent can use these tools for any query during its lifetime
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=MCPStreamableHTTPTool( # Tools defined at agent creation
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, tool
from agent_framework import Agent, AgentThread, tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
@@ -30,8 +30,8 @@ async def example_with_automatic_thread_creation() -> None:
"""Example showing automatic thread creation."""
print("=== Automatic Thread Creation Example ===")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -57,8 +57,8 @@ async def example_with_thread_persistence_in_memory() -> None:
"""
print("=== Thread Persistence Example (In-Memory) ===")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -96,8 +96,8 @@ async def example_with_existing_thread_id() -> None:
# First, create a conversation and capture the thread ID
existing_thread_id = None
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -117,8 +117,8 @@ async def example_with_existing_thread_id() -> None:
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatAgent, HostedWebSearchTool
from agent_framework import Agent, HostedWebSearchTool
from agent_framework.openai import OpenAIResponsesClient
"""
@@ -22,8 +22,8 @@ async def main() -> None:
}
}
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
instructions="You are a helpful assistant that can search the web for current information.",
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
)
@@ -76,8 +76,8 @@ Expected response:
{
"status": "healthy",
"agents": [
{"name": "WeatherAgent", "type": "ChatAgent"},
{"name": "MathAgent", "type": "ChatAgent"}
{"name": "WeatherAgent", "type": "Agent"},
{"name": "MathAgent", "type": "Agent"}
],
"agent_count": 2
}
@@ -56,15 +56,15 @@ def calculate_tip(bill_amount: float, tip_percentage: float = 15.0) -> dict[str,
# 1. Create multiple agents, each with its own instruction set and tools.
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
weather_agent = chat_client.as_agent(
weather_agent = client.as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant. Provide current weather information.",
tools=[get_weather],
)
math_agent = chat_client.as_agent(
math_agent = client.as_agent(
name="MathAgent",
instructions="You are a helpful math assistant. Help users with calculations like tip calculations.",
tools=[calculate_tip],
@@ -30,14 +30,14 @@ CHEMIST_AGENT_NAME = "ChemistAgent"
# 2. Instantiate both agents that the orchestration will run concurrently.
def _create_agents() -> list[Any]:
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
physicist = chat_client.as_agent(
physicist = client.as_agent(
name=PHYSICIST_AGENT_NAME,
instructions="You are an expert in physics. You answer questions from a physics perspective.",
)
chemist = chat_client.as_agent(
chemist = client.as_agent(
name=CHEMIST_AGENT_NAME,
instructions="You are an expert in chemistry. You answer questions from a chemistry perspective.",
)
@@ -45,14 +45,14 @@ class EmailPayload(BaseModel):
# 2. Instantiate both agents so they can be registered with AgentFunctionApp.
def _create_agents() -> list[Any]:
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
spam_agent = chat_client.as_agent(
spam_agent = client.as_agent(
name=SPAM_AGENT_NAME,
instructions="You are a spam detection assistant that identifies spam emails.",
)
email_agent = chat_client.as_agent(
email_agent = client.as_agent(
name=EMAIL_AGENT_NAME,
instructions="You are an email assistant that helps users draft responses to emails with professionalism.",
)
@@ -142,20 +142,20 @@ The sample shows how to enable MCP tool triggers with flexible agent configurati
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
# Create Azure OpenAI Chat Client
chat_client = AzureOpenAIChatClient()
client = AzureOpenAIChatClient()
# Define agents with different roles
joker_agent = chat_client.as_agent(
joker_agent = client.as_agent(
name="Joker",
instructions="You are good at telling jokes.",
)
stock_agent = chat_client.as_agent(
stock_agent = client.as_agent(
name="StockAdvisor",
instructions="Check stock prices.",
)
plant_agent = chat_client.as_agent(
plant_agent = client.as_agent(
name="PlantAdvisor",
instructions="Recommend plants.",
description="Get plant recommendations.",
@@ -28,23 +28,23 @@ from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
# Create Azure OpenAI Chat Client
# This uses AzureCliCredential for authentication (requires 'az login')
chat_client = AzureOpenAIChatClient()
client = AzureOpenAIChatClient()
# Define three AI agents with different roles
# Agent 1: Joker - HTTP trigger only (default)
agent1 = chat_client.as_agent(
agent1 = client.as_agent(
name="Joker",
instructions="You are good at telling jokes.",
)
# Agent 2: StockAdvisor - MCP tool trigger only
agent2 = chat_client.as_agent(
agent2 = client.as_agent(
name="StockAdvisor",
instructions="Check stock prices.",
)
# Agent 3: PlantAdvisor - Both HTTP and MCP tool triggers
agent3 = chat_client.as_agent(
agent3 = client.as_agent(
name="PlantAdvisor",
instructions="Recommend plants.",
description="Get plant recommendations.",
@@ -14,7 +14,7 @@ This folder contains simple examples demonstrating direct usage of various chat
| [`openai_assistants_client.py`](openai_assistants_client.py) | Direct usage of OpenAI Assistants Client for basic chat interactions with OpenAI assistants. |
| [`openai_chat_client.py`](openai_chat_client.py) | Direct usage of OpenAI Chat Client for chat interactions with OpenAI models. |
| [`openai_responses_client.py`](openai_responses_client.py) | Direct usage of OpenAI Responses Client for structured response generation with OpenAI models. |
| [`custom_chat_client.py`](custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `ChatAgent` using the `as_agent()` method. |
| [`custom_chat_client.py`](custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `Agent` using the `as_agent()` method. |
## Environment Variables
@@ -21,10 +21,10 @@ async def main() -> None:
- OpenAI model ID: Use "model_id" parameter or "OPENAI_CHAT_MODEL_ID" environment variable
- OpenAI API key: Use "api_key" parameter or "OPENAI_API_KEY" environment variable
"""
chat_client = OpenAIChatClient()
client = OpenAIChatClient()
try:
task = asyncio.create_task(chat_client.get_response(messages=["Tell me a fantasy story."]))
task = asyncio.create_task(client.get_response(messages=["Tell me a fantasy story."]))
await asyncio.sleep(1)
task.cancel()
await task
@@ -8,12 +8,12 @@ from typing import Any, ClassVar, Generic
from agent_framework import (
BaseChatClient,
ChatMessage,
ChatMiddlewareLayer,
ChatResponse,
ChatResponseUpdate,
Content,
FunctionInvocationLayer,
Message,
ResponseStream,
Role,
)
@@ -61,7 +61,7 @@ class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
def _inner_get_response(
self,
*,
messages: Sequence[ChatMessage],
messages: Sequence[Message],
stream: bool = False,
options: Mapping[str, Any],
**kwargs: Any,
@@ -82,7 +82,7 @@ class EchoingChatClient(BaseChatClient[OptionsCoT], Generic[OptionsCoT]):
else:
response_text = f"{self.prefix} [No text message found]"
response_message = ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(response_text)])
response_message = Message(role=Role.ASSISTANT, contents=[Content.from_text(response_text)])
response = ChatResponse(
messages=[response_message],
@@ -124,7 +124,7 @@ class EchoingChatClientWithLayers( # type: ignore[misc,type-var]
async def main() -> None:
"""Demonstrates how to implement and use a custom chat client with ChatAgent."""
"""Demonstrates how to implement and use a custom chat client with Agent."""
print("=== Custom Chat Client Example ===\n")
# Create the custom chat client
@@ -139,14 +139,14 @@ Different agents with isolated or shared memory configurations.
To create a custom context provider, implement the `ContextProvider` protocol:
```python
from agent_framework import ContextProvider, Context, ChatMessage
from agent_framework import ContextProvider, Context, Message
from collections.abc import MutableSequence, Sequence
from typing import Any
class MyContextProvider(ContextProvider):
async def invoking(
self,
messages: ChatMessage | MutableSequence[ChatMessage],
messages: Message | MutableSequence[Message],
**kwargs: Any
) -> Context:
"""Provide context before the agent processes the request."""
@@ -155,8 +155,8 @@ class MyContextProvider(ContextProvider):
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
@@ -17,7 +17,7 @@ from contextlib import AsyncExitStack
from types import TracebackType
from typing import TYPE_CHECKING, Any, cast
from agent_framework import ChatAgent, ChatMessage, Context, ContextProvider
from agent_framework import Agent, Context, ContextProvider, Message
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
@@ -47,7 +47,7 @@ class AggregateContextProvider(ContextProvider):
Examples:
.. code-block:: python
from agent_framework import ChatAgent
from agent_framework import Agent
# Create multiple context providers
provider1 = CustomContextProvider1()
@@ -58,7 +58,7 @@ class AggregateContextProvider(ContextProvider):
aggregate = AggregateContextProvider([provider1, provider2, provider3])
# Pass the aggregate to the agent
agent = ChatAgent(chat_client=client, name="assistant", context_provider=aggregate)
agent = Agent(client=client, name="assistant", context_provider=aggregate)
# You can also add more providers later
provider4 = CustomContextProvider4()
@@ -90,10 +90,10 @@ class AggregateContextProvider(ContextProvider):
await asyncio.gather(*[x.thread_created(thread_id) for x in self.providers])
@override
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
contexts = await asyncio.gather(*[provider.invoking(messages, **kwargs) for provider in self.providers])
instructions: str = ""
return_messages: list[ChatMessage] = []
return_messages: list[Message] = []
tools: list["ToolProtocol"] = []
for ctx in contexts:
if ctx.instructions:
@@ -107,8 +107,8 @@ class AggregateContextProvider(ContextProvider):
@override
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
@@ -167,7 +167,7 @@ class TimeContextProvider(ContextProvider):
"""A simple context provider that adds time-related instructions."""
@override
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -181,7 +181,7 @@ class PersonaContextProvider(ContextProvider):
self.persona = persona
@override
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
return Context(instructions=f"Your persona: {self.persona}. ")
@@ -192,7 +192,7 @@ class PreferencesContextProvider(ContextProvider):
self.preferences: dict[str, str] = {}
@override
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
if not self.preferences:
return Context()
prefs_str = ", ".join(f"{k}: {v}" for k, v in self.preferences.items())
@@ -201,14 +201,14 @@ class PreferencesContextProvider(ContextProvider):
@override
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
# Simple example: extract and store preferences from user messages
# In a real implementation, you might use structured extraction
msgs = [request_messages] if isinstance(request_messages, ChatMessage) else list(request_messages)
msgs = [request_messages] if isinstance(request_messages, Message) else list(request_messages)
for msg in msgs:
content = msg.text if hasattr(msg, "text") else ""
@@ -230,7 +230,7 @@ class PreferencesContextProvider(ContextProvider):
async def main():
"""Demonstrate using AggregateContextProvider to combine multiple providers."""
async with AzureCliCredential() as credential:
chat_client = AzureAIClient(credential=credential)
client = AzureAIClient(credential=credential)
# Create individual context providers
time_provider = TimeContextProvider()
@@ -245,8 +245,8 @@ async def main():
])
# Create the agent with the aggregate provider
async with ChatAgent(
chat_client=chat_client,
async with Agent(
client=client,
instructions="You are a helpful assistant.",
context_provider=aggregate_provider,
) as agent:
@@ -126,7 +126,7 @@ AZURE_OPENAI_RESOURCE_URL=https://myresource.openai.azure.com
### Semantic Mode
```python
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import DefaultAzureCredential
@@ -141,8 +141,8 @@ search_provider = AzureAISearchContextProvider(
# Create agent with search context
async with AzureAIAgentClient(credential=DefaultAzureCredential()) as client:
async with ChatAgent(
chat_client=client,
async with Agent(
client=client,
model=model_deployment,
context_provider=search_provider,
) as agent:
@@ -166,8 +166,8 @@ search_provider = AzureAISearchContextProvider(
)
# Use with agent (same as semantic mode)
async with ChatAgent(
chat_client=client,
async with Agent(
client=client,
model=model_deployment,
context_provider=search_provider,
) as agent:
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import AzureCliCredential
from dotenv import load_dotenv
@@ -112,8 +112,8 @@ async def main() -> None:
model_deployment_name=model_deployment,
credential=AzureCliCredential(),
) as client,
ChatAgent(
chat_client=client,
Agent(
client=client,
name="SearchAgent",
instructions=(
"You are a helpful assistant with advanced reasoning capabilities. "
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureAIAgentClient, AzureAISearchContextProvider
from azure.identity.aio import AzureCliCredential
from dotenv import load_dotenv
@@ -69,8 +69,8 @@ async def main() -> None:
model_deployment_name=model_deployment,
credential=AzureCliCredential(),
) as client,
ChatAgent(
chat_client=client,
Agent(
client=client,
name="SearchAgent",
instructions=(
"You are a helpful assistant. Use the provided context from the "
@@ -30,7 +30,7 @@ Run:
import asyncio
import os
from agent_framework import ChatMessage, tool
from agent_framework import Message, tool
from agent_framework.openai import OpenAIChatClient
from agent_framework_redis._provider import RedisProvider
from redisvl.extensions.cache.embeddings import EmbeddingsCache
@@ -128,9 +128,9 @@ async def main() -> None:
# Build sample chat messages to persist to Redis
messages = [
ChatMessage("user", ["runA CONVO: User Message"]),
ChatMessage("assistant", ["runA CONVO: Assistant Message"]),
ChatMessage("system", ["runA CONVO: System Message"]),
Message("user", ["runA CONVO: User Message"]),
Message("assistant", ["runA CONVO: Assistant Message"]),
Message("system", ["runA CONVO: System Message"]),
]
# Declare/start a conversation/thread and write messages under 'runA'.
@@ -142,7 +142,7 @@ async def main() -> None:
# Retrieve relevant memories for a hypothetical model call. The provider uses
# the current request messages as the retrieval query and returns context to
# be injected into the model's instructions.
ctx = await provider.invoking([ChatMessage("system", ["B: Assistant Message"])])
ctx = await provider.invoking([Message("system", ["B: Assistant Message"])])
# Inspect retrieved memories that would be injected into instructions
# (Debug-only output so you can verify retrieval works as expected.)
@@ -4,7 +4,7 @@ import asyncio
from collections.abc import MutableSequence, Sequence
from typing import Any
from agent_framework import ChatAgent, ChatClientProtocol, ChatMessage, Context, ContextProvider
from agent_framework import Agent, Context, ContextProvider, Message, SupportsChatGetResponse
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import BaseModel
@@ -16,13 +16,13 @@ class UserInfo(BaseModel):
class UserInfoMemory(ContextProvider):
def __init__(self, chat_client: ChatClientProtocol, user_info: UserInfo | None = None, **kwargs: Any):
def __init__(self, client: SupportsChatGetResponse, user_info: UserInfo | None = None, **kwargs: Any):
"""Create the memory.
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
"""
self._chat_client = chat_client
self._chat_client = client
if user_info:
self.user_info = user_info
elif kwargs:
@@ -32,8 +32,8 @@ class UserInfoMemory(ContextProvider):
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
request_messages: Message | Sequence[Message],
response_messages: Message | Sequence[Message] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
@@ -64,7 +64,7 @@ class UserInfoMemory(ContextProvider):
except Exception:
pass # Failed to extract, continue without updating
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
async def invoking(self, messages: Message | MutableSequence[Message], **kwargs: Any) -> Context:
"""Provide user information context before each agent call."""
instructions: list[str] = []
@@ -92,14 +92,14 @@ class UserInfoMemory(ContextProvider):
async def main():
async with AzureCliCredential() as credential:
chat_client = AzureAIClient(credential=credential)
client = AzureAIClient(credential=credential)
# Create the memory provider
memory_provider = UserInfoMemory(chat_client)
memory_provider = UserInfoMemory(client)
# Create the agent with memory
async with ChatAgent(
chat_client=chat_client,
async with Agent(
client=client,
instructions="You are a friendly assistant. Always address the user by their name.",
context_provider=memory_provider,
) as agent:
@@ -175,7 +175,7 @@ agent = agent_factory.create_agent_from_yaml_path(Path("custom_provider.yaml"))
This allows you to extend the declarative framework with custom chat client implementations. The mapping requires:
- **package**: The Python package/module to import from
- **name**: The class name of your ChatClientProtocol implementation
- **name**: The class name of your SupportsChatGetResponse implementation
- **model_id_field**: The constructor parameter name that accepts the value of the `model.id` field from the YAML
You can reference your custom provider using either `Provider.ApiType` format or just `Provider` in your YAML configuration, as long as it matches the registered mapping.
@@ -26,7 +26,7 @@ async def main():
# create the AgentFactory with a chat client and bindings
agent_factory = AgentFactory(
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
bindings={"get_weather": get_weather},
)
# create the agent from the yaml
@@ -44,7 +44,7 @@ Each agent/workflow follows a strict structure required by DevUI's discovery sys
```
agent_name/
├── __init__.py # Must export: agent = ChatAgent(...)
├── __init__.py # Must export: agent = Agent(...)
├── agent.py # Agent implementation
└── .env.example # Example environment variables
```
@@ -100,13 +100,13 @@ Example:
```python
# my_agent/__init__.py
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
agent = ChatAgent(
agent = Agent(
name="MyAgent",
description="My custom agent",
chat_client=OpenAIChatClient(),
client=OpenAIChatClient(),
# ... your configuration
)
```
@@ -21,7 +21,7 @@ import logging
import os
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIResponsesClient
logger = logging.getLogger(__name__)
@@ -68,7 +68,7 @@ def extract_key_points(
# Agent using Azure OpenAI Responses API (supports PDF uploads!)
agent = ChatAgent(
agent = Agent(
name="AzureResponsesAgent",
description="An agent that can analyze PDFs, images, and other documents using Azure OpenAI Responses API",
instructions="""
@@ -85,7 +85,7 @@ agent = ChatAgent(
For PDFs, you can read and understand the text, tables, and structure.
For images, you can describe what you see and extract any text.
""",
chat_client=AzureOpenAIResponsesClient(
client=AzureOpenAIResponsesClient(
deployment_name=_deployment_name,
endpoint=_endpoint,
api_version="2025-03-01-preview", # Required for Responses API
@@ -8,7 +8,7 @@ Make sure to run 'az login' before starting devui.
import os
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -43,9 +43,9 @@ def get_forecast(
# Agent instance following Agent Framework conventions
agent = ChatAgent(
agent = Agent(
name="FoundryWeatherAgent",
chat_client=AzureAIAgentClient(
client=AzureAIAgentClient(
project_endpoint=os.environ.get("AZURE_AI_PROJECT_ENDPOINT"),
model_deployment_name=os.environ.get("FOUNDRY_MODEL_DEPLOYMENT_NAME"),
credential=AzureCliCredential(),
@@ -10,7 +10,7 @@ import logging
import os
from typing import Annotated
from agent_framework import ChatAgent, Executor, WorkflowBuilder, WorkflowContext, handler, tool
from agent_framework import Agent, Executor, WorkflowBuilder, WorkflowContext, handler, tool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.devui import serve
from typing_extensions import Never
@@ -68,7 +68,7 @@ def main():
logger = logging.getLogger(__name__)
# Create Azure OpenAI chat client
chat_client = AzureOpenAIChatClient(
client = AzureOpenAIChatClient(
api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2024-10-21"),
@@ -76,22 +76,22 @@ def main():
)
# Create agents
weather_agent = ChatAgent(
weather_agent = Agent(
name="weather-assistant",
description="Provides weather information and time",
instructions=(
"You are a helpful weather and time assistant. Use the available tools to "
"provide accurate weather information and current time for any location."
),
chat_client=chat_client,
client=client,
tools=[get_weather, get_time],
)
simple_agent = ChatAgent(
simple_agent = Agent(
name="general-assistant",
description="A simple conversational agent",
instructions="You are a helpful assistant.",
chat_client=chat_client,
client=client,
)
# Create a basic workflow: Input -> UpperCase -> AddExclamation -> Output
@@ -7,15 +7,16 @@ from collections.abc import AsyncIterable, Awaitable, Callable
from typing import Annotated
from agent_framework import (
ChatAgent,
Agent,
ChatContext,
ChatMessage,
ChatResponse,
ChatResponseUpdate,
Content,
FunctionInvocationContext,
Message,
MiddlewareTermination,
ResponseStream,
Role,
chat_middleware,
function_middleware,
tool,
@@ -44,7 +45,7 @@ async def security_filter_middleware(
# Check only the last message (most recent user input)
last_message = context.messages[-1] if context.messages else None
if last_message and last_message.role == "user" and last_message.text:
if last_message and last_message.role == Role.USER and last_message.text:
message_lower = last_message.text.lower()
for term in blocked_terms:
if term in message_lower:
@@ -55,26 +56,29 @@ async def security_filter_middleware(
)
if context.stream:
# Streaming mode: return async generator
# Streaming mode: wrap in ResponseStream
async def blocked_stream(msg: str = error_message) -> AsyncIterable[ChatResponseUpdate]:
yield ChatResponseUpdate(
contents=[Content.from_text(text=msg)],
role="assistant",
role=Role.ASSISTANT,
)
context.result = ResponseStream(blocked_stream(), finalizer=ChatResponse.from_updates)
response = ChatResponse(
messages=[Message(role=Role.ASSISTANT, text=error_message)]
)
context.result = ResponseStream(blocked_stream(), finalizer=lambda _, r=response: r)
else:
# Non-streaming mode: return complete response
context.result = ChatResponse(
messages=[
ChatMessage(
role="assistant",
Message(
role=Role.ASSISTANT,
text=error_message,
)
]
)
raise MiddlewareTermination
raise MiddlewareTermination(result=context.result)
await call_next(context)
@@ -92,7 +96,7 @@ async def atlantis_location_filter_middleware(
"Blocked! Hold up right there!! Tell the user that "
"'Atlantis is a special place, we must never ask about the weather there!!'"
)
raise MiddlewareTermination
raise MiddlewareTermination(result=context.result)
await call_next(context)
@@ -136,7 +140,7 @@ def send_email(
# Agent instance following Agent Framework conventions
agent = ChatAgent(
agent = Agent(
name="AzureWeatherAgent",
description="A helpful agent that provides weather information and forecasts",
instructions="""
@@ -144,7 +148,7 @@ agent = ChatAgent(
and forecasts for any location. Always be helpful and provide detailed
weather information when asked.
""",
chat_client=AzureOpenAIChatClient(
client=AzureOpenAIChatClient(
api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""),
),
tools=[get_weather, get_forecast, send_email],
@@ -59,10 +59,10 @@ def is_approved(message: Any) -> bool:
# Create Azure OpenAI chat client
chat_client = AzureOpenAIChatClient(api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""))
client = AzureOpenAIChatClient(api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""))
# Create Writer agent - generates content
writer = chat_client.as_agent(
writer = client.as_agent(
name="Writer",
instructions=(
"You are an excellent content writer. "
@@ -72,7 +72,7 @@ writer = chat_client.as_agent(
)
# Create Reviewer agent - evaluates and provides structured feedback
reviewer = chat_client.as_agent(
reviewer = client.as_agent(
name="Reviewer",
instructions=(
"You are an expert content reviewer. "
@@ -90,7 +90,7 @@ reviewer = chat_client.as_agent(
)
# Create Editor agent - improves content based on feedback
editor = chat_client.as_agent(
editor = client.as_agent(
name="Editor",
instructions=(
"You are a skilled editor. "
@@ -101,7 +101,7 @@ editor = chat_client.as_agent(
)
# Create Publisher agent - formats content for publication
publisher = chat_client.as_agent(
publisher = client.as_agent(
name="Publisher",
instructions=(
"You are a publishing agent. "
@@ -111,7 +111,7 @@ publisher = chat_client.as_agent(
)
# Create Summarizer agent - creates final publication report
summarizer = chat_client.as_agent(
summarizer = client.as_agent(
name="Summarizer",
instructions=(
"You are a summarizer agent. "
@@ -15,7 +15,7 @@ import asyncio
import logging
import os
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
@@ -25,11 +25,11 @@ logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)
def create_joker_agent() -> ChatAgent:
def create_joker_agent() -> Agent:
"""Create the Joker agent using Azure OpenAI.
Returns:
ChatAgent: The configured Joker agent
Agent: The configured Joker agent
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name="Joker",
@@ -65,7 +65,7 @@ def create_weather_agent():
"""Create the Weather agent using Azure OpenAI.
Returns:
ChatAgent: The configured Weather agent with weather tool
Agent: The configured Weather agent with weather tool
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=WEATHER_AGENT_NAME,
@@ -78,7 +78,7 @@ def create_math_agent():
"""Create the Math agent using Azure OpenAI.
Returns:
ChatAgent: The configured Math agent with calculation tools
Agent: The configured Math agent with calculation tools
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=MATH_AGENT_NAME,
@@ -18,7 +18,7 @@ import os
from datetime import timedelta
import redis.asyncio as aioredis
from agent_framework import AgentResponseUpdate, ChatAgent
from agent_framework import Agent, AgentResponseUpdate
from agent_framework.azure import (
AgentCallbackContext,
AgentResponseCallbackProtocol,
@@ -143,11 +143,11 @@ class RedisStreamCallback(AgentResponseCallbackProtocol):
logger.error(f"Error writing end-of-stream marker: {ex}", exc_info=True)
def create_travel_agent() -> "ChatAgent":
def create_travel_agent() -> "Agent":
"""Create the TravelPlanner agent using Azure OpenAI.
Returns:
ChatAgent: The configured TravelPlanner agent with travel planning tools.
Agent: The configured TravelPlanner agent with travel planning tools.
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name="TravelPlanner",
@@ -17,7 +17,7 @@ import logging
import os
from collections.abc import Generator
from agent_framework import AgentResponse, ChatAgent
from agent_framework import Agent, AgentResponse
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
@@ -31,14 +31,14 @@ logger = logging.getLogger(__name__)
WRITER_AGENT_NAME = "WriterAgent"
def create_writer_agent() -> "ChatAgent":
def create_writer_agent() -> "Agent":
"""Create the Writer agent using Azure OpenAI.
This agent refines short pieces of text, enhancing initial sentences
and polishing improved versions further.
Returns:
ChatAgent: The configured Writer agent
Agent: The configured Writer agent
"""
instructions = (
"You refine short pieces of text. When given an initial sentence you enhance it;\n"
@@ -18,7 +18,7 @@ import os
from collections.abc import Generator
from typing import Any
from agent_framework import AgentResponse, ChatAgent
from agent_framework import Agent, AgentResponse
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
@@ -33,11 +33,11 @@ PHYSICIST_AGENT_NAME = "PhysicistAgent"
CHEMIST_AGENT_NAME = "ChemistAgent"
def create_physicist_agent() -> "ChatAgent":
def create_physicist_agent() -> "Agent":
"""Create the Physicist agent using Azure OpenAI.
Returns:
ChatAgent: The configured Physicist agent
Agent: The configured Physicist agent
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=PHYSICIST_AGENT_NAME,
@@ -45,11 +45,11 @@ def create_physicist_agent() -> "ChatAgent":
)
def create_chemist_agent() -> "ChatAgent":
def create_chemist_agent() -> "Agent":
"""Create the Chemist agent using Azure OpenAI.
Returns:
ChatAgent: The configured Chemist agent
Agent: The configured Chemist agent
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=CHEMIST_AGENT_NAME,
@@ -18,7 +18,7 @@ import os
from collections.abc import Generator
from typing import Any, cast
from agent_framework import AgentResponse, ChatAgent
from agent_framework import Agent, AgentResponse
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
@@ -51,11 +51,11 @@ class EmailPayload(BaseModel):
email_content: str
def create_spam_agent() -> "ChatAgent":
def create_spam_agent() -> "Agent":
"""Create the Spam Detection agent using Azure OpenAI.
Returns:
ChatAgent: The configured Spam Detection agent
Agent: The configured Spam Detection agent
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=SPAM_AGENT_NAME,
@@ -63,11 +63,11 @@ def create_spam_agent() -> "ChatAgent":
)
def create_email_agent() -> "ChatAgent":
def create_email_agent() -> "Agent":
"""Create the Email Assistant agent using Azure OpenAI.
Returns:
ChatAgent: The configured Email Assistant agent
Agent: The configured Email Assistant agent
"""
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
name=EMAIL_AGENT_NAME,
@@ -19,7 +19,7 @@ from collections.abc import Generator
from datetime import timedelta
from typing import Any, cast
from agent_framework import AgentResponse, ChatAgent
from agent_framework import Agent, AgentResponse
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
from azure.identity import AzureCliCredential, DefaultAzureCredential
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
@@ -54,11 +54,11 @@ class HumanApproval(BaseModel):
feedback: str = ""
def create_writer_agent() -> "ChatAgent":
def create_writer_agent() -> "Agent":
"""Create the Writer agent using Azure OpenAI.
Returns:
ChatAgent: The configured Writer agent
Agent: The configured Writer agent
"""
instructions = (
"You are a professional content writer who creates high-quality articles on various topics. "
@@ -17,7 +17,7 @@ from typing import Any
import openai
import pandas as pd
from agent_framework import ChatAgent, ChatMessage
from agent_framework import Agent, Message
from agent_framework.azure import AzureOpenAIChatClient
from azure.ai.projects import AIProjectClient
from azure.identity import AzureCliCredential
@@ -142,7 +142,7 @@ def run_eval(
async def execute_query_with_self_reflection(
*,
client: openai.OpenAI,
agent: ChatAgent,
agent: Agent,
eval_object: openai.types.EvalCreateResponse,
full_user_query: str,
context: str,
@@ -152,7 +152,7 @@ async def execute_query_with_self_reflection(
Execute a query with self-reflection loop.
Args:
agent: ChatAgent instance to use for generating responses
agent: Agent instance to use for generating responses
full_user_query: Complete prompt including system prompt, user request, and context
context: Context document for groundedness evaluation
evaluator: Groundedness evaluator function
@@ -170,7 +170,7 @@ async def execute_query_with_self_reflection(
- total_groundedness_eval_time: Time spent on evaluations (seconds)
- total_end_to_end_time: Total execution time (seconds)
"""
messages = [ChatMessage("user", [full_user_query])]
messages = [Message("user", [full_user_query])]
best_score = 0
max_score = 5
@@ -223,14 +223,14 @@ async def execute_query_with_self_reflection(
print(f" → No improvement (score: {score}/{max_score}). Trying again...")
# Add to conversation history
messages.append(ChatMessage("assistant", [agent_response]))
messages.append(Message("assistant", [agent_response]))
# Request improvement
reflection_prompt = (
f"The groundedness score of your response is {score}/{max_score}. "
f"Reflect on your answer and improve it to get the maximum score of {max_score} "
)
messages.append(ChatMessage("user", [reflection_prompt]))
messages.append(Message("user", [reflection_prompt]))
end_time = time.time()
latency = end_time - start_time
@@ -2,7 +2,7 @@
import os
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIResponsesClient
from httpx import AsyncClient
@@ -43,8 +43,8 @@ async def api_key_auth_example() -> None:
url=mcp_server_url,
http_client=http_client, # Pass HTTP client with authentication headers
) as mcp_tool,
ChatAgent(
chat_client=OpenAIResponsesClient(),
Agent(
client=OpenAIResponsesClient(),
name="Agent",
instructions="You are a helpful assistant.",
tools=mcp_tool,
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework import ChatAgent, HostedMCPTool
from agent_framework import Agent, HostedMCPTool
from agent_framework.openai import OpenAIResponsesClient
from dotenv import load_dotenv
@@ -54,8 +54,8 @@ async def github_mcp_example() -> None:
)
# 5. Create agent with the GitHub MCP tool
async with ChatAgent(
chat_client=OpenAIResponsesClient(),
async with Agent(
client=OpenAIResponsesClient(),
name="GitHubAgent",
instructions=(
"You are a helpful assistant that can help users interact with GitHub. "
@@ -7,9 +7,9 @@ from typing import Annotated
from agent_framework import (
ChatContext,
ChatMessage,
ChatMiddleware,
ChatResponse,
Message,
MiddlewareTermination,
chat_middleware,
tool,
@@ -69,7 +69,7 @@ class InputObserverMiddleware(ChatMiddleware):
print(f"[InputObserverMiddleware] Total messages: {len(context.messages)}")
# Modify user messages by creating new messages with enhanced text
modified_messages: list[ChatMessage] = []
modified_messages: list[Message] = []
modified_count = 0
for message in context.messages:
@@ -81,7 +81,7 @@ class InputObserverMiddleware(ChatMiddleware):
updated_text = self.replacement
print(f"[InputObserverMiddleware] Updated: '{original_text}' -> '{updated_text}'")
modified_message = ChatMessage(message.role, [updated_text])
modified_message = Message(message.role, [updated_text])
modified_messages.append(modified_message)
modified_count += 1
else:
@@ -118,7 +118,7 @@ async def security_and_override_middleware(
# Override the response instead of calling AI
context.result = ChatResponse(
messages=[
ChatMessage(
Message(
role="assistant",
text="I cannot process requests containing sensitive information. "
"Please rephrase your question without including passwords, secrets, or other "
@@ -10,9 +10,9 @@ from agent_framework import (
AgentContext,
AgentMiddleware,
AgentResponse,
ChatMessage,
FunctionInvocationContext,
FunctionMiddleware,
Message,
tool,
)
from agent_framework.azure import AzureAIAgentClient
@@ -61,7 +61,7 @@ class SecurityAgentMiddleware(AgentMiddleware):
print("[SecurityAgentMiddleware] Security Warning: Detected sensitive information, blocking request.")
# Override the result with warning message
context.result = AgentResponse(
messages=[ChatMessage("assistant", ["Detected sensitive information, the request is blocked."])]
messages=[Message("assistant", ["Detected sensitive information, the request is blocked."])]
)
# Simply don't call call_next() to prevent execution
return
@@ -9,7 +9,7 @@ from agent_framework import (
AgentContext,
AgentMiddleware,
AgentResponse,
ChatMessage,
Message,
MiddlewareTermination,
tool,
)
@@ -62,7 +62,7 @@ class PreTerminationMiddleware(AgentMiddleware):
# Set a custom response
context.result = AgentResponse(
messages=[
ChatMessage(
Message(
role="assistant",
text=(
f"Sorry, I cannot process requests containing '{blocked_word}'. "
@@ -72,8 +72,8 @@ class PreTerminationMiddleware(AgentMiddleware):
]
)
# Set terminate flag to prevent further processing
raise MiddlewareTermination
# Terminate to prevent further processing
raise MiddlewareTermination(result=context.result)
await call_next(context)
@@ -11,10 +11,11 @@ from agent_framework import (
AgentResponse,
AgentResponseUpdate,
ChatContext,
ChatMessage,
ChatResponse,
ChatResponseUpdate,
Message,
ResponseStream,
Role,
tool,
)
from agent_framework.openai import OpenAIResponsesClient
@@ -78,9 +79,9 @@ async def weather_override_middleware(context: ChatContext, call_next: Callable[
context.result.with_transform_hook(_update_hook)
else:
# For non-streaming: just replace with a new message
current_text = context.result.text or "" # type: ignore
current_text = context.result.text if isinstance(context.result, ChatResponse) else ""
custom_message = f"Weather Advisory: [0] {''.join(chunks)} Original message was: {current_text}"
context.result = ChatResponse(messages=[ChatMessage(role="assistant", text=custom_message)])
context.result = ChatResponse(messages=[Message(role=Role.ASSISTANT, text=custom_message)])
async def validate_weather_middleware(context: ChatContext, call_next: Callable[[ChatContext], Awaitable[None]]) -> None:
@@ -95,12 +96,12 @@ async def validate_weather_middleware(context: ChatContext, call_next: Callable[
if context.stream and isinstance(context.result, ResponseStream):
def _append_validation_note(response: ChatResponse) -> ChatResponse:
response.messages.append(ChatMessage(role="assistant", text=validation_note))
response.messages.append(Message(role=Role.ASSISTANT, text=validation_note))
return response
context.result.with_result_hook(_append_validation_note)
context.result.with_finalizer(_append_validation_note)
elif isinstance(context.result, ChatResponse):
context.result.messages.append(ChatMessage(role="assistant", text=validation_note))
context.result.messages.append(Message(role=Role.ASSISTANT, text=validation_note))
async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[AgentContext], Awaitable[None]]) -> None:
@@ -117,7 +118,7 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[A
def _sanitize(response: AgentResponse) -> AgentResponse:
found_prefix = state["found_prefix"]
found_validation = False
cleaned_messages: list[ChatMessage] = []
cleaned_messages: list[Message] = []
for message in response.messages:
text = message.text
@@ -138,7 +139,7 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[A
text = re.sub(r"\[\d+\]\s*", "", text)
cleaned_messages.append(
ChatMessage(
Message(
role=message.role,
text=text.strip(),
author_name=message.author_name,
@@ -153,7 +154,7 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[A
if not found_validation:
raise RuntimeError("Expected validation note not found in agent response.")
cleaned_messages.append(ChatMessage(role="assistant", text=" Agent: OK"))
cleaned_messages.append(Message(role=Role.ASSISTANT, text=" Agent: OK"))
response.messages = cleaned_messages
return response
@@ -172,7 +173,7 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[A
return update
context.result.with_transform_hook(_clean_update)
context.result.with_result_hook(_sanitize)
context.result.with_finalizer(_sanitize)
elif isinstance(context.result, AgentResponse):
context.result = _sanitize(context.result)
@@ -191,19 +192,6 @@ async def main() -> None:
tools=get_weather,
middleware=[agent_cleanup_middleware],
)
# Streaming example
print("\n--- Streaming Example ---")
query = "What's the weather like in Portland?"
print(f"User: {query}")
print("Agent: ", end="", flush=True)
response = agent.run(query, stream=True)
# add the hooks to print what you want to see
response.with_transform_hook(lambda chunk: print(chunk.text, end="", flush=True)).with_result_hook(
lambda final: print(f"\nFinal streamed response: {final.text}", flush=True)
)
# consume the stream to trigger the hooks
await response.get_final_response()
# Non-streaming example
print("\n--- Non-streaming Example ---")
query = "What's the weather like in Seattle?"
@@ -211,6 +199,18 @@ async def main() -> None:
result = await agent.run(query)
print(f"Agent: {result}")
# Streaming example
print("\n--- Streaming Example ---")
query = "What's the weather like in Portland?"
print(f"User: {query}")
print("Agent: ", end="", flush=True)
response = agent.run(query, stream=True)
async for chunk in response:
if chunk.text:
print(chunk.text, end="", flush=True)
print("\n")
print(f"Final Result: {(await response.get_final_response()).text}")
if __name__ == "__main__":
asyncio.run(main())
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
@@ -24,7 +24,7 @@ async def test_image() -> None:
client = AzureOpenAIChatClient(credential=AzureCliCredential())
image_uri = create_sample_image()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What's in this image?"),
@@ -3,7 +3,7 @@
import asyncio
from pathlib import Path
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
@@ -33,7 +33,7 @@ async def test_image() -> None:
client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
image_uri = create_sample_image()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What's in this image?"),
@@ -50,7 +50,7 @@ async def test_pdf() -> None:
client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
pdf_bytes = load_sample_pdf()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What information can you extract from this document?"),
@@ -5,7 +5,7 @@ import base64
import struct
from pathlib import Path
from agent_framework import ChatMessage, Content
from agent_framework import Content, Message
from agent_framework.openai import OpenAIChatClient
ASSETS_DIR = Path(__file__).resolve().parent.parent / "sample_assets"
@@ -45,7 +45,7 @@ async def test_image() -> None:
client = OpenAIChatClient(model_id="gpt-4o")
image_uri = create_sample_image()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What's in this image?"),
@@ -62,7 +62,7 @@ async def test_audio() -> None:
client = OpenAIChatClient(model_id="gpt-4o-audio-preview")
audio_uri = create_sample_audio()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What do you hear in this audio?"),
@@ -79,7 +79,7 @@ async def test_pdf() -> None:
client = OpenAIChatClient(model_id="gpt-4o")
pdf_bytes = load_sample_pdf()
message = ChatMessage(
message = Message(
role="user",
contents=[
Content.from_text(text="What information can you extract from this document?"),
@@ -12,7 +12,7 @@ from opentelemetry.trace.span import format_trace_id
from pydantic import Field
if TYPE_CHECKING:
from agent_framework import ChatClientProtocol
from agent_framework import SupportsChatGetResponse
"""
@@ -51,7 +51,7 @@ async def get_weather(
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) -> None:
async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = False) -> None:
"""Run an AI service.
This function runs an AI service and prints the output.
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.observability import configure_otel_providers, get_tracer
from agent_framework.openai import OpenAIChatClient
from opentelemetry.trace import SpanKind
@@ -39,8 +39,8 @@ async def main():
with get_tracer().start_as_current_span("Scenario: Agent Chat", kind=SpanKind.CLIENT) as current_span:
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
agent = ChatAgent(
chat_client=OpenAIChatClient(),
agent = Agent(
client=OpenAIChatClient(),
tools=get_weather,
name="WeatherAgent",
instructions="You are a weather assistant.",
@@ -16,7 +16,7 @@ from random import randint
from typing import Annotated
import dotenv
from agent_framework import ChatAgent, tool
from agent_framework import Agent, 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
@@ -30,7 +30,7 @@ from pydantic import Field
This sample shows you can can setup telemetry in Microsoft Foundry for a custom agent.
First ensure you have a Foundry workspace with Application Insights enabled.
And use the Operate tab to Register an Agent.
Set the OpenTelemetry agent ID to the value used below in the ChatAgent creation: `weather-agent` (or change both).
Set the OpenTelemetry agent ID to the value used below in the Agent creation: `weather-agent` (or change both).
The sample uses the Azure Monitor OpenTelemetry exporter to send traces to Application Insights.
So ensure you have the `azure-monitor-opentelemetry` package installed.
"""
@@ -85,8 +85,8 @@ async def main():
with get_tracer().start_as_current_span("Weather Agent Chat", kind=SpanKind.CLIENT) as current_span:
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
agent = ChatAgent(
chat_client=OpenAIResponsesClient(),
agent = Agent(
client=OpenAIResponsesClient(),
tools=get_weather,
name="WeatherAgent",
instructions="You are a weather assistant.",
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
import dotenv
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.azure import AzureAIClient
from agent_framework.observability import get_tracer
from azure.ai.projects.aio import AIProjectClient
@@ -56,8 +56,8 @@ async def main():
with get_tracer().start_as_current_span("Single Agent Chat", kind=SpanKind.CLIENT) as current_span:
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
agent = ChatAgent(
chat_client=client,
agent = Agent(
client=client,
tools=get_weather,
name="WeatherAgent",
instructions="You are a weather assistant.",
@@ -14,7 +14,7 @@ from opentelemetry.trace.span import format_trace_id
from pydantic import Field
if TYPE_CHECKING:
from agent_framework import ChatClientProtocol
from agent_framework import SupportsChatGetResponse
"""
This sample, show how you can configure observability of an application via the
@@ -28,7 +28,7 @@ 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", "tool", "all"]
SCENARIOS = ["client", "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.
@@ -42,7 +42,7 @@ async def get_weather(
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) -> None:
async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = False) -> None:
"""Run an AI service.
This function runs an AI service and prints the output.
@@ -97,7 +97,7 @@ async def run_tool() -> None:
print(f"Weather in Amsterdam:\n{weather}")
async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "all"] = "all"):
async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "all"):
"""Run the selected scenario(s)."""
# This will enable tracing and create the necessary tracing, logging and metrics providers
@@ -113,10 +113,10 @@ async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "a
if scenario == "tool" or scenario == "all":
with suppress(Exception):
await run_tool()
if scenario == "chat_client_stream" or scenario == "all":
if scenario == "client_stream" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=True)
if scenario == "chat_client" or scenario == "all":
if scenario == "client" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=False)
@@ -14,7 +14,7 @@ from opentelemetry.trace.span import format_trace_id
from pydantic import Field
if TYPE_CHECKING:
from agent_framework import ChatClientProtocol
from agent_framework import SupportsChatGetResponse
"""
This sample shows how you can configure observability with custom exporters passed directly
@@ -28,7 +28,7 @@ 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", "tool", "all"]
SCENARIOS = ["client", "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.
@@ -42,7 +42,7 @@ async def get_weather(
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) -> None:
async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = False) -> None:
"""Run an AI service.
This function runs an AI service and prints the output.
@@ -97,7 +97,7 @@ async def run_tool() -> None:
print(f"Weather in Amsterdam:\n{weather}")
async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "all"] = "all"):
async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "all"):
"""Run the selected scenario(s)."""
# Setup the logging with the more complete format
@@ -148,10 +148,10 @@ async def main(scenario: Literal["chat_client", "chat_client_stream", "tool", "a
if scenario == "tool" or scenario == "all":
with suppress(Exception):
await run_tool()
if scenario == "chat_client_stream" or scenario == "all":
if scenario == "client_stream" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=True)
if scenario == "chat_client" or scenario == "all":
if scenario == "client" or scenario == "all":
with suppress(Exception):
await run_chat_client(client, stream=False)
@@ -30,32 +30,29 @@ from agent_framework.orchestrations import (
| Sample | File | Concepts |
| ------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| Concurrent Orchestration (Default Aggregator) | [concurrent_agents.py](./concurrent_agents.py) | Fan-out to multiple agents; fan-in with default aggregator returning combined ChatMessages |
| Concurrent Orchestration (Default Aggregator) | [concurrent_agents.py](./concurrent_agents.py) | Fan-out to multiple agents; fan-in with default aggregator returning combined Messages |
| Concurrent Orchestration (Custom Aggregator) | [concurrent_custom_aggregator.py](./concurrent_custom_aggregator.py) | Override aggregator via callback; summarize results with an LLM |
| Concurrent Orchestration (Custom Agent Executors) | [concurrent_custom_agent_executors.py](./concurrent_custom_agent_executors.py) | Child executors own ChatAgents; concurrent fan-out/fan-in via ConcurrentBuilder |
| Concurrent Orchestration (Participant Factory) | [concurrent_participant_factory.py](./concurrent_participant_factory.py) | Use participant factories for state isolation between workflow instances |
| Concurrent Orchestration (Custom Agent Executors) | [concurrent_custom_agent_executors.py](./concurrent_custom_agent_executors.py) | Child executors own Agents; concurrent fan-out/fan-in via ConcurrentBuilder |
| Group Chat with Agent Manager | [group_chat_agent_manager.py](./group_chat_agent_manager.py) | Agent-based manager using `with_orchestrator(agent=)` to select next speaker |
| Group Chat Philosophical Debate | [group_chat_philosophical_debate.py](./group_chat_philosophical_debate.py) | Agent manager moderates long-form, multi-round debate across diverse participants |
| Group Chat with Simple Function Selector | [group_chat_simple_selector.py](./group_chat_simple_selector.py) | Group chat with a simple function selector for next speaker |
| Handoff (Simple) | [handoff_simple.py](./handoff_simple.py) | Single-tier routing: triage agent routes to specialists, control returns to user after each specialist response |
| Handoff (Autonomous) | [handoff_autonomous.py](./handoff_autonomous.py) | Autonomous mode: specialists iterate independently until invoking a handoff tool using `.with_autonomous_mode()` |
| Handoff (Participant Factory) | [handoff_participant_factory.py](./handoff_participant_factory.py) | Use participant factories for state isolation between workflow instances |
| Handoff with Code Interpreter | [handoff_with_code_interpreter_file.py](./handoff_with_code_interpreter_file.py) | Retrieve file IDs from code interpreter output in handoff workflow |
| Magentic Workflow (Multi-Agent) | [magentic.py](./magentic.py) | Orchestrate multiple agents with Magentic manager and streaming |
| Magentic + Human Plan Review | [magentic_human_plan_review.py](./magentic_human_plan_review.py) | Human reviews/updates the plan before execution |
| Magentic + Checkpoint Resume | [magentic_checkpoint.py](./magentic_checkpoint.py) | Resume Magentic orchestration from saved checkpoints |
| Sequential Orchestration (Agents) | [sequential_agents.py](./sequential_agents.py) | Chain agents sequentially with shared conversation context |
| Sequential Orchestration (Custom Executor) | [sequential_custom_executors.py](./sequential_custom_executors.py) | Mix agents with a summarizer that appends a compact summary |
| Sequential Orchestration (Participant Factories) | [sequential_participant_factory.py](./sequential_participant_factory.py) | Use participant factories for state isolation between workflow instances |
## Tips
**Magentic checkpointing tip**: Treat `MagenticBuilder.participants` keys as stable identifiers. When resuming from a checkpoint, the rebuilt workflow must reuse the same participant names; otherwise the checkpoint cannot be applied and the run will fail fast.
**Handoff workflow tip**: Handoff workflows maintain the full conversation history including any `ChatMessage.additional_properties` emitted by your agents. This ensures routing metadata remains intact across all agent transitions. For specialist-to-specialist handoffs, use `.add_handoff(source, targets)` to configure which agents can route to which others with a fluent, type-safe API.
**Handoff workflow tip**: Handoff workflows maintain the full conversation history including any `Message.additional_properties` emitted by your agents. This ensures routing metadata remains intact across all agent transitions. For specialist-to-specialist handoffs, use `.add_handoff(source, targets)` to configure which agents can route to which others with a fluent, type-safe API.
**Sequential orchestration note**: Sequential orchestration uses a few small adapter nodes for plumbing:
- `input-conversation` normalizes input to `list[ChatMessage]`
- `input-conversation` normalizes input to `list[Message]`
- `to-conversation:<participant>` converts agent responses into the shared conversation
- `complete` publishes the final output event (type='output')
@@ -3,7 +3,7 @@
import asyncio
from typing import Any
from agent_framework import ChatMessage
from agent_framework import Message
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import ConcurrentBuilder
from azure.identity import AzureCliCredential
@@ -14,7 +14,7 @@ Sample: Concurrent fan-out/fan-in (agent-only API) with default aggregator
Build a high-level concurrent workflow using ConcurrentBuilder and three domain agents.
The default dispatcher fans out the same user prompt to all agents in parallel.
The default aggregator fans in their results and yields output containing
a list[ChatMessage] representing the concatenated conversations from all agents.
a list[Message] representing the concatenated conversations from all agents.
Demonstrates:
- Minimal wiring with ConcurrentBuilder(participants=[...]).build()
@@ -29,9 +29,9 @@ Prerequisites:
async def main() -> None:
# 1) Create three domain agents using AzureOpenAIChatClient
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
researcher = chat_client.as_agent(
researcher = client.as_agent(
instructions=(
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
" opportunities, and risks."
@@ -39,7 +39,7 @@ async def main() -> None:
name="researcher",
)
marketer = chat_client.as_agent(
marketer = client.as_agent(
instructions=(
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
" aligned to the prompt."
@@ -47,7 +47,7 @@ async def main() -> None:
name="marketer",
)
legal = chat_client.as_agent(
legal = client.as_agent(
instructions=(
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
" based on the prompt."
@@ -66,7 +66,7 @@ async def main() -> None:
if outputs:
print("===== Final Aggregated Conversation (messages) =====")
for output in outputs:
messages: list[ChatMessage] | Any = output
messages: list[Message] | Any = output
for i, msg in enumerate(messages, start=1):
name = msg.author_name if msg.author_name else "user"
print(f"{'-' * 60}\n\n{i:02d} [{name}]:\n{msg.text}")
@@ -4,11 +4,11 @@ import asyncio
from typing import Any
from agent_framework import (
Agent,
AgentExecutorRequest,
AgentExecutorResponse,
ChatAgent,
ChatMessage,
Executor,
Message,
WorkflowContext,
handler,
)
@@ -20,15 +20,15 @@ from azure.identity import AzureCliCredential
Sample: Concurrent Orchestration with Custom Agent Executors
This sample shows a concurrent fan-out/fan-in pattern using child Executor classes
that each own their ChatAgent. The executors accept AgentExecutorRequest inputs
that each own their Agent. The executors accept AgentExecutorRequest inputs
and emit AgentExecutorResponse outputs, which allows reuse of the high-level
ConcurrentBuilder API and the default aggregator.
Demonstrates:
- Executors that create their ChatAgent in __init__ (via AzureOpenAIChatClient)
- Executors that create their Agent in __init__ (via AzureOpenAIChatClient)
- A @handler that converts AgentExecutorRequest -> AgentExecutorResponse
- ConcurrentBuilder(participants=[...]) to build fan-out/fan-in
- Default aggregator returning list[ChatMessage] (one user + one assistant per agent)
- Default aggregator returning list[Message] (one user + one assistant per agent)
- Workflow completion when all participants become idle
Prerequisites:
@@ -37,10 +37,10 @@ Prerequisites:
class ResearcherExec(Executor):
agent: ChatAgent
agent: Agent
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "researcher"):
self.agent = chat_client.as_agent(
def __init__(self, client: AzureOpenAIChatClient, id: str = "researcher"):
self.agent = client.as_agent(
instructions=(
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
" opportunities, and risks."
@@ -57,10 +57,10 @@ class ResearcherExec(Executor):
class MarketerExec(Executor):
agent: ChatAgent
agent: Agent
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "marketer"):
self.agent = chat_client.as_agent(
def __init__(self, client: AzureOpenAIChatClient, id: str = "marketer"):
self.agent = client.as_agent(
instructions=(
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
" aligned to the prompt."
@@ -77,10 +77,10 @@ class MarketerExec(Executor):
class LegalExec(Executor):
agent: ChatAgent
agent: Agent
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "legal"):
self.agent = chat_client.as_agent(
def __init__(self, client: AzureOpenAIChatClient, id: str = "legal"):
self.agent = client.as_agent(
instructions=(
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
" based on the prompt."
@@ -97,11 +97,11 @@ class LegalExec(Executor):
async def main() -> None:
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
researcher = ResearcherExec(chat_client)
marketer = MarketerExec(chat_client)
legal = LegalExec(chat_client)
researcher = ResearcherExec(client)
marketer = MarketerExec(client)
legal = LegalExec(client)
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
@@ -110,7 +110,7 @@ async def main() -> None:
if outputs:
print("===== Final Aggregated Conversation (messages) =====")
messages: list[ChatMessage] | Any = outputs[0] # Get the first (and typically only) output
messages: list[Message] | Any = outputs[0] # Get the first (and typically only) output
for i, msg in enumerate(messages, start=1):
name = msg.author_name if msg.author_name else "user"
print(f"{'-' * 60}\n\n{i:02d} [{name}]:\n{msg.text}")
@@ -3,7 +3,7 @@
import asyncio
from typing import Any
from agent_framework import ChatMessage
from agent_framework import Message
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import ConcurrentBuilder
from azure.identity import AzureCliCredential
@@ -20,7 +20,7 @@ The workflow completes when all participants become idle.
Demonstrates:
- ConcurrentBuilder(participants=[...]).with_aggregator(callback)
- Fan-out to agents and fan-in at an aggregator
- Aggregation implemented via an LLM call (chat_client.get_response)
- Aggregation implemented via an LLM call (client.get_response)
- Workflow output yielded with the synthesized summary string
Prerequisites:
@@ -29,23 +29,23 @@ Prerequisites:
async def main() -> None:
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
researcher = chat_client.as_agent(
researcher = client.as_agent(
instructions=(
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
" opportunities, and risks."
),
name="researcher",
)
marketer = chat_client.as_agent(
marketer = client.as_agent(
instructions=(
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
" aligned to the prompt."
),
name="marketer",
)
legal = chat_client.as_agent(
legal = client.as_agent(
instructions=(
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
" based on the prompt."
@@ -66,16 +66,16 @@ async def main() -> None:
expert_sections.append(f"{getattr(r, 'executor_id', 'expert')}: (error: {type(e).__name__}: {e})")
# Ask the model to synthesize a concise summary of the experts' outputs
system_msg = ChatMessage(
system_msg = Message(
"system",
text=(
"You are a helpful assistant that consolidates multiple domain expert outputs "
"into one cohesive, concise summary with clear takeaways. Keep it under 200 words."
),
)
user_msg = ChatMessage("user", text="\n\n".join(expert_sections))
user_msg = Message("user", text="\n\n".join(expert_sections))
response = await chat_client.get_response([system_msg, user_msg])
response = await client.get_response([system_msg, user_msg])
# Return the model's final assistant text as the completion result
return response.messages[-1].text if response.messages else ""
@@ -83,7 +83,7 @@ async def main() -> None:
# - participants([...]) accepts SupportsAgentRun (agents) or Executor instances.
# Each participant becomes a parallel branch (fan-out) from an internal dispatcher.
# - with_aggregator(...) overrides the default aggregator:
# • Default aggregator -> returns list[ChatMessage] (one user + one assistant per agent)
# • Default aggregator -> returns list[Message] (one user + one assistant per agent)
# • Custom callback -> return value becomes workflow output (string here)
# The callback can be sync or async; it receives list[AgentExecutorResponse].
workflow = (
@@ -4,9 +4,9 @@ import asyncio
from typing import cast
from agent_framework import (
Agent,
AgentResponseUpdate,
ChatAgent,
ChatMessage,
Message,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder
@@ -17,7 +17,7 @@ Sample: Group Chat with Agent-Based Manager
What it does:
- Demonstrates the new set_manager() API for agent-based coordination
- Manager is a full ChatAgent with access to tools, context, and observability
- Manager is a full Agent with access to tools, context, and observability
- Coordinates a researcher and writer agent to solve tasks collaboratively
Prerequisites:
@@ -36,32 +36,32 @@ Guidelines:
async def main() -> None:
# Create a chat client using Azure OpenAI and Azure CLI credentials for all agents
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
# Orchestrator agent that manages the conversation
# Note: This agent (and the underlying chat client) must support structured outputs.
# The group chat workflow relies on this to parse the orchestrator's decisions.
# `response_format` is set internally by the GroupChat workflow when the agent is invoked.
orchestrator_agent = ChatAgent(
orchestrator_agent = Agent(
name="Orchestrator",
description="Coordinates multi-agent collaboration by selecting speakers",
instructions=ORCHESTRATOR_AGENT_INSTRUCTIONS,
chat_client=chat_client,
client=client,
)
# Participant agents
researcher = ChatAgent(
researcher = Agent(
name="Researcher",
description="Collects relevant background information",
instructions="Gather concise facts that help a teammate answer the question.",
chat_client=chat_client,
client=client,
)
writer = ChatAgent(
writer = Agent(
name="Writer",
description="Synthesizes polished answers from gathered information",
instructions="Compose clear and structured answers using any notes provided.",
chat_client=chat_client,
client=client,
)
# Build the group chat workflow
@@ -103,7 +103,7 @@ async def main() -> None:
print(data.text, end="", flush=True)
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
outputs = cast(list[Message], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
@@ -5,9 +5,9 @@ import logging
from typing import cast
from agent_framework import (
Agent,
AgentResponseUpdate,
ChatAgent,
ChatMessage,
Message,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder
@@ -48,7 +48,7 @@ def _get_chat_client() -> AzureOpenAIChatClient:
async def main() -> None:
# Create debate moderator with structured output for speaker selection
# Note: Participant names and descriptions are automatically injected by the orchestrator
moderator = ChatAgent(
moderator = Agent(
name="Moderator",
description="Guides philosophical discussion by selecting next speaker",
instructions="""
@@ -75,10 +75,10 @@ Finish when:
In your final_message, provide a brief synthesis highlighting key themes that emerged.
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
farmer = ChatAgent(
farmer = Agent(
name="Farmer",
description="A rural farmer from Southeast Asia",
instructions="""
@@ -91,10 +91,10 @@ Share your perspective authentically. Feel free to:
- Use concrete examples from your experience
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
developer = ChatAgent(
developer = Agent(
name="Developer",
description="An urban software developer from the United States",
instructions="""
@@ -107,10 +107,10 @@ Share your perspective authentically. Feel free to:
- Use concrete examples from your experience
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
teacher = ChatAgent(
teacher = Agent(
name="Teacher",
description="A retired history teacher from Eastern Europe",
instructions="""
@@ -124,10 +124,10 @@ Share your perspective authentically. Feel free to:
- Use concrete examples from history or your teaching experience
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
activist = ChatAgent(
activist = Agent(
name="Activist",
description="A young activist from South America",
instructions="""
@@ -140,10 +140,10 @@ Share your perspective authentically. Feel free to:
- Use concrete examples from your activism
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
spiritual_leader = ChatAgent(
spiritual_leader = Agent(
name="SpiritualLeader",
description="A spiritual leader from the Middle East",
instructions="""
@@ -156,10 +156,10 @@ Share your perspective authentically. Feel free to:
- Use examples from spiritual teachings or community work
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
artist = ChatAgent(
artist = Agent(
name="Artist",
description="An artist from Africa",
instructions="""
@@ -172,10 +172,10 @@ Share your perspective authentically. Feel free to:
- Use examples from your art or cultural traditions
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
immigrant = ChatAgent(
immigrant = Agent(
name="Immigrant",
description="An immigrant entrepreneur from Asia living in Canada",
instructions="""
@@ -188,10 +188,10 @@ Share your perspective authentically. Feel free to:
- Use examples from your immigrant and entrepreneurial journey
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
doctor = ChatAgent(
doctor = Agent(
name="Doctor",
description="A doctor from Scandinavia",
instructions="""
@@ -204,7 +204,7 @@ Share your perspective authentically. Feel free to:
- Use examples from healthcare and societal systems
- Keep responses thoughtful but concise (2-4 sentences)
""",
chat_client=_get_chat_client(),
client=_get_chat_client(),
)
# termination_condition: stop after 10 assistant messages
@@ -255,7 +255,7 @@ Share your perspective authentically. Feel free to:
print(data.text, end="", flush=True)
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
outputs = cast(list[Message], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
@@ -4,9 +4,9 @@ import asyncio
from typing import cast
from agent_framework import (
Agent,
AgentResponseUpdate,
ChatAgent,
ChatMessage,
Message,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder, GroupChatState
@@ -33,20 +33,20 @@ def round_robin_selector(state: GroupChatState) -> str:
async def main() -> None:
# Create a chat client using Azure OpenAI and Azure CLI credentials for all agents
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
# Participant agents
expert = ChatAgent(
expert = Agent(
name="PythonExpert",
instructions=(
"You are an expert in Python in a workgroup. "
"Your job is to answer Python related questions and refine your answer "
"based on feedback from all the other participants."
),
chat_client=chat_client,
client=client,
)
verifier = ChatAgent(
verifier = Agent(
name="AnswerVerifier",
instructions=(
"You are a programming expert in a workgroup. "
@@ -54,10 +54,10 @@ async def main() -> None:
"out statements that are technically true but practically dangerous."
"If there is nothing woth pointing out, respond with 'The answer looks good to me.'"
),
chat_client=chat_client,
client=client,
)
clarifier = ChatAgent(
clarifier = Agent(
name="AnswerClarifier",
instructions=(
"You are an accessibility expert in a workgroup. "
@@ -65,10 +65,10 @@ async def main() -> None:
"out jargons or complex terms that may be difficult for a beginner to understand."
"If there is nothing worth pointing out, respond with 'The answer looks clear to me.'"
),
chat_client=chat_client,
client=client,
)
skeptic = ChatAgent(
skeptic = Agent(
name="Skeptic",
instructions=(
"You are a devil's advocate in a workgroup. "
@@ -76,7 +76,7 @@ async def main() -> None:
"out caveats, exceptions, and alternative perspectives."
"If there is nothing worth pointing out, respond with 'I have no further questions.'"
),
chat_client=chat_client,
client=client,
)
# Build the group chat workflow
@@ -124,7 +124,7 @@ async def main() -> None:
print(data.text, end="", flush=True)
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
outputs = cast(list[Message], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
@@ -5,9 +5,9 @@ import logging
from typing import cast
from agent_framework import (
Agent,
AgentResponseUpdate,
ChatAgent,
ChatMessage,
Message,
resolve_agent_id,
)
from agent_framework.azure import AzureOpenAIChatClient
@@ -37,10 +37,10 @@ Key Concepts:
def create_agents(
chat_client: AzureOpenAIChatClient,
) -> tuple[ChatAgent, ChatAgent, ChatAgent]:
client: AzureOpenAIChatClient,
) -> tuple[Agent, Agent, Agent]:
"""Create coordinator and specialists for autonomous iteration."""
coordinator = chat_client.as_agent(
coordinator = client.as_agent(
instructions=(
"You are a coordinator. You break down a user query into a research task and a summary task. "
"Assign the two tasks to the appropriate specialists, one after the other."
@@ -48,7 +48,7 @@ def create_agents(
name="coordinator",
)
research_agent = chat_client.as_agent(
research_agent = client.as_agent(
instructions=(
"You are a research specialist that explores topics thoroughly using web search. "
"When given a research task, break it down into multiple aspects and explore each one. "
@@ -60,7 +60,7 @@ def create_agents(
name="research_agent",
)
summary_agent = chat_client.as_agent(
summary_agent = client.as_agent(
instructions=(
"You summarize research findings. Provide a concise, well-organized summary. When done, return "
"control to the coordinator."
@@ -73,8 +73,8 @@ def create_agents(
async def main() -> None:
"""Run an autonomous handoff workflow with specialist iteration enabled."""
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
coordinator, research_agent, summary_agent = create_agents(chat_client)
client = AzureOpenAIChatClient(credential=AzureCliCredential())
coordinator, research_agent, summary_agent = create_agents(client)
# Build the workflow with autonomous mode
# In autonomous mode, agents continue iterating until they invoke a handoff tool
@@ -129,7 +129,7 @@ async def main() -> None:
print(data.text, end="", flush=True)
elif event.type == "output":
# The output of the handoff workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
outputs = cast(list[Message], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
@@ -4,9 +4,9 @@ import asyncio
from typing import Annotated, cast
from agent_framework import (
Agent,
AgentResponse,
ChatAgent,
ChatMessage,
Message,
WorkflowEvent,
WorkflowRunState,
tool,
@@ -54,17 +54,17 @@ def process_return(order_number: Annotated[str, "Order number to process return
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAgent, ChatAgent, ChatAgent]:
def create_agents(client: AzureOpenAIChatClient) -> tuple[Agent, Agent, Agent, Agent]:
"""Create and configure the triage and specialist agents.
Args:
chat_client: The AzureOpenAIChatClient to use for creating agents.
client: The AzureOpenAIChatClient to use for creating agents.
Returns:
Tuple of (triage_agent, refund_agent, order_agent, return_agent)
"""
# Triage agent: Acts as the frontline dispatcher
triage_agent = chat_client.as_agent(
triage_agent = client.as_agent(
instructions=(
"You are frontline support triage. Route customer issues to the appropriate specialist agents "
"based on the problem described."
@@ -73,7 +73,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
)
# Refund specialist: Handles refund requests
refund_agent = chat_client.as_agent(
refund_agent = client.as_agent(
instructions="You process refund requests.",
name="refund_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
@@ -81,7 +81,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
)
# Order/shipping specialist: Resolves delivery issues
order_agent = chat_client.as_agent(
order_agent = client.as_agent(
instructions="You handle order and shipping inquiries.",
name="order_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
@@ -89,7 +89,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
)
# Return specialist: Handles return requests
return_agent = chat_client.as_agent(
return_agent = client.as_agent(
instructions="You manage product return requests.",
name="return_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
@@ -138,7 +138,7 @@ def _handle_events(events: list[WorkflowEvent]) -> list[WorkflowEvent[HandoffAge
print(f"- {speaker}: {message.text}")
elif event.type == "output":
# The output of the handoff workflow is a collection of chat messages from all participants
conversation = cast(list[ChatMessage], event.data)
conversation = cast(list[Message], event.data)
if isinstance(conversation, list):
print("\n=== Final Conversation Snapshot ===")
for message in conversation:
@@ -189,10 +189,10 @@ async def main() -> None:
replace the scripted_responses with actual user input collection.
"""
# Initialize the Azure OpenAI chat client
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
client = AzureOpenAIChatClient(credential=AzureCliCredential())
# Create all agents: triage + specialists
triage, refund, order, support = create_agents(chat_client)
triage, refund, order, support = create_agents(client)
# Build the handoff workflow
# - participants: All agents that can participate in the workflow
@@ -31,10 +31,10 @@ from contextlib import asynccontextmanager
from typing import cast
from agent_framework import (
Agent,
AgentResponseUpdate,
ChatAgent,
ChatMessage,
HostedCodeInterpreterTool,
Message,
WorkflowEvent,
WorkflowRunState,
)
@@ -83,7 +83,7 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[WorkflowEvent[Hand
file_ids.append(file_id)
print(f"[Found file annotation: file_id={file_id}]")
elif event.type == "output":
conversation = cast(list[ChatMessage], event.data)
conversation = cast(list[Message], event.data)
if isinstance(conversation, list):
print("\n=== Final Conversation Snapshot ===")
for message in conversation:
@@ -95,7 +95,7 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[WorkflowEvent[Hand
@asynccontextmanager
async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tuple[ChatAgent, ChatAgent]]:
async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tuple[Agent, Agent]]:
"""Create agents using V1 AzureAIAgentClient."""
from agent_framework.azure import AzureAIAgentClient
@@ -122,7 +122,7 @@ async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tupl
@asynccontextmanager
async def create_agents_v2(credential: AzureCliCredential) -> AsyncIterator[tuple[ChatAgent, ChatAgent]]:
async def create_agents_v2(credential: AzureCliCredential) -> AsyncIterator[tuple[Agent, Agent]]:
"""Create agents using V2 AzureAIClient.
Each agent needs its own client instance because the V2 client binds

Some files were not shown because too many files have changed in this diff Show More