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-11 00:04:32 +01:00
committed by GitHub
Unverified
parent a4c9e43afb
commit 0521f5bed8
418 changed files with 5385 additions and 5389 deletions
+14 -14
View File
@@ -35,9 +35,9 @@ python client_advanced.py
**Note:** This example shows direct `AGUIChatClient` usage. Tool execution and conversation continuity depend on server-side configuration and capabilities.
### ChatAgent Integration (`client_with_agent.py`)
### Agent Integration (`client_with_agent.py`)
Best practice example using `ChatAgent` wrapper with **AgentThread**
Best practice example using `Agent` wrapper with **AgentThread**
- **AgentThread** maintains conversation state
- Client-side conversation history management via `thread.message_store`
- **Hybrid tool execution**: client-side + server-side tools simultaneously
@@ -77,7 +77,7 @@ The AG-UI protocol supports two approaches to conversation history:
- Full message history sent with each request
- Works with any AG-UI server (stateful or stateless)
The `ChatAgent` wrapper (used in client_with_agent.py) collects messages from local storage and sends the full history to `AGUIChatClient`, which then forwards everything to the server.
The `Agent` wrapper (used in client_with_agent.py) collects messages from local storage and sends the full history to `AGUIChatClient`, which then forwards everything to the server.
### Tool/Function Calling
@@ -91,14 +91,14 @@ Client defines: Server defines:
User: "What's the weather in SF and what time is it?"
ChatAgent sends: full history + tool definitions for get_weather, read_sensors
Agent sends: full history + tool definitions for get_weather, read_sensors
Server LLM decides: "I need get_weather('SF') and get_current_time()"
Server executes get_current_time() → "2025-11-11 14:30:00 UTC"
Server sends function call request → get_weather('SF')
ChatAgent intercepts get_weather call → executes locally
Agent intercepts get_weather call → executes locally
Client sends result → "Sunny, 72°F"
@@ -110,7 +110,7 @@ Client receives final response
**How it works:**
1. **Client-Side Tools** (`client_with_agent.py`):
- Tools defined in ChatAgent's `tools` parameter execute locally
- Tools defined in Agent's `tools` parameter execute locally
- Tool metadata (name, description, schema) sent to server for planning
- When server requests client tool → client intercepts → executes locally → sends result
@@ -126,7 +126,7 @@ Client receives final response
- Client tools execute client-side
**Direct AGUIChatClient Usage** (client_advanced.py):
Even without ChatAgent wrapper, client-side tools work:
Even without Agent wrapper, client-side tools work:
- Tools passed in ChatOptions execute locally
- Server can also have its own tools
- Hybrid execution works automatically
@@ -184,7 +184,7 @@ Create a file named `server.py`:
import os
from agent_framework import ChatAgent
from agent_framework import Agent
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.ag_ui import add_agent_framework_fastapi_endpoint
from fastapi import FastAPI
@@ -202,10 +202,10 @@ if not api_key:
raise ValueError("AZURE_OPENAI_API_KEY environment variable is required")
# Create the AI agent
agent = ChatAgent(
agent = Agent(
name="AGUIAssistant",
instructions="You are a helpful assistant.",
chat_client=AzureOpenAIChatClient(
client=AzureOpenAIChatClient(
endpoint=endpoint,
deployment_name=deployment_name,
api_key=api_key,
@@ -227,7 +227,7 @@ if __name__ == "__main__":
### Key Concepts
- **`add_agent_framework_fastapi_endpoint`**: Registers the AG-UI endpoint with automatic request/response handling and SSE streaming
- **`ChatAgent`**: The agent that will handle incoming requests
- **`Agent`**: The agent that will handle incoming requests
- **FastAPI Integration**: Uses FastAPI's native async support for streaming responses
- **Instructions**: The agent is created with default instructions, which can be overridden by client messages
- **Configuration**: `AzureOpenAIChatClient` can read from environment variables (`AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, `AZURE_OPENAI_API_KEY`) or accept parameters directly
@@ -236,10 +236,10 @@ if __name__ == "__main__":
```python
# No need to read environment variables manually
agent = ChatAgent(
agent = Agent(
name="AGUIAssistant",
instructions="You are a helpful assistant.",
chat_client=AzureOpenAIChatClient(), # Reads from environment automatically
client=AzureOpenAIChatClient(), # Reads from environment automatically
)
```
@@ -354,7 +354,7 @@ if __name__ == "__main__":
- **Thread Management**: Pass `thread_id` in metadata to maintain conversation context across requests
- **Streaming Responses**: Use `get_response(..., stream=True)` for real-time streaming or `get_response(..., stream=False)` for non-streaming
- **Context Manager**: Use `async with` for automatic cleanup of HTTP connections
- **Standard Interface**: Works with all Agent Framework patterns (ChatAgent, tools, etc.)
- **Standard Interface**: Works with all Agent Framework patterns (Agent, tools, etc.)
- **Hybrid Tool Execution**: Supports both client-side and server-side tools executing together in the same conversation
### Configure and Run the Client
@@ -114,15 +114,15 @@ async def non_streaming_example(client: AGUIChatClient, thread_id: str | None =
async def tool_example(client: AGUIChatClient, thread_id: str | None = None):
"""Demonstrate sending tool definitions to the server.
IMPORTANT: When using AGUIChatClient directly (without ChatAgent wrapper):
IMPORTANT: When using AGUIChatClient directly (without Agent wrapper):
- Tools are sent as DEFINITIONS only
- No automatic client-side execution (no function invocation middleware)
- Server must have matching tool implementations to execute them
For CLIENT-SIDE tool execution (like .NET AGUIClient sample):
- Use ChatAgent wrapper with tools
- Use Agent wrapper with tools
- See client_with_agent.py for the hybrid pattern
- ChatAgent middleware intercepts and executes client tools locally
- Agent middleware intercepts and executes client tools locally
- Server can have its own tools that execute server-side
- Both client and server tools work together in same conversation
@@ -186,7 +186,7 @@ async def conversation_example(client: AGUIChatClient):
# Check if context was maintained
if "alice" not in response2.text.lower():
print("\n[Note: Server may not maintain thread context - consider using ChatAgent for history management]")
print("\n[Note: Server may not maintain thread context - consider using Agent for history management]")
# Third turn
print("\nUser: Can you also tell me what 10 * 5 is?\n")
@@ -1,6 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""Example showing ChatAgent with AGUIChatClient for hybrid tool execution.
"""Example showing Agent with AGUIChatClient for hybrid tool execution.
This demonstrates the HYBRID pattern matching .NET AGUIClient implementation:
@@ -24,7 +24,7 @@ import asyncio
import logging
import os
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.ag_ui import AGUIChatClient
# Enable debug logging
@@ -55,7 +55,7 @@ def get_weather(location: str) -> str:
async def main():
"""Demonstrate ChatAgent + AGUIChatClient hybrid tool execution.
"""Demonstrate Agent + AGUIChatClient hybrid tool execution.
This matches the .NET pattern from Program.cs where:
- AIAgent agent = chatClient.CreateAIAgent(tools: [...])
@@ -63,14 +63,14 @@ async def main():
- RunStreamingAsync(messages, thread)
Python equivalent:
- agent = ChatAgent(chat_client=AGUIChatClient(...), tools=[...])
- agent = Agent(client=AGUIChatClient(...), tools=[...])
- thread = agent.get_new_thread() # Creates thread with message_store
- agent.run(message, stream=True, thread=thread) # Thread accumulates history
"""
server_url = os.environ.get("AGUI_SERVER_URL", "http://127.0.0.1:5100/")
print("=" * 70)
print("ChatAgent + AGUIChatClient: Hybrid Tool Execution")
print("Agent + AGUIChatClient: Hybrid Tool Execution")
print("=" * 70)
print(f"\nServer: {server_url}")
print("\nThis example demonstrates:")
@@ -82,11 +82,11 @@ async def main():
try:
# Create remote client in async context manager
async with AGUIChatClient(endpoint=server_url) as remote_client:
# Wrap in ChatAgent for conversation history management
agent = ChatAgent(
# Wrap in Agent for conversation history management
agent = Agent(
name="remote_assistant",
instructions="You are a helpful assistant. Remember user information across the conversation.",
chat_client=remote_client,
client=remote_client,
tools=[get_weather],
)
@@ -7,7 +7,7 @@ from __future__ import annotations
import logging
import os
from agent_framework import ChatAgent, tool
from agent_framework import Agent, tool
from agent_framework.ag_ui import add_agent_framework_fastapi_endpoint
from agent_framework.azure import AzureOpenAIChatClient
from dotenv import load_dotenv
@@ -116,10 +116,10 @@ def get_time_zone(location: str) -> str:
# The client will send get_weather tool metadata so the LLM knows about it,
# and the function invocation mixin on AGUIChatClient will execute it client-side.
# This matches the .NET AG-UI hybrid execution pattern.
agent = ChatAgent(
agent = Agent(
name="AGUIAssistant",
instructions="You are a helpful assistant. Use get_weather for weather and get_time_zone for time zones.",
chat_client=AzureOpenAIChatClient(
client=AzureOpenAIChatClient(
endpoint=endpoint,
deployment_name=deployment_name,
),