Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
44f4548
Replace Role and FinishReason classes with NewType + Literal
eavanvalkenburg Feb 3, 2026
1877398
Simplify ChatResponse and AgentResponse type hints (#3592)
eavanvalkenburg Feb 3, 2026
5b1dbb9
Remove text parameter from ChatResponseUpdate and AgentResponseUpdate…
eavanvalkenburg Feb 3, 2026
02ed495
Rename from_chat_response_updates to from_updates (#3593)
eavanvalkenburg Feb 3, 2026
758536a
Remove try_parse_value method from ChatResponse and AgentResponse (#3…
eavanvalkenburg Feb 3, 2026
f5a153f
Add agent_id to AgentResponse and clarify author_name documentation (…
eavanvalkenburg Feb 3, 2026
268032f
Simplify ChatMessage.__init__ signature (#3618)
eavanvalkenburg Feb 3, 2026
af7db9a
Allow Content as input on run and get_response
eavanvalkenburg Feb 3, 2026
e0a2bb6
Fix ChatMessage usage across packages and samples
eavanvalkenburg Feb 3, 2026
b2ab128
Fix Role string usage and response format parsing
eavanvalkenburg Feb 3, 2026
ee88378
Fix ollama .value and ai_model_id issues, handle None in content list
eavanvalkenburg Feb 3, 2026
9b05be6
Fix A2AAgent type signature to include Content
eavanvalkenburg Feb 3, 2026
827be5a
Fix Role/FinishReason NewType dict annotations and improve test cover…
eavanvalkenburg Feb 3, 2026
4397131
Fix mypy errors for Role/FinishReason NewType usage
eavanvalkenburg Feb 4, 2026
2d79b74
Fix Role.TOOL and Role.ASSISTANT usage in _orchestrator_helpers.py
eavanvalkenburg Feb 4, 2026
d46c840
Fix Role NewType usage in durabletask _models.py
eavanvalkenburg Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions python/CODING_STANDARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ Prefer attributes over inheritance when parameters are mostly the same:
# ✅ Preferred - using attributes
from agent_framework import ChatMessage

user_msg = ChatMessage(role="user", content="Hello, world!")
asst_msg = ChatMessage(role="assistant", content="Hello, world!")
user_msg = ChatMessage("user", ["Hello, world!"])
asst_msg = ChatMessage("assistant", ["Hello, world!"])

# ❌ Not preferred - unnecessary inheritance
from agent_framework import UserMessage, AssistantMessage
Expand Down
4 changes: 2 additions & 2 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ async def main():
client = OpenAIChatClient()

messages = [
ChatMessage(role="system", text="You are a helpful assistant."),
ChatMessage(role="user", text="Write a haiku about Agent Framework.")
ChatMessage("system", ["You are a helpful assistant."]),
ChatMessage("user", ["Write a haiku about Agent Framework."])
]

response = await client.get_response(messages)
Expand Down
15 changes: 7 additions & 8 deletions python/packages/a2a/agent_framework_a2a/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
BaseAgent,
ChatMessage,
Content,
Role,
normalize_messages,
prepend_agent_framework_to_user_agent,
)
Expand Down Expand Up @@ -187,7 +186,7 @@ async def __aexit__(

async def run(
self,
messages: str | ChatMessage | Sequence[str | ChatMessage] | None = None,
messages: str | Content | ChatMessage | Sequence[str | Content | ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
Expand All @@ -210,11 +209,11 @@ async def run(
"""
# Collect all updates and use framework to consolidate updates into response
updates = [update async for update in self.run_stream(messages, thread=thread, **kwargs)]
return AgentResponse.from_agent_run_response_updates(updates)
return AgentResponse.from_updates(updates)

async def run_stream(
self,
messages: str | ChatMessage | Sequence[str | ChatMessage] | None = None,
messages: str | Content | ChatMessage | Sequence[str | Content | ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
Expand Down Expand Up @@ -245,7 +244,7 @@ async def run_stream(
contents = self._parse_contents_from_a2a(item.parts)
yield AgentResponseUpdate(
contents=contents,
role=Role.ASSISTANT if item.role == A2ARole.agent else Role.USER,
role="assistant" if item.role == A2ARole.agent else "user",
response_id=str(getattr(item, "message_id", uuid.uuid4())),
raw_representation=item,
)
Expand All @@ -269,7 +268,7 @@ async def run_stream(
# Empty task
yield AgentResponseUpdate(
contents=[],
role=Role.ASSISTANT,
role="assistant",
response_id=task.id,
raw_representation=task,
)
Expand Down Expand Up @@ -421,7 +420,7 @@ def _parse_messages_from_task(self, task: Task) -> list[ChatMessage]:
contents = self._parse_contents_from_a2a(history_item.parts)
messages.append(
ChatMessage(
role=Role.ASSISTANT if history_item.role == A2ARole.agent else Role.USER,
role="assistant" if history_item.role == A2ARole.agent else "user",
contents=contents,
raw_representation=history_item,
)
Expand All @@ -433,7 +432,7 @@ def _parse_message_from_artifact(self, artifact: Artifact) -> ChatMessage:
"""Parse A2A Artifact into ChatMessage using part contents."""
contents = self._parse_contents_from_a2a(artifact.parts)
return ChatMessage(
role=Role.ASSISTANT,
role="assistant",
contents=contents,
raw_representation=artifact,
)
25 changes: 12 additions & 13 deletions python/packages/a2a/tests/test_a2a_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
AgentResponseUpdate,
ChatMessage,
Content,
Role,
)
from agent_framework.a2a import A2AAgent
from pytest import fixture, raises
Expand Down Expand Up @@ -129,7 +128,7 @@ async def test_run_with_message_response(a2a_agent: A2AAgent, mock_a2a_client: M

assert isinstance(response, AgentResponse)
assert len(response.messages) == 1
assert response.messages[0].role == Role.ASSISTANT
assert response.messages[0].role == "assistant"
assert response.messages[0].text == "Hello from agent!"
assert response.response_id == "msg-123"
assert mock_a2a_client.call_count == 1
Expand All @@ -144,7 +143,7 @@ async def test_run_with_task_response_single_artifact(a2a_agent: A2AAgent, mock_

assert isinstance(response, AgentResponse)
assert len(response.messages) == 1
assert response.messages[0].role == Role.ASSISTANT
assert response.messages[0].role == "assistant"
assert response.messages[0].text == "Generated report content"
assert response.response_id == "task-456"
assert mock_a2a_client.call_count == 1
Expand All @@ -170,7 +169,7 @@ async def test_run_with_task_response_multiple_artifacts(a2a_agent: A2AAgent, mo

# All should be assistant messages
for message in response.messages:
assert message.role == Role.ASSISTANT
assert message.role == "assistant"

assert response.response_id == "task-789"

Expand Down Expand Up @@ -233,7 +232,7 @@ def test_parse_messages_from_task_with_artifacts(a2a_agent: A2AAgent) -> None:
assert len(result) == 2
assert result[0].text == "Content 1"
assert result[1].text == "Content 2"
assert all(msg.role == Role.ASSISTANT for msg in result)
assert all(msg.role == "assistant" for msg in result)


def test_parse_message_from_artifact(a2a_agent: A2AAgent) -> None:
Expand All @@ -252,7 +251,7 @@ def test_parse_message_from_artifact(a2a_agent: A2AAgent) -> None:
result = a2a_agent._parse_message_from_artifact(artifact)

assert isinstance(result, ChatMessage)
assert result.role == Role.ASSISTANT
assert result.role == "assistant"
assert result.text == "Artifact content"
assert result.raw_representation == artifact

Expand Down Expand Up @@ -296,7 +295,7 @@ def test_prepare_message_for_a2a_with_error_content(a2a_agent: A2AAgent) -> None

# Create ChatMessage with ErrorContent
error_content = Content.from_error(message="Test error message")
message = ChatMessage(role=Role.USER, contents=[error_content])
message = ChatMessage("user", [error_content])

# Convert to A2A message
a2a_message = a2a_agent._prepare_message_for_a2a(message)
Expand All @@ -311,7 +310,7 @@ def test_prepare_message_for_a2a_with_uri_content(a2a_agent: A2AAgent) -> None:

# Create ChatMessage with UriContent
uri_content = Content.from_uri(uri="http://example.com/file.pdf", media_type="application/pdf")
message = ChatMessage(role=Role.USER, contents=[uri_content])
message = ChatMessage("user", [uri_content])

# Convert to A2A message
a2a_message = a2a_agent._prepare_message_for_a2a(message)
Expand All @@ -327,7 +326,7 @@ def test_prepare_message_for_a2a_with_data_content(a2a_agent: A2AAgent) -> None:

# Create ChatMessage with DataContent (base64 data URI)
data_content = Content.from_uri(uri="data:text/plain;base64,SGVsbG8gV29ybGQ=", media_type="text/plain")
message = ChatMessage(role=Role.USER, contents=[data_content])
message = ChatMessage("user", [data_content])

# Convert to A2A message
a2a_message = a2a_agent._prepare_message_for_a2a(message)
Expand All @@ -341,7 +340,7 @@ def test_prepare_message_for_a2a_with_data_content(a2a_agent: A2AAgent) -> None:
def test_prepare_message_for_a2a_empty_contents_raises_error(a2a_agent: A2AAgent) -> None:
"""Test _prepare_message_for_a2a with empty contents raises ValueError."""
# Create ChatMessage with no contents
message = ChatMessage(role=Role.USER, contents=[])
message = ChatMessage("user", [])

# Should raise ValueError for empty contents
with raises(ValueError, match="ChatMessage.contents is empty"):
Expand All @@ -360,7 +359,7 @@ async def test_run_stream_with_message_response(a2a_agent: A2AAgent, mock_a2a_cl
# Verify streaming response
assert len(updates) == 1
assert isinstance(updates[0], AgentResponseUpdate)
assert updates[0].role == Role.ASSISTANT
assert updates[0].role == "assistant"
assert len(updates[0].contents) == 1

content = updates[0].contents[0]
Expand Down Expand Up @@ -408,7 +407,7 @@ def test_prepare_message_for_a2a_with_multiple_contents() -> None:

# Create message with multiple content types
message = ChatMessage(
role=Role.USER,
role="user",
contents=[
Content.from_text(text="Here's the analysis:"),
Content.from_data(data=b"binary data", media_type="application/octet-stream"),
Expand Down Expand Up @@ -465,7 +464,7 @@ def test_prepare_message_for_a2a_with_hosted_file() -> None:

# Create message with hosted file content
message = ChatMessage(
role=Role.USER,
role="user",
contents=[Content.from_hosted_file(file_id="hosted://storage/document.pdf")],
)

Expand Down
2 changes: 1 addition & 1 deletion python/packages/ag-ui/agent_framework_ag_ui/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ async def _inner_get_response(
Returns:
ChatResponse object
"""
return await ChatResponse.from_chat_response_generator(
return await ChatResponse.from_update_generator(
self._inner_get_streaming_response(
messages=messages,
options=options,
Expand Down
22 changes: 10 additions & 12 deletions python/packages/ag-ui/agent_framework_ag_ui/_event_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from agent_framework import (
ChatResponseUpdate,
Content,
FinishReason,
Role,
)


Expand Down Expand Up @@ -86,7 +84,7 @@ def _handle_run_started(self, event: dict[str, Any]) -> ChatResponseUpdate:
self.run_id = event.get("runId")

return ChatResponseUpdate(
role=Role.ASSISTANT,
role="assistant",
contents=[],
additional_properties={
"thread_id": self.thread_id,
Expand All @@ -98,7 +96,7 @@ def _handle_text_message_start(self, event: dict[str, Any]) -> ChatResponseUpdat
"""Handle TEXT_MESSAGE_START event."""
self.current_message_id = event.get("messageId")
return ChatResponseUpdate(
role=Role.ASSISTANT,
role="assistant",
message_id=self.current_message_id,
contents=[],
)
Expand All @@ -112,7 +110,7 @@ def _handle_text_message_content(self, event: dict[str, Any]) -> ChatResponseUpd
self.current_message_id = message_id

return ChatResponseUpdate(
role=Role.ASSISTANT,
role="assistant",
message_id=self.current_message_id,
contents=[Content.from_text(text=delta)],
)
Expand All @@ -128,7 +126,7 @@ def _handle_tool_call_start(self, event: dict[str, Any]) -> ChatResponseUpdate:
self.accumulated_tool_args = ""

return ChatResponseUpdate(
role=Role.ASSISTANT,
role="assistant",
contents=[
Content.from_function_call(
call_id=self.current_tool_call_id or "",
Expand All @@ -144,7 +142,7 @@ def _handle_tool_call_args(self, event: dict[str, Any]) -> ChatResponseUpdate:
self.accumulated_tool_args += delta

return ChatResponseUpdate(
role=Role.ASSISTANT,
role="assistant",
contents=[
Content.from_function_call(
call_id=self.current_tool_call_id or "",
Expand All @@ -165,7 +163,7 @@ def _handle_tool_call_result(self, event: dict[str, Any]) -> ChatResponseUpdate:
result = event.get("result") if event.get("result") is not None else event.get("content")

return ChatResponseUpdate(
role=Role.TOOL,
role="tool",
contents=[
Content.from_function_result(
call_id=tool_call_id,
Expand All @@ -177,8 +175,8 @@ def _handle_tool_call_result(self, event: dict[str, Any]) -> ChatResponseUpdate:
def _handle_run_finished(self, event: dict[str, Any]) -> ChatResponseUpdate:
"""Handle RUN_FINISHED event."""
return ChatResponseUpdate(
role=Role.ASSISTANT,
finish_reason=FinishReason.STOP,
role="assistant",
finish_reason="stop",
contents=[],
additional_properties={
"thread_id": self.thread_id,
Expand All @@ -191,8 +189,8 @@ def _handle_run_error(self, event: dict[str, Any]) -> ChatResponseUpdate:
error_message = event.get("message", "Unknown error")

return ChatResponseUpdate(
role=Role.ASSISTANT,
finish_reason=FinishReason.CONTENT_FILTER,
role="assistant",
finish_reason="content_filter",
contents=[
Content.from_error(
message=error_message,
Expand Down
Loading
Loading