From dbeb394c5fe47122d58f7eecd48318ccd711dd5d Mon Sep 17 00:00:00 2001 From: voidborne-d Date: Wed, 15 Apr 2026 12:11:50 +0000 Subject: [PATCH] fix: preserve empty-string text parts in A2A converter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part(text='') is valid — it is produced by code_execution_utils.py when code execution returns None, and by interactions_utils.py when the Interactions API returns None text. Gemini 2.5 Flash thinking mode also emits empty text parts. The truthiness check `if part.text:` treats '' as falsy, causing the converter to skip the part entirely. When all parts in a response are empty the A2A message ends up with zero parts and the client sees "broken thinking" with no content. Change the check to `if part.text is not None:` so that empty strings are correctly wrapped as TextPart while None is still skipped. Add a regression test for the empty-string case. Fixes #5341 --- src/google/adk/a2a/converters/part_converter.py | 2 +- .../a2a/converters/test_part_converter.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/google/adk/a2a/converters/part_converter.py b/src/google/adk/a2a/converters/part_converter.py index ef4a94fd5d..cb7eb688cd 100644 --- a/src/google/adk/a2a/converters/part_converter.py +++ b/src/google/adk/a2a/converters/part_converter.py @@ -179,7 +179,7 @@ def convert_genai_part_to_a2a_part( ) -> Optional[a2a_types.Part]: """Convert a Google GenAI Part to an A2A Part.""" - if part.text: + if part.text is not None: a2a_part = a2a_types.TextPart(text=part.text) if part.thought is not None: a2a_part.metadata = {_get_adk_metadata_key('thought'): part.thought} diff --git a/tests/unittests/a2a/converters/test_part_converter.py b/tests/unittests/a2a/converters/test_part_converter.py index 446e118534..f921b7e061 100644 --- a/tests/unittests/a2a/converters/test_part_converter.py +++ b/tests/unittests/a2a/converters/test_part_converter.py @@ -297,6 +297,23 @@ def test_convert_text_part_with_thought(self): assert result.root.metadata is not None assert result.root.metadata[_get_adk_metadata_key("thought")] + def test_convert_empty_text_part(self): + """Test that Part(text='') is preserved, not dropped. + + Regression test for #5341: empty-string text parts are valid and + must not fall through to the unsupported-part warning. + """ + # Arrange + genai_part = genai_types.Part(text="") + + # Act + result = convert_genai_part_to_a2a_part(genai_part) + + # Assert — should produce a valid TextPart, not None + assert result is not None + assert isinstance(result.root, a2a_types.TextPart) + assert result.root.text == "" + def test_convert_file_data_part(self): """Test conversion of GenAI file_data Part to A2A Part.""" # Arrange