Skip to content

feat(helpers): add non-text Part, Message, and Artifact helpers#1004

Open
martimfasantos wants to merge 2 commits intoa2aproject:mainfrom
martimfasantos:fix/data-part-struct
Open

feat(helpers): add non-text Part, Message, and Artifact helpers#1004
martimfasantos wants to merge 2 commits intoa2aproject:mainfrom
martimfasantos:fix/data-part-struct

Conversation

@martimfasantos
Copy link
Copy Markdown
Contributor

@martimfasantos martimfasantos commented Apr 21, 2026

Description


Summary

proto_helpers.py provided new_text_message and new_text_artifact for the text Part variant, but nothing for the three remaining Part types (data, raw, url). This PR completes the set.

The data case is especially awkward without a helper. Part.data is google.protobuf.Value in the v1.0 spec, which requires a non-obvious ParseDict dance to construct from a plain Python value:

# Without helper
from google.protobuf.json_format import ParseDict
from google.protobuf import struct_pb2
part = Part(data=ParseDict({"answer": "hello"}, struct_pb2.Value()))

# With helper
part = new_data_part({"answer": "hello"})

New helpers

Part primitives (building blocks, mirror the existing implicit Part(text=...) pattern):

Helper Part field Accepts
new_data_part(data) data (google.protobuf.Value) Any JSON-serializable value (dict, list, str, …)
new_raw_part(raw, media_type, filename) raw (bytes) Raw bytes with optional MIME type and filename
new_url_part(url, media_type, filename) url (str) URL with optional MIME type and filename

Message helpers (mirror new_text_message):

Helper Wraps
new_data_message(data, role, context_id, task_id) new_data_part
new_raw_message(raw, media_type, filename, role, context_id, task_id) new_raw_part
new_url_message(url, media_type, filename, role, context_id, task_id) new_url_part

Artifact helpers (mirror new_text_artifact):

Helper Wraps
new_data_artifact(name, data, description, artifact_id) new_data_part
new_raw_artifact(name, raw, media_type, filename, description, artifact_id) new_raw_part
new_url_artifact(name, url, media_type, filename, description, artifact_id) new_url_part

Changes

  • src/a2a/helpers/proto_helpers.py — 9 new helper functions
  • tests/helpers/test_proto_helpers.py — tests for all new helpers (35 total, all passing)

Reviewer feedback addressed

  • new_data_part type hint broadened from dict[str, Any] to Any, since google.protobuf.Value accepts any JSON-serializable value, not just dicts. Added a list-value test to cover this.

@martimfasantos martimfasantos requested a review from a team as a code owner April 21, 2026 17:35
Copy link
Copy Markdown
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 introduces new helper functions (new_data_part, new_raw_part, and new_url_part) to proto_helpers.py for creating various Part message types, along with corresponding unit tests. A suggestion was made to broaden the type hint for new_data_part from dict[str, Any] to Any, as google.protobuf.Value can represent any JSON-serializable type, including lists and scalars.

Comment thread src/a2a/helpers/proto_helpers.py Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 21, 2026

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/helpers/proto_helpers.py 94.12% 95.35% 🟢 +1.23%
Total 93.02% 93.03% 🟢 +0.02%

Generated by coverage-comment.yml

Part.data is google.protobuf.Value in the v1.0 spec, which requires a
non-obvious ParseDict dance to construct from a plain dict. Add
new_data_part() to hide that complexity. Also add new_raw_part() and
new_url_part() to cover the remaining non-text Part variants that had
no helpers, keeping the API consistent with new_text_message() et al.
@martimfasantos martimfasantos changed the title feat(helpers): add new_data_part, new_raw_part, and new_url_part part helpers feat(helpers): add non-text Part, Message, and Artifact helpers Apr 21, 2026
Returns:
A Part with the data field set.
"""
return Part(data=ParseDict(data, struct_pb2.Value()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why not including media_type and filename in this case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I saw this as a legacy DataPart from 0.3 - something like a dict. In that case, including a media type and file name doesn’t really make sense I believe. If I needed those parameters, I’d use a Raw Part instead. Am I missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants