From c4ceb4d1884b175a97c8b80ac6387a539d2f5757 Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Thu, 22 Jan 2026 16:56:44 -0300 Subject: [PATCH 1/3] fix metadata in genai parts --- .../adk/a2a/converters/part_converter.py | 21 ++- .../a2a/converters/test_part_converter.py | 166 +++++++++++++++++- 2 files changed, 178 insertions(+), 9 deletions(-) diff --git a/src/google/adk/a2a/converters/part_converter.py b/src/google/adk/a2a/converters/part_converter.py index 72dbcb213f..f5b525a90c 100644 --- a/src/google/adk/a2a/converters/part_converter.py +++ b/src/google/adk/a2a/converters/part_converter.py @@ -60,15 +60,20 @@ def convert_a2a_part_to_genai_part( ) -> Optional[genai_types.Part]: """Convert an A2A Part to a Google GenAI Part.""" part = a2a_part.root + + thought = None + if part.metadata: + thought = part.metadata.get(_get_adk_metadata_key('thought')) + if isinstance(part, a2a_types.TextPart): - return genai_types.Part(text=part.text) + return genai_types.Part(text=part.text, thought=thought) if isinstance(part, a2a_types.FilePart): if isinstance(part.file, a2a_types.FileWithUri): return genai_types.Part( file_data=genai_types.FileData( file_uri=part.file.uri, mime_type=part.file.mime_type - ) + ), thought=thought ) elif isinstance(part.file, a2a_types.FileWithBytes): @@ -76,7 +81,7 @@ def convert_a2a_part_to_genai_part( inline_data=genai_types.Blob( data=base64.b64decode(part.file.bytes), mime_type=part.file.mime_type, - ) + ), thought=thought ) else: logger.warning( @@ -104,7 +109,7 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( function_call=genai_types.FunctionCall.model_validate( part.data, by_alias=True - ) + ), thought=thought ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -113,7 +118,7 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( function_response=genai_types.FunctionResponse.model_validate( part.data, by_alias=True - ) + ), thought=thought ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -122,7 +127,7 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( code_execution_result=genai_types.CodeExecutionResult.model_validate( part.data, by_alias=True - ) + ), thought=thought ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -131,7 +136,7 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( executable_code=genai_types.ExecutableCode.model_validate( part.data, by_alias=True - ) + ), thought=thought ) return genai_types.Part( inline_data=genai_types.Blob( @@ -141,7 +146,7 @@ def convert_a2a_part_to_genai_part( ) + A2A_DATA_PART_END_TAG, mime_type=A2A_DATA_PART_TEXT_MIME_TYPE, - ) + ), thought=thought ) logger.warning( diff --git a/tests/unittests/a2a/converters/test_part_converter.py b/tests/unittests/a2a/converters/test_part_converter.py index 647caa5bd3..b720a8f632 100644 --- a/tests/unittests/a2a/converters/test_part_converter.py +++ b/tests/unittests/a2a/converters/test_part_converter.py @@ -48,6 +48,62 @@ def test_convert_text_part(self): assert isinstance(result, genai_types.Part) assert result.text == "Hello, world!" + def test_convert_text_part_with_thought_true_metadata(self): + """Test conversion of A2A TextPart with thought=True metadata to GenAI Part.""" + # Arrange + a2a_part = a2a_types.Part( + root=a2a_types.TextPart( + text="I'm thinking about this...", + metadata={_get_adk_metadata_key("thought"): True}, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert isinstance(result, genai_types.Part) + assert result.text == "I'm thinking about this..." + assert result.thought is True + + def test_convert_text_part_with_thought_false_metadata(self): + """Test conversion of A2A TextPart with thought=False metadata to GenAI Part.""" + # Arrange + a2a_part = a2a_types.Part( + root=a2a_types.TextPart( + text="This is not a thought.", + metadata={_get_adk_metadata_key("thought"): False}, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert isinstance(result, genai_types.Part) + assert result.text == "This is not a thought." + assert result.thought is False + + def test_convert_text_part_without_thought_metadata(self): + """Test that TextPart without thought metadata has thought=None.""" + # Arrange + a2a_part = a2a_types.Part( + root=a2a_types.TextPart( + text="Regular text", + metadata={"some_other_key": "value"}, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert result.text == "Regular text" + assert result.thought is None + def test_convert_file_part_with_uri(self): """Test conversion of A2A FilePart with URI to GenAI Part.""" # Arrange @@ -96,6 +152,54 @@ def test_convert_file_part_with_bytes(self): assert result.inline_data.data == test_bytes assert result.inline_data.mime_type == "text/plain" + def test_convert_file_part_with_uri_and_thought_metadata(self): + """Test conversion of A2A FilePart with URI and thought metadata to GenAI Part.""" + # Arrange + a2a_part = a2a_types.Part( + root=a2a_types.FilePart( + file=a2a_types.FileWithUri( + uri="gs://bucket/file.txt", mime_type="text/plain" + ), + metadata={_get_adk_metadata_key("thought"): True}, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert isinstance(result, genai_types.Part) + assert result.file_data is not None + assert result.file_data.file_uri == "gs://bucket/file.txt" + assert result.thought is True + + def test_convert_file_part_with_bytes_and_thought_metadata(self): + """Test conversion of A2A FilePart with bytes and thought metadata to GenAI Part.""" + # Arrange + import base64 + + test_bytes = b"test file content" + base64_encoded = base64.b64encode(test_bytes).decode("utf-8") + a2a_part = a2a_types.Part( + root=a2a_types.FilePart( + file=a2a_types.FileWithBytes( + bytes=base64_encoded, mime_type="text/plain" + ), + metadata={_get_adk_metadata_key("thought"): True}, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert isinstance(result, genai_types.Part) + assert result.inline_data is not None + assert result.inline_data.data == test_bytes + assert result.thought is True + def test_convert_data_part_function_call(self): """Test conversion of A2A DataPart with function call metadata.""" # Arrange @@ -125,6 +229,34 @@ def test_convert_data_part_function_call(self): assert result.function_call.name == "test_function" assert result.function_call.args == {"param1": "value1", "param2": 42} + def test_convert_data_part_function_call_with_thought_metadata(self): + """Test conversion of A2A DataPart with function call and thought metadata.""" + # Arrange + function_call_data = { + "name": "test_function", + "args": {"param1": "value1"}, + } + a2a_part = a2a_types.Part( + root=a2a_types.DataPart( + data=function_call_data, + metadata={ + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL, + _get_adk_metadata_key("thought"): True, + }, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert result.function_call is not None + assert result.function_call.name == "test_function" + assert result.thought is True + def test_convert_data_part_function_response(self): """Test conversion of A2A DataPart with function response metadata.""" # Arrange @@ -242,7 +374,7 @@ def test_convert_unsupported_part_type(self): # Arrange - Create a mock unsupported part type class UnsupportedPartType: - pass + metadata = None # Required since code accesses part.metadata mock_part = Mock() mock_part.root = UnsupportedPartType() @@ -516,6 +648,38 @@ def test_text_part_round_trip(self): assert isinstance(result_a2a_part.root, a2a_types.TextPart) assert result_a2a_part.root.text == original_text + def test_text_part_with_thought_round_trip(self): + """Test round-trip conversion for text parts with thought=True.""" + # Arrange - Start with GenAI part with thought=True + original_text = "I'm reasoning about this problem..." + genai_part = genai_types.Part(text=original_text, thought=True) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert isinstance(result_genai_part, genai_types.Part) + assert result_genai_part.text == original_text + assert result_genai_part.thought is True + + def test_text_part_with_thought_false_round_trip(self): + """Test round-trip conversion for text parts with thought=False.""" + # Arrange - Start with GenAI part with thought=False + original_text = "This is not a thought." + genai_part = genai_types.Part(text=original_text, thought=False) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert isinstance(result_genai_part, genai_types.Part) + assert result_genai_part.text == original_text + assert result_genai_part.thought is False + def test_file_uri_round_trip(self): """Test round-trip conversion for file parts with URI.""" # Arrange From 7e41a9458b8f3a05c6f822d0aa5c1d3ebbf15868 Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Thu, 22 Jan 2026 17:05:18 -0300 Subject: [PATCH 2/3] fix a2a genai part convert --- .../adk/a2a/converters/part_converter.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/google/adk/a2a/converters/part_converter.py b/src/google/adk/a2a/converters/part_converter.py index f5b525a90c..09f32ad571 100644 --- a/src/google/adk/a2a/converters/part_converter.py +++ b/src/google/adk/a2a/converters/part_converter.py @@ -73,7 +73,8 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( file_data=genai_types.FileData( file_uri=part.file.uri, mime_type=part.file.mime_type - ), thought=thought + ), + thought=thought, ) elif isinstance(part.file, a2a_types.FileWithBytes): @@ -81,7 +82,8 @@ def convert_a2a_part_to_genai_part( inline_data=genai_types.Blob( data=base64.b64decode(part.file.bytes), mime_type=part.file.mime_type, - ), thought=thought + ), + thought=thought, ) else: logger.warning( @@ -109,7 +111,8 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( function_call=genai_types.FunctionCall.model_validate( part.data, by_alias=True - ), thought=thought + ), + thought=thought, ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -118,7 +121,8 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( function_response=genai_types.FunctionResponse.model_validate( part.data, by_alias=True - ), thought=thought + ), + thought=thought, ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -127,7 +131,8 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( code_execution_result=genai_types.CodeExecutionResult.model_validate( part.data, by_alias=True - ), thought=thought + ), + thought=thought, ) if ( part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)] @@ -136,7 +141,8 @@ def convert_a2a_part_to_genai_part( return genai_types.Part( executable_code=genai_types.ExecutableCode.model_validate( part.data, by_alias=True - ), thought=thought + ), + thought=thought, ) return genai_types.Part( inline_data=genai_types.Blob( @@ -146,7 +152,8 @@ def convert_a2a_part_to_genai_part( ) + A2A_DATA_PART_END_TAG, mime_type=A2A_DATA_PART_TEXT_MIME_TYPE, - ), thought=thought + ), + thought=thought, ) logger.warning( From b00d26c6f1fe6e8423f7a5b482334f63e091b02f Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Fri, 23 Jan 2026 09:49:25 -0300 Subject: [PATCH 3/3] fix part converter --- .../adk/a2a/converters/part_converter.py | 78 ++++--- .../a2a/converters/test_part_converter.py | 211 ++++++++++++++++++ 2 files changed, 258 insertions(+), 31 deletions(-) diff --git a/src/google/adk/a2a/converters/part_converter.py b/src/google/adk/a2a/converters/part_converter.py index 09f32ad571..d89852460b 100644 --- a/src/google/adk/a2a/converters/part_converter.py +++ b/src/google/adk/a2a/converters/part_converter.py @@ -177,14 +177,15 @@ def convert_genai_part_to_a2a_part( return a2a_types.Part(root=a2a_part) if part.file_data: - return a2a_types.Part( - root=a2a_types.FilePart( - file=a2a_types.FileWithUri( - uri=part.file_data.file_uri, - mime_type=part.file_data.mime_type, - ) + a2a_part = a2a_types.FilePart( + file=a2a_types.FileWithUri( + uri=part.file_data.file_uri, + mime_type=part.file_data.mime_type, ) ) + if part.thought is not None: + a2a_part.metadata = {_get_adk_metadata_key('thought'): part.thought} + return a2a_types.Part(root=a2a_part) if part.inline_data: if ( @@ -208,12 +209,15 @@ def convert_genai_part_to_a2a_part( ) ) + metadata = {} if part.video_metadata: - a2a_part.metadata = { - _get_adk_metadata_key( - 'video_metadata' - ): part.video_metadata.model_dump(by_alias=True, exclude_none=True) - } + metadata[_get_adk_metadata_key('video_metadata')] = ( + part.video_metadata.model_dump(by_alias=True, exclude_none=True) + ) + if part.thought is not None: + metadata[_get_adk_metadata_key('thought')] = part.thought + if metadata: + a2a_part.metadata = metadata return a2a_types.Part(root=a2a_part) @@ -223,58 +227,70 @@ def convert_genai_part_to_a2a_part( # TODO once A2A defined how to service such information, migrate below # logic accordingly if part.function_call: + metadata = { + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL + } + if part.thought is not None: + metadata[_get_adk_metadata_key('thought')] = part.thought return a2a_types.Part( root=a2a_types.DataPart( data=part.function_call.model_dump( by_alias=True, exclude_none=True ), - metadata={ - _get_adk_metadata_key( - A2A_DATA_PART_METADATA_TYPE_KEY - ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL - }, + metadata=metadata, ) ) if part.function_response: + metadata = { + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE + } + if part.thought is not None: + metadata[_get_adk_metadata_key('thought')] = part.thought return a2a_types.Part( root=a2a_types.DataPart( data=part.function_response.model_dump( by_alias=True, exclude_none=True ), - metadata={ - _get_adk_metadata_key( - A2A_DATA_PART_METADATA_TYPE_KEY - ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE - }, + metadata=metadata, ) ) if part.code_execution_result: + metadata = { + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT + } + if part.thought is not None: + metadata[_get_adk_metadata_key('thought')] = part.thought return a2a_types.Part( root=a2a_types.DataPart( data=part.code_execution_result.model_dump( by_alias=True, exclude_none=True ), - metadata={ - _get_adk_metadata_key( - A2A_DATA_PART_METADATA_TYPE_KEY - ): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT - }, + metadata=metadata, ) ) if part.executable_code: + metadata = { + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE + } + if part.thought is not None: + metadata[_get_adk_metadata_key('thought')] = part.thought return a2a_types.Part( root=a2a_types.DataPart( data=part.executable_code.model_dump( by_alias=True, exclude_none=True ), - metadata={ - _get_adk_metadata_key( - A2A_DATA_PART_METADATA_TYPE_KEY - ): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE - }, + metadata=metadata, ) ) diff --git a/tests/unittests/a2a/converters/test_part_converter.py b/tests/unittests/a2a/converters/test_part_converter.py index b720a8f632..62a7d78c72 100644 --- a/tests/unittests/a2a/converters/test_part_converter.py +++ b/tests/unittests/a2a/converters/test_part_converter.py @@ -289,6 +289,92 @@ def test_convert_data_part_function_response(self): "data": [1, 2, 3], } + def test_convert_data_part_function_response_with_thought_metadata(self): + """Test conversion of A2A DataPart with function response and thought metadata.""" + # Arrange + function_response_data = { + "name": "test_function", + "response": {"result": "success"}, + } + a2a_part = a2a_types.Part( + root=a2a_types.DataPart( + data=function_response_data, + metadata={ + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE, + _get_adk_metadata_key("thought"): True, + }, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert result.function_response is not None + assert result.function_response.name == "test_function" + assert result.thought is True + + def test_convert_data_part_code_execution_result_with_thought_metadata(self): + """Test conversion of A2A DataPart with code execution result and thought metadata.""" + # Arrange + code_execution_result_data = { + "outcome": "OUTCOME_OK", + "output": "Hello, World!", + } + a2a_part = a2a_types.Part( + root=a2a_types.DataPart( + data=code_execution_result_data, + metadata={ + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT, + _get_adk_metadata_key("thought"): True, + }, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert result.code_execution_result is not None + assert ( + result.code_execution_result.outcome == genai_types.Outcome.OUTCOME_OK + ) + assert result.thought is True + + def test_convert_data_part_executable_code_with_thought_metadata(self): + """Test conversion of A2A DataPart with executable code and thought metadata.""" + # Arrange + executable_code_data = { + "language": "PYTHON", + "code": "print('Hello, World!')", + } + a2a_part = a2a_types.Part( + root=a2a_types.DataPart( + data=executable_code_data, + metadata={ + _get_adk_metadata_key( + A2A_DATA_PART_METADATA_TYPE_KEY + ): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE, + _get_adk_metadata_key("thought"): True, + }, + ) + ) + + # Act + result = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result is not None + assert result.executable_code is not None + assert result.executable_code.language == genai_types.Language.PYTHON + assert result.thought is True + @pytest.mark.parametrize( "test_name, data, metadata", [ @@ -729,6 +815,49 @@ def test_file_bytes_round_trip(self): assert result_genai_part.inline_data.data == original_bytes assert result_genai_part.inline_data.mime_type == original_mime_type + def test_file_uri_part_with_thought_round_trip(self): + """Test round-trip conversion for file_data parts with thought=True.""" + # Arrange - Start with GenAI part with file_data and thought=True + genai_part = genai_types.Part( + file_data=genai_types.FileData( + file_uri="gs://bucket/file.txt", mime_type="text/plain" + ), + thought=True, + ) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert isinstance(result_genai_part, genai_types.Part) + assert result_genai_part.file_data is not None + assert result_genai_part.file_data.file_uri == "gs://bucket/file.txt" + assert result_genai_part.thought is True + + def test_file_bytes_part_with_thought_round_trip(self): + """Test round-trip conversion for inline_data parts with thought=True.""" + # Arrange - Start with GenAI part with inline_data and thought=True + original_bytes = b"test file content" + genai_part = genai_types.Part( + inline_data=genai_types.Blob( + data=original_bytes, mime_type="application/octet-stream" + ), + thought=True, + ) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert isinstance(result_genai_part, genai_types.Part) + assert result_genai_part.inline_data is not None + assert result_genai_part.inline_data.data == original_bytes + assert result_genai_part.thought is True + def test_function_call_round_trip(self): """Test round-trip conversion for function call parts.""" # Arrange @@ -748,6 +877,24 @@ def test_function_call_round_trip(self): assert result_genai_part.function_call.name == function_call.name assert result_genai_part.function_call.args == function_call.args + def test_function_call_with_thought_round_trip(self): + """Test round-trip conversion for function call parts with thought=True.""" + # Arrange + function_call = genai_types.FunctionCall( + name="test_function", args={"param1": "value1"} + ) + genai_part = genai_types.Part(function_call=function_call, thought=True) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert result_genai_part.function_call is not None + assert result_genai_part.function_call.name == function_call.name + assert result_genai_part.thought is True + def test_function_response_round_trip(self): """Test round-trip conversion for function response parts.""" # Arrange @@ -770,6 +917,26 @@ def test_function_response_round_trip(self): == function_response.response ) + def test_function_response_with_thought_round_trip(self): + """Test round-trip conversion for function response parts with thought=True.""" + # Arrange + function_response = genai_types.FunctionResponse( + name="test_function", response={"result": "success"} + ) + genai_part = genai_types.Part( + function_response=function_response, thought=True + ) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert result_genai_part.function_response is not None + assert result_genai_part.function_response.name == function_response.name + assert result_genai_part.thought is True + def test_code_execution_result_round_trip(self): """Test round-trip conversion for code execution result parts.""" # Arrange @@ -795,6 +962,29 @@ def test_code_execution_result_round_trip(self): == code_execution_result.output ) + def test_code_execution_result_with_thought_round_trip(self): + """Test round-trip conversion for code execution result parts with thought=True.""" + # Arrange + code_execution_result = genai_types.CodeExecutionResult( + outcome=genai_types.Outcome.OUTCOME_OK, output="Hello, World!" + ) + genai_part = genai_types.Part( + code_execution_result=code_execution_result, thought=True + ) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert result_genai_part.code_execution_result is not None + assert ( + result_genai_part.code_execution_result.outcome + == code_execution_result.outcome + ) + assert result_genai_part.thought is True + def test_executable_code_round_trip(self): """Test round-trip conversion for executable code parts.""" # Arrange @@ -816,6 +1006,27 @@ def test_executable_code_round_trip(self): ) assert result_genai_part.executable_code.code == executable_code.code + def test_executable_code_with_thought_round_trip(self): + """Test round-trip conversion for executable code parts with thought=True.""" + # Arrange + executable_code = genai_types.ExecutableCode( + language=genai_types.Language.PYTHON, code="print('Hello, World!')" + ) + genai_part = genai_types.Part(executable_code=executable_code, thought=True) + + # Act - Round trip: GenAI -> A2A -> GenAI + a2a_part = convert_genai_part_to_a2a_part(genai_part) + result_genai_part = convert_a2a_part_to_genai_part(a2a_part) + + # Assert + assert result_genai_part is not None + assert result_genai_part.executable_code is not None + assert ( + result_genai_part.executable_code.language == executable_code.language + ) + assert result_genai_part.executable_code.code == executable_code.code + assert result_genai_part.thought is True + def test_data_part_round_trip(self): """Test round-trip conversion for data parts.""" # Arrange