Skip to content

fix: preserve empty-string text parts in A2A converter#5343

Closed
voidborne-d wants to merge 6 commits intogoogle:mainfrom
voidborne-d:fix/part-converter-empty-text
Closed

fix: preserve empty-string text parts in A2A converter#5343
voidborne-d wants to merge 6 commits intogoogle:mainfrom
voidborne-d:fix/part-converter-empty-text

Conversation

@voidborne-d
Copy link
Copy Markdown
Contributor

Summary

Fixes #5341convert_genai_part_to_a2a_part() drops Part(text='') because the truthiness check if part.text: treats empty strings as falsy.

Root Cause

Part(text='') is valid and is produced in at least two places in the ADK:

  1. code_executors/code_execution_utils.py — when code execution completes with None output
  2. models/interactions_utils.py — when the Interactions API returns None text content

Gemini 2.5 Flash (thinking mode) also emits empty text parts.

When the converter drops all parts the A2A message ends up with zero parts and the client sees "broken thinking" with no content.

Fix

Change line 182 from if part.text: to if part.text is not None: so that empty strings are correctly wrapped as TextPart while None is still skipped.

Test

Added test_convert_empty_text_part — verifies that Part(text='') produces a valid TextPart(text='') instead of returning None.

@rohityan rohityan self-assigned this Apr 16, 2026
@voidborne-d voidborne-d force-pushed the fix/part-converter-empty-text branch 4 times, most recently from e141980 to 20f7177 Compare April 20, 2026 12:07
@rohityan rohityan added a2a [Component] This issue is related a2a support inside ADK. needs review [Status] The PR/issue is awaiting review from the maintainer labels Apr 20, 2026
@rohityan
Copy link
Copy Markdown
Collaborator

Hi @voidborne-d, Thank you for your contribution! We appreciate you taking the time to submit this pull request. Your PR has been received by the team and is currently under review. We will provide feedback as soon as we have an update to share.

@rohityan
Copy link
Copy Markdown
Collaborator

Hi @DeanChensj , can you please review this.

@voidborne-d voidborne-d force-pushed the fix/part-converter-empty-text branch 3 times, most recently from fac8136 to 8b9c374 Compare April 21, 2026 13:06
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 google#5341
@voidborne-d voidborne-d force-pushed the fix/part-converter-empty-text branch from 8b9c374 to ae61e3d Compare April 21, 2026 18:07
@DeanChensj
Copy link
Copy Markdown
Collaborator

Hi @voidborne-d , could you fix the formatting and lint issue?

@voidborne-d
Copy link
Copy Markdown
Contributor Author

Thanks for the ping @DeanChensj — pushed d9a7b94 applying the pyink reformat (pyink 25.12.0 wanted the implicit-paren grouping on the thought_signature literal). The pre-commit job is failing repo-wide because .pre-commit-config.yaml isn't in main, so that one is unrelated to this PR. Should be green on the next run.

@DeanChensj DeanChensj added the ready to pull [Status] This PR is ready to be imported back to Google label Apr 25, 2026
copybara-service Bot pushed a commit that referenced this pull request Apr 25, 2026
Merge #5343

## Summary

Fixes #5341 — `convert_genai_part_to_a2a_part()` drops `Part(text='')`  because the truthiness check `if part.text:` treats empty strings as falsy.

## Root Cause

`Part(text='')`  is valid and is produced in at least two places in the ADK:

1. **`code_executors/code_execution_utils.py`** — when code execution completes with `None` output
2. **`models/interactions_utils.py`** — when the Interactions API returns `None` text content

Gemini 2.5 Flash (thinking mode) also emits empty text parts.

When the converter drops all parts the A2A message ends up with zero parts and the client sees "broken thinking" with no content.

## Fix

Change line 182 from `if part.text:` to `if part.text is not None:` so that empty strings are correctly wrapped as `TextPart` while `None` is still skipped.

## Test

Added `test_convert_empty_text_part` — verifies that `Part(text='')`  produces a valid `TextPart(text='')`  instead of returning `None`.

COPYBARA_INTEGRATE_REVIEW=#5343 from voidborne-d:fix/part-converter-empty-text dbb2f20
PiperOrigin-RevId: 905370961
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Apr 25, 2026

Thank you @voidborne-d for your contribution! 🎉

Your changes have been successfully imported and merged via Copybara in commit 2d61cb6.

Closing this PR as the changes are now in the main branch.

@adk-bot adk-bot closed this Apr 25, 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. needs review [Status] The PR/issue is awaiting review from the maintainer ready to pull [Status] This PR is ready to be imported back to Google

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Part converter drops Part(text='') causing broken A2A responses

5 participants