mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
co-authored by
Evan Mattson
parent
a4c9e43afb
commit
0521f5bed8
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user