Skip to content

Conversation

@filipecaixeta
Copy link

@filipecaixeta filipecaixeta commented Jan 22, 2026

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

#4242

2. Or, if no issue exists, describe the change:

Problem:

The part converter in part_converter.py had asymmetric handling of thought metadata:

  1. A2A → GenAI (convert_a2a_part_to_genai_part): Metadata was completely ignored, so thought was always None for all part types.

  2. GenAI → A2A (convert_genai_part_to_a2a_part): thought was only preserved for TextPart, but dropped for FilePart (file_data, inline_data) and all DataPart types (function_call, function_response, code_execution_result, executable_code).

This breaks multi-agent scenarios where reasoning/thought content needs to survive round-trip conversions between agents.

Solution:

  1. A2A → GenAI: Extract thought from part metadata at the beginning of convert_a2a_part_to_genai_part and pass it to all genai_types.Part() constructors.

  2. GenAI → A2A: Add thought metadata preservation to all part types in convert_genai_part_to_a2a_part:

    • FilePart with file_data
    • FilePart with inline_data (merged with existing video_metadata handling)
    • DataPart for function_call, function_response, code_execution_result, executable_code

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Manual End-to-End (E2E) Tests:

The fix can be verified by setting up two agents communicating via A2A where the sub-agent generates content with thought=True. Before this fix, the parent agent would receive the content with thought=None. After this fix, thought=True is preserved through the full round-trip.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

Files changed:

  • src/google/adk/a2a/converters/part_converter.py - Added bidirectional thought metadata preservation
  • tests/unittests/a2a/converters/test_part_converter.py - Added 17 new tests for thought preservation

@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Jan 22, 2026
@ryanaiagent ryanaiagent self-assigned this Jan 23, 2026
@ryanaiagent
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request aims to preserve thought metadata during A2A to GenAI part conversions. The changes correctly handle the A2A to GenAI direction by extracting thought from metadata and passing it to the genai_types.Part constructors. The accompanying unit tests verify this for several part types.

However, the fix is incomplete for a full round-trip conversion. The reverse conversion (GenAI to A2A) in convert_genai_part_to_a2a_part does not preserve the thought field for most part types, which contradicts the goal of the PR. I've added comments in the test files suggesting adding more comprehensive round-trip tests to expose and address this issue. Additionally, test coverage for the current changes could be improved by adding tests for all DataPart variants.

Comment on lines +651 to +665
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

These round-trip tests for TextPart are great for ensuring thought is preserved. However, the fix for preserving thought seems incomplete for the full round-trip. The convert_genai_part_to_a2a_part function only handles thought for TextPart and will drop it for other part types like FilePart and DataPart.

I recommend adding similar round-trip tests for other part types (e.g., test_file_part_with_thought_round_trip) to expose this bug. Such a test would start with a genai_types.Part containing file_data and thought=True, convert it to A2A and back, and then assert that thought is still True in the final genai_types.Part. This would currently fail and help ensure the fix is complete for all scenarios.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please address this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +232 to +258
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is a good test for function_call with thought metadata. However, the changes in part_converter.py also add thought preservation for other DataPart types like function_response, code_execution_result, and executable_code. To ensure full test coverage for the changes, please consider adding similar tests for these other DataPart types.

@ryanaiagent ryanaiagent added a2a [Component] This issue is related a2a support inside ADK. and removed core [Component] This issue is related to the core interface and implementation labels Jan 23, 2026
@ryanaiagent
Copy link
Collaborator

Hi @filipecaixeta , Thank you for your contribution! We appreciate you taking the time to submit this pull request.
Could you also address the recommendations before we can proceed with the review.

@ryanaiagent ryanaiagent added the request clarification [Status] The maintainer need clarification or more information from the author label Jan 23, 2026
@filipecaixeta filipecaixeta changed the title fix(a2a): Preserve thought metadata in A2A to GenAI part conversion fix(a2a): Preserve thought metadata in bidirectional part conversions Jan 23, 2026
@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a2a [Component] This issue is related a2a support inside ADK. core [Component] This issue is related to the core interface and implementation request clarification [Status] The maintainer need clarification or more information from the author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants