diff --git a/python/packages/core/agent_framework/_types.py b/python/packages/core/agent_framework/_types.py index 17dba475e3e..a21644a5789 100644 --- a/python/packages/core/agent_framework/_types.py +++ b/python/packages/core/agent_framework/_types.py @@ -1581,9 +1581,12 @@ def __add__(self, other: Content) -> Content: def _add_text_content(self, other: Content) -> Content: """Add two TextContent instances.""" + self_text = self.text or "" + other_text = other.text or "" + combined_text = None if self.text is None and other.text is None else self_text + other_text return Content( "text", - text=self.text + other.text, # type: ignore[attr-defined, operator] + text=combined_text, annotations=_combine_annotations(self.annotations, other.annotations), additional_properties=_combine_additional_props(self.additional_properties, other.additional_properties), raw_representation=_combine_raw_representations(self.raw_representation, other.raw_representation), @@ -1644,7 +1647,7 @@ def _add_function_call_content(self, other: Content) -> Content: arguments = self_arguments elif isinstance(self_arguments, str) and isinstance(other_arguments, str): arguments = self_arguments + other_arguments - elif isinstance(self_arguments, dict) and isinstance(other_arguments, dict): + elif isinstance(self_arguments, Mapping) and isinstance(other_arguments, Mapping): arguments = {**self_arguments, **other_arguments} else: raise TypeError("Incompatible argument types") diff --git a/python/packages/core/tests/core/test_types.py b/python/packages/core/tests/core/test_types.py index a6797b30449..ff6f9d89b35 100644 --- a/python/packages/core/tests/core/test_types.py +++ b/python/packages/core/tests/core/test_types.py @@ -676,6 +676,14 @@ def test_function_call_content_add_merging_and_errors(): c = a + b assert c.arguments == {"x": 1, "y": 2} + # mapping merge + from types import MappingProxyType + + a = Content.from_function_call(call_id="1", name="f", arguments=MappingProxyType({"x": 1})) + b = Content.from_function_call(call_id="1", name="f", arguments={"y": 2}) + c = a + b + assert c.arguments == {"x": 1, "y": 2} + # informational_only is preserved across streamed chunks a = Content.from_function_call(call_id="1", name="f", arguments='{"x":', informational_only=True) b = Content.from_function_call(call_id="1", name="f", arguments="1}") @@ -2235,6 +2243,22 @@ def test_text_content_iadd_coverage(): assert t1.additional_properties == {"key1": "val1", "key2": "val2"} +def test_text_content_add_handles_none_text(): + """Test TextContent __add__ when one or both text fields are None.""" + t_none = Content("text", text=None) + t_str = Content.from_text("hello") + + res1 = t_none + t_str + assert res1.text == "hello" + + res2 = t_str + t_none + assert res2.text == "hello" + + t_none2 = Content("text", text=None) + res3 = t_none + t_none2 + assert res3.text is None + + def test_text_reasoning_content_add_coverage(): """Test TextReasoningContent __add__ method for better coverage."""