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
@@ -18,7 +18,6 @@ from typing import Any, ClassVar, Generic, TypedDict
from agent_framework import (
BaseChatClient,
ChatAndFunctionMiddlewareTypes,
ChatMessage,
ChatMiddlewareLayer,
ChatOptions,
ChatResponse,
@@ -28,6 +27,7 @@ from agent_framework import (
FunctionInvocationLayer,
FunctionTool,
HostedWebSearchTool,
Message,
ResponseStream,
ToolProtocol,
UsageDetails,
@@ -356,7 +356,7 @@ class OllamaChatClient(
def _inner_get_response(
self,
*,
messages: Sequence[ChatMessage],
messages: Sequence[Message],
options: Mapping[str, Any],
stream: bool = False,
**kwargs: Any,
@@ -397,7 +397,7 @@ class OllamaChatClient(
return _get_response()
def _prepare_options(self, messages: Sequence[ChatMessage], options: Mapping[str, Any]) -> dict[str, Any]:
def _prepare_options(self, messages: Sequence[Message], options: Mapping[str, Any]) -> dict[str, Any]:
# Handle instructions by prepending to messages as system message
instructions = options.get("instructions")
if instructions:
@@ -448,13 +448,13 @@ class OllamaChatClient(
return run_options
def _prepare_messages_for_ollama(self, messages: Sequence[ChatMessage]) -> list[OllamaMessage]:
def _prepare_messages_for_ollama(self, messages: Sequence[Message]) -> list[OllamaMessage]:
ollama_messages = [self._prepare_message_for_ollama(msg) for msg in messages]
# Flatten the list of lists into a single list
return list(chain.from_iterable(ollama_messages))
def _prepare_message_for_ollama(self, message: ChatMessage) -> list[OllamaMessage]:
message_converters: dict[str, Callable[[ChatMessage], list[OllamaMessage]]] = {
def _prepare_message_for_ollama(self, message: Message) -> list[OllamaMessage]:
message_converters: dict[str, Callable[[Message], list[OllamaMessage]]] = {
"system": self._format_system_message,
"user": self._format_user_message,
"assistant": self._format_assistant_message,
@@ -462,10 +462,10 @@ class OllamaChatClient(
}
return message_converters[message.role](message)
def _format_system_message(self, message: ChatMessage) -> list[OllamaMessage]:
def _format_system_message(self, message: Message) -> list[OllamaMessage]:
return [OllamaMessage(role="system", content=message.text)]
def _format_user_message(self, message: ChatMessage) -> list[OllamaMessage]:
def _format_user_message(self, message: Message) -> list[OllamaMessage]:
if not any(c.type in {"text", "data"} for c in message.contents) and not message.text:
raise ServiceInvalidRequestError(
"Ollama connector currently only supports user messages with TextContent or DataContent."
@@ -483,7 +483,7 @@ class OllamaChatClient(
user_message["images"] = [c.uri.split(",")[1] for c in data_contents if c.uri]
return [user_message]
def _format_assistant_message(self, message: ChatMessage) -> list[OllamaMessage]:
def _format_assistant_message(self, message: Message) -> list[OllamaMessage]:
text_content = message.text
# Ollama shouldn't have encrypted reasoning, so we just process text.
reasoning_contents = "".join((c.text or "") for c in message.contents if c.type == "text_reasoning")
@@ -506,7 +506,7 @@ class OllamaChatClient(
]
return [assistant_message]
def _format_tool_message(self, message: ChatMessage) -> list[OllamaMessage]:
def _format_tool_message(self, message: Message) -> list[OllamaMessage]:
# Ollama does not support multiple tool results in a single message, so we create a separate
return [
OllamaMessage(role="tool", content=str(item.result), tool_name=item.call_id)
@@ -538,7 +538,7 @@ class OllamaChatClient(
contents = self._parse_contents_from_ollama(response)
return ChatResponse(
messages=[ChatMessage(role="assistant", contents=contents)],
messages=[Message(role="assistant", contents=contents)],
model_id=response.model,
created_at=response.created_at,
usage_details=UsageDetails(
@@ -8,10 +8,10 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from agent_framework import (
BaseChatClient,
ChatMessage,
ChatResponseUpdate,
Content,
HostedWebSearchTool,
Message,
chat_middleware,
tool,
)
@@ -77,7 +77,7 @@ def ollama_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): #
@fixture
def chat_history() -> list[ChatMessage]:
def chat_history() -> list[Message]:
return []
@@ -244,12 +244,12 @@ async def test_empty_messages() -> None:
async def test_cmc(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_chat_completion_response: AsyncStream[OllamaChatResponse],
) -> None:
mock_chat.return_value = mock_chat_completion_response
chat_history.append(ChatMessage(text="hello world", role="system"))
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="system"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
result = await ollama_client.get_response(messages=chat_history)
@@ -261,11 +261,11 @@ async def test_cmc(
async def test_cmc_reasoning(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_chat_completion_response_reasoning: AsyncStream[OllamaChatResponse],
) -> None:
mock_chat.return_value = mock_chat_completion_response_reasoning
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
result = await ollama_client.get_response(messages=chat_history)
@@ -278,11 +278,11 @@ async def test_cmc_reasoning(
async def test_cmc_chat_failure(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
# Simulate a failure in the Ollama client
mock_chat.side_effect = Exception("Connection error")
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
@@ -297,12 +297,12 @@ async def test_cmc_chat_failure(
async def test_cmc_streaming(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_streaming_chat_completion_response: AsyncStream[OllamaChatResponse],
) -> None:
mock_chat.return_value = mock_streaming_chat_completion_response
chat_history.append(ChatMessage(text="hello world", role="system"))
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="system"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
result = ollama_client.get_response(messages=chat_history, stream=True)
@@ -315,11 +315,11 @@ async def test_cmc_streaming(
async def test_cmc_streaming_reasoning(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_streaming_chat_completion_response_reasoning: AsyncStream[OllamaChatResponse],
) -> None:
mock_chat.return_value = mock_streaming_chat_completion_response_reasoning
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
result = ollama_client.get_response(messages=chat_history, stream=True)
@@ -333,11 +333,11 @@ async def test_cmc_streaming_reasoning(
async def test_cmc_streaming_chat_failure(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
# Simulate a failure in the Ollama client for streaming
mock_chat.side_effect = Exception("Streaming connection error")
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
@@ -353,7 +353,7 @@ async def test_cmc_streaming_chat_failure(
async def test_cmc_streaming_with_tool_call(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_streaming_chat_completion_response: AsyncStream[OllamaChatResponse],
mock_streaming_chat_completion_tool_call: AsyncStream[OllamaChatResponse],
) -> None:
@@ -362,7 +362,7 @@ async def test_cmc_streaming_with_tool_call(
mock_streaming_chat_completion_response,
]
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
result = ollama_client.get_response(messages=chat_history, stream=True, options={"tools": [hello_world]})
@@ -386,7 +386,7 @@ async def test_cmc_streaming_with_tool_call(
async def test_cmc_with_hosted_tool_call(
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
with pytest.raises(ServiceInvalidRequestError):
additional_properties = {
@@ -396,7 +396,7 @@ async def test_cmc_with_hosted_tool_call(
}
}
chat_history.append(ChatMessage(text="hello world", role="user"))
chat_history.append(Message(text="hello world", role="user"))
ollama_client = OllamaChatClient()
await ollama_client.get_response(
@@ -411,12 +411,12 @@ async def test_cmc_with_hosted_tool_call(
async def test_cmc_with_data_content_type(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_chat_completion_response: OllamaChatResponse,
) -> None:
mock_chat.return_value = mock_chat_completion_response
chat_history.append(
ChatMessage(
Message(
contents=[Content.from_uri(uri="data:image/png;base64,xyz", media_type="image/png")],
role="user",
)
@@ -432,14 +432,14 @@ async def test_cmc_with_data_content_type(
async def test_cmc_with_invalid_data_content_media_type(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_streaming_chat_completion_response: AsyncStream[OllamaChatResponse],
) -> None:
with pytest.raises(ServiceInvalidRequestError):
mock_chat.return_value = mock_streaming_chat_completion_response
# Remote Uris are not supported by Ollama client
chat_history.append(
ChatMessage(
Message(
contents=[Content.from_uri(uri="data:audio/mp3;base64,xyz", media_type="audio/mp3")],
role="user",
)
@@ -455,14 +455,14 @@ async def test_cmc_with_invalid_data_content_media_type(
async def test_cmc_with_invalid_content_type(
mock_chat: AsyncMock,
ollama_unit_test_env: dict[str, str],
chat_history: list[ChatMessage],
chat_history: list[Message],
mock_chat_completion_response: AsyncStream[OllamaChatResponse],
) -> None:
with pytest.raises(ServiceInvalidRequestError):
mock_chat.return_value = mock_chat_completion_response
# Remote Uris are not supported by Ollama client
chat_history.append(
ChatMessage(
Message(
contents=[Content.from_uri(uri="http://example.com/image.png", media_type="image/png")],
role="user",
)
@@ -475,9 +475,9 @@ async def test_cmc_with_invalid_content_type(
@skip_if_azure_integration_tests_disabled
async def test_cmc_integration_with_tool_call(
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
chat_history.append(ChatMessage(text="Call the hello world function and repeat what it says", role="user"))
chat_history.append(Message(text="Call the hello world function and repeat what it says", role="user"))
ollama_client = OllamaChatClient()
result = await ollama_client.get_response(messages=chat_history, options={"tools": [hello_world]})
@@ -490,9 +490,9 @@ async def test_cmc_integration_with_tool_call(
@skip_if_azure_integration_tests_disabled
async def test_cmc_integration_with_chat_completion(
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
chat_history.append(ChatMessage(text="Say Hello World", role="user"))
chat_history.append(Message(text="Say Hello World", role="user"))
ollama_client = OllamaChatClient()
result = await ollama_client.get_response(messages=chat_history)
@@ -502,9 +502,9 @@ async def test_cmc_integration_with_chat_completion(
@skip_if_azure_integration_tests_disabled
async def test_cmc_streaming_integration_with_tool_call(
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
chat_history.append(ChatMessage(text="Call the hello world function and repeat what it says", role="user"))
chat_history.append(Message(text="Call the hello world function and repeat what it says", role="user"))
ollama_client = OllamaChatClient()
result: AsyncIterable[ChatResponseUpdate] = ollama_client.get_response(
@@ -527,9 +527,9 @@ async def test_cmc_streaming_integration_with_tool_call(
@skip_if_azure_integration_tests_disabled
async def test_cmc_streaming_integration_with_chat_completion(
chat_history: list[ChatMessage],
chat_history: list[Message],
) -> None:
chat_history.append(ChatMessage(text="Say Hello World", role="user"))
chat_history.append(Message(text="Say Hello World", role="user"))
ollama_client = OllamaChatClient()
result: AsyncIterable[ChatResponseUpdate] = ollama_client.get_response(messages=chat_history, stream=True)