Repository navigation
Python: fix(core): keep annotations and metadata when from_dict builds data content - #8701
Merged
Eduard van Valkenburg (eavanvalkenburg) merged 1 commit intoSep 24, 2026
Conversation
…s data content
`Content.from_dict` pops `annotations`, `additional_properties` and
`raw_representation` off the mapping before dispatching, then returns early
for a data content:
return cls.from_data(remaining["data"], remaining["media_type"])
`from_data` takes all three as keyword arguments, but none were passed, so a
data content lost them while every other content type kept them:
Content.from_dict({"type": "data", "data": b"hello", "media_type": "text/plain",
"additional_properties": {"origin": "upload"}})
# main: additional_properties == {}
This is reachable from `Message.from_dict`, `ChatResponse.from_dict` and
`ChatResponseUpdate.from_dict`, so rebuilding a stored conversation dropped
the metadata on any data content in it, silently.
Pass the three through.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyCUJcDPnCnQMmuDwsnDmD
Anish Mehta (anishmehta24)
temporarily deployed
to
github-app-auth
September 23, 2026 17:00 — with
GitHub Actions
Inactive
Anish Mehta (anishmehta24)
temporarily deployed
to
github-app-auth
September 23, 2026 17:00 — with
GitHub Actions
Inactive
Eduard van Valkenburg (eavanvalkenburg)
approved these changes
Sep 24, 2026
Eduard van Valkenburg (eavanvalkenburg)
left a comment
Member
There was a problem hiding this comment.
Anish Mehta (@anishmehta24) The current head is green, and the dedicated full-diff review found no remaining actionable issues with preserving annotations and metadata when Content.from_dict constructs data content.
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
Content.from_dictpops the three shared fields off the mapping before dispatching:then hands them to the constructor at the bottom. But the
datacase returns before ever reaching it:from_dataacceptsannotations,additional_propertiesandraw_representationas keyword arguments — they just weren't being passed. So a data content silently lost all three, while every other content type kept them:This isn't only a direct-call concern.
Content.from_dictis whatMessage.from_dict,ChatResponse.from_dictandChatResponseUpdate.from_dictuse to rebuild contents, so restoring a stored conversation dropped the metadata on any data content it held, with nothing to indicate it.Worth noting it doesn't show up in a plain round trip, which is probably why it survived:
to_dict()on a data content emitsurirather thandata, sofrom_dict(to_dict(c))never takes this branch. It's the hand-built or provider-supplieddata+media_typemapping — the shape this branch exists to support — that loses the fields.Description
Pass the three popped fields to
from_data. One call, no signature change, no new behaviour — the special case now keeps what the general case already kept.One thing I left alone: this branch requires
datato bebytesand raisesTypeErroron a base64 string. That looks deliberate —from_data's docstring says "If you have a base64 string, decode it first" — so I didn't touch it, though it does mean the branch can't consume a mapping that came from JSON. Happy to look at that separately if it's worth changing.Contribution Checklist
Tests in
packages/core/tests/core/test_types.py:test_content_from_dict_data_keeps_annotations_and_metadata— all three survivetest_content_from_dict_data_matches_other_types_on_metadata— a data content and a uri content built from the same shape are treated the sametest_message_from_dict_keeps_data_content_metadata— the reachable path, throughMessage.from_dictAll three fail on
main.tests/core/test_types.pypasses in full and the wholetests/coredirectory is green.ruff checkandruff formatclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01WyCUJcDPnCnQMmuDwsnDmD