Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions python/packages/core/agent_framework/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,13 @@ def _add_text_content(self, other: Content) -> Content:

def _add_text_reasoning_content(self, other: Content) -> Content:
"""Add two TextReasoningContent instances."""
# Ensure we do not silently merge contents with conflicting ids
if self.id and other.id and self.id != other.id:
raise AdditionItemMismatch(
f"Cannot add text_reasoning content with different ids: {self.id!r} != {other.id!r}"
)
combined_id = self.id or other.id

# Concatenate text, handling None values
self_text = self.text or "" # type: ignore[attr-defined]
other_text = other.text or "" # type: ignore[attr-defined]
Expand All @@ -1390,6 +1397,7 @@ def _add_text_reasoning_content(self, other: Content) -> Content:

return Content(
"text_reasoning",
id=combined_id,
text=combined_text,
protected_data=protected_data,
annotations=_combine_annotations(self.annotations, other.annotations),
Expand Down
60 changes: 59 additions & 1 deletion python/packages/core/tests/core/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
add_usage_details,
validate_tool_mode,
)
from agent_framework.exceptions import ContentError
from agent_framework.exceptions import AdditionItemMismatch, ContentError


@fixture
Expand Down Expand Up @@ -1526,6 +1526,64 @@ def test_text_reasoning_content_iadd_coverage():
assert t1.text == "Thinking 1 Thinking 2"


def test_text_reasoning_content_add_preserves_id():
"""Test that coalescing text_reasoning Content preserves the id field."""

t1 = Content.from_text_reasoning(id="rs_abc123", text="Thinking part 1")
t2 = Content.from_text_reasoning(id="rs_abc123", text=" part 2")

result = t1 + t2
assert result.text == "Thinking part 1 part 2"
assert result.id == "rs_abc123"


def test_text_reasoning_content_add_id_fallback_to_other():
"""Test that coalescing falls back to other's id when self has no id."""

t1 = Content.from_text_reasoning(text="Thinking part 1")
t2 = Content.from_text_reasoning(id="rs_abc123", text=" part 2")

result = t1 + t2
assert result.id == "rs_abc123"


def test_text_reasoning_content_add_preserves_id_with_encrypted_content():
"""Test that id and encrypted_content both survive coalescing for round-trip."""

t1 = Content.from_text_reasoning(
id="rs_abc123",
text="Thinking",
additional_properties={"encrypted_content": "enc_blob_data"},
)
t2 = Content.from_text_reasoning(id="rs_abc123", text=" more")

result = t1 + t2
assert result.text == "Thinking more"
assert result.id == "rs_abc123"
assert result.additional_properties.get("encrypted_content") == "enc_blob_data"


def test_text_reasoning_content_add_conflicting_ids_raises():
"""Test that coalescing text_reasoning Content with different ids raises AdditionItemMismatch."""

t1 = Content.from_text_reasoning(id="rs_abc123", text="Thinking part 1")
t2 = Content.from_text_reasoning(id="rs_xyz789", text=" part 2")

with pytest.raises(AdditionItemMismatch, match="different ids"):
t1 + t2


def test_text_reasoning_content_add_neither_has_id():
"""Test that coalescing text_reasoning Content when neither has an id results in None id."""

t1 = Content.from_text_reasoning(text="Thinking part 1")
t2 = Content.from_text_reasoning(text=" part 2")

result = t1 + t2
assert result.text == "Thinking part 1 part 2"
assert result.id is None


def test_comprehensive_to_dict_exclude_options():
"""Test to_dict methods with various exclude options for better coverage."""

Expand Down
Loading