Python: [Breaking] Simplified Content types to a single class with classmethod constructors. (#3252)

* ported Content to a new model

* fixed linting

* fixes

* fixed data format handling

* fix for 3.10 mypy

* fix

* fix int test
This commit is contained in:
Eduard van Valkenburg
2026-01-20 23:09:39 +01:00
committed by GitHub
Unverified
parent 73761aa4a3
commit 83e6229c11
132 changed files with 3949 additions and 4741 deletions
@@ -31,11 +31,8 @@ from agent_framework import (
AgentThread,
BaseAgent,
ChatMessage,
Contents,
DataContent,
Content,
Role,
TextContent,
UriContent,
normalize_messages,
prepend_agent_framework_to_user_agent,
)
@@ -333,7 +330,7 @@ class A2AAgent(BaseAgent):
A2APart(
root=FilePart(
file=FileWithBytes(
bytes=_get_uri_data(content.uri),
bytes=_get_uri_data(content.uri), # type: ignore[arg-type]
mime_type=content.media_type,
),
metadata=content.additional_properties,
@@ -362,19 +359,19 @@ class A2AAgent(BaseAgent):
metadata=cast(dict[str, Any], message.additional_properties),
)
def _parse_contents_from_a2a(self, parts: Sequence[A2APart]) -> list[Contents]:
"""Parse A2A Parts into Agent Framework Contents.
def _parse_contents_from_a2a(self, parts: Sequence[A2APart]) -> list[Content]:
"""Parse A2A Parts into Agent Framework Content.
Transforms A2A protocol Parts into framework-native Content objects,
handling text, file (URI/bytes), and data parts with metadata preservation.
"""
contents: list[Contents] = []
contents: list[Content] = []
for part in parts:
inner_part = part.root
match inner_part.kind:
case "text":
contents.append(
TextContent(
Content.from_text(
text=inner_part.text,
additional_properties=inner_part.metadata,
raw_representation=inner_part,
@@ -383,7 +380,7 @@ class A2AAgent(BaseAgent):
case "file":
if isinstance(inner_part.file, FileWithUri):
contents.append(
UriContent(
Content.from_uri(
uri=inner_part.file.uri,
media_type=inner_part.file.mime_type or "",
additional_properties=inner_part.metadata,
@@ -392,7 +389,7 @@ class A2AAgent(BaseAgent):
)
elif isinstance(inner_part.file, FileWithBytes):
contents.append(
DataContent(
Content.from_data(
data=base64.b64decode(inner_part.file.bytes),
media_type=inner_part.file.mime_type or "",
additional_properties=inner_part.metadata,
@@ -401,7 +398,7 @@ class A2AAgent(BaseAgent):
)
case "data":
contents.append(
TextContent(
Content.from_text(
text=json.dumps(inner_part.data),
additional_properties=inner_part.metadata,
raw_representation=inner_part,
+14 -18
View File
@@ -24,12 +24,8 @@ from agent_framework import (
AgentResponse,
AgentResponseUpdate,
ChatMessage,
DataContent,
ErrorContent,
HostedFileContent,
Content,
Role,
TextContent,
UriContent,
)
from agent_framework.a2a import A2AAgent
from pytest import fixture, raises
@@ -289,8 +285,8 @@ def test_parse_contents_from_a2a_conversion(a2a_agent: A2AAgent) -> None:
# Verify conversion
assert len(contents) == 2
assert isinstance(contents[0], TextContent)
assert isinstance(contents[1], TextContent)
assert contents[0].type == "text"
assert contents[1].type == "text"
assert contents[0].text == "First part"
assert contents[1].text == "Second part"
@@ -299,7 +295,7 @@ def test_prepare_message_for_a2a_with_error_content(a2a_agent: A2AAgent) -> None
"""Test _prepare_message_for_a2a with ErrorContent."""
# Create ChatMessage with ErrorContent
error_content = ErrorContent(message="Test error message")
error_content = Content.from_error(message="Test error message")
message = ChatMessage(role=Role.USER, contents=[error_content])
# Convert to A2A message
@@ -314,7 +310,7 @@ def test_prepare_message_for_a2a_with_uri_content(a2a_agent: A2AAgent) -> None:
"""Test _prepare_message_for_a2a with UriContent."""
# Create ChatMessage with UriContent
uri_content = UriContent(uri="http://example.com/file.pdf", media_type="application/pdf")
uri_content = Content.from_uri(uri="http://example.com/file.pdf", media_type="application/pdf")
message = ChatMessage(role=Role.USER, contents=[uri_content])
# Convert to A2A message
@@ -330,7 +326,7 @@ def test_prepare_message_for_a2a_with_data_content(a2a_agent: A2AAgent) -> None:
"""Test _prepare_message_for_a2a with DataContent."""
# Create ChatMessage with DataContent (base64 data URI)
data_content = DataContent(uri="data:text/plain;base64,SGVsbG8gV29ybGQ=", media_type="text/plain")
data_content = Content.from_uri(uri="data:text/plain;base64,SGVsbG8gV29ybGQ=", media_type="text/plain")
message = ChatMessage(role=Role.USER, contents=[data_content])
# Convert to A2A message
@@ -368,7 +364,7 @@ async def test_run_stream_with_message_response(a2a_agent: A2AAgent, mock_a2a_cl
assert len(updates[0].contents) == 1
content = updates[0].contents[0]
assert isinstance(content, TextContent)
assert content.type == "text"
assert content.text == "Streaming response from agent!"
assert updates[0].response_id == "msg-stream-123"
@@ -414,10 +410,10 @@ def test_prepare_message_for_a2a_with_multiple_contents() -> None:
message = ChatMessage(
role=Role.USER,
contents=[
TextContent(text="Here's the analysis:"),
DataContent(data=b"binary data", media_type="application/octet-stream"),
UriContent(uri="https://example.com/image.png", media_type="image/png"),
TextContent(text='{"structured": "data"}'),
Content.from_text(text="Here's the analysis:"),
Content.from_data(data=b"binary data", media_type="application/octet-stream"),
Content.from_uri(uri="https://example.com/image.png", media_type="image/png"),
Content.from_text(text='{"structured": "data"}'),
],
)
@@ -445,7 +441,7 @@ def test_parse_contents_from_a2a_with_data_part() -> None:
assert len(contents) == 1
assert isinstance(contents[0], TextContent)
assert contents[0].type == "text"
assert contents[0].text == '{"key": "value", "number": 42}'
assert contents[0].additional_properties == {"source": "test"}
@@ -470,7 +466,7 @@ def test_prepare_message_for_a2a_with_hosted_file() -> None:
# Create message with hosted file content
message = ChatMessage(
role=Role.USER,
contents=[HostedFileContent(file_id="hosted://storage/document.pdf")],
contents=[Content.from_hosted_file(file_id="hosted://storage/document.pdf")],
)
result = agent._prepare_message_for_a2a(message) # noqa: SLF001
@@ -507,7 +503,7 @@ def test_parse_contents_from_a2a_with_hosted_file_uri() -> None:
assert len(contents) == 1
assert isinstance(contents[0], UriContent)
assert contents[0].type == "uri"
assert contents[0].uri == "hosted://storage/document.pdf"
assert contents[0].media_type == "" # Converted None to empty string