Skip to content

Python: Fix missing methods on the Content class in durable tasks#4738

Merged
eavanvalkenburg merged 7 commits intomicrosoft:mainfrom
eavanvalkenburg:agent/fix-4719-1
Mar 18, 2026
Merged

Python: Fix missing methods on the Content class in durable tasks#4738
eavanvalkenburg merged 7 commits intomicrosoft:mainfrom
eavanvalkenburg:agent/fix-4719-1

Conversation

@eavanvalkenburg
Copy link
Member

Motivation and Context

The Content class exposes to_dict/from_dict for dictionary serialization but lacks corresponding to_json/from_json methods for JSON string serialization, forcing users to manually call json.dumps/json.loads as a workaround.

Fixes #4719

Description

The fix adds a to_json instance method that serializes Content to a JSON string via to_dict, and a from_json classmethod that deserializes a JSON string back into a Content instance via from_dict. Both methods delegate to the existing dict-based serialization logic to stay consistent. A minor type-narrowing fix in DurableAgentStateUnknownContent.to_ai_content was also included to resolve a related type-checking issue with Content.from_dict.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Note: PR autogenerated by eavanvalkenburg's agent

Copilot and others added 2 commits March 17, 2026 11:27
…ft#4719)

DurableAgentStateUnknownContent.from_unknown_content() stored raw Content
objects without converting them to dicts, causing json.dumps to fail in
Azure Durable Functions' entity state serialization. This affected content
types not explicitly handled (e.g., mcp_server_tool_call/result).

The fix converts Content objects to dicts via to_dict() when storing in
DurableAgentStateUnknownContent, and restores them via Content.from_dict()
in to_ai_content().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add to_json() and from_json() methods to the Content class to match the
serialization interface provided by SerializationMixin on other model classes.
Also fix pre-existing pyright type errors in durabletask's
DurableAgentStateUnknownContent.to_ai_content().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eavanvalkenburg eavanvalkenburg requested a review from a team as a code owner March 17, 2026 11:36
@eavanvalkenburg eavanvalkenburg self-assigned this Mar 17, 2026
Copilot AI review requested due to automatic review settings March 17, 2026 11:36
Copy link
Member Author

@eavanvalkenburg eavanvalkenburg left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 86%

✓ Correctness

Clean, straightforward addition of JSON serialization/deserialization methods to the Content class, plus a type-narrowing fix in the durable agent state module. The logic is correct and well-tested. One minor robustness concern: from_json passes the result of json.loads directly to from_dict without checking it's a dict, which could produce a confusing AttributeError instead of a clear ValueError if the JSON string represents a non-object type (e.g., a list or string). No blocking issues.

✓ Security Reliability

The diff adds to_json/from_json serialization helpers to Content and refactors a type-narrowing cast in the durable agent state module. The changes are low-risk overall. The main reliability concern is that from_json passes the result of json.loads directly to from_dict without verifying it is a dict, which means non-object JSON inputs (e.g., arrays, scalars) will produce confusing errors instead of a clear validation message.

✓ Test Coverage

The new to_json and from_json methods on Content are reasonably well tested: basic serialization/deserialization, the exclude_none flag, roundtrip, and invalid input (missing 'type'). However, the exclude parameter of to_json() has zero test coverage despite being part of the public API, and the **kwargs pass-through to json.dumps is also untested. The refactor in _durable_agent_state.py is purely a type-narrowing change with no behavioral difference, so no new tests are needed there.

✓ Design Approach

The diff adds to_json/from_json convenience methods to Content and fixes a type-narrowing issue in DurableAgentStateUnknownContent.to_ai_content. The JSON serialization methods are thin, correct wrappers around the existing to_dict/from_dict pair and introduce no design problems. The durable-state fix properly casts to dict[str, Any] after an isinstance guard. No fundamentally misguided approaches or leaky abstractions are present. The test coverage is thorough and aligns with the existing roundtrip pattern. The only minor concern is that from_json silently delegates all error handling to json.loads and from_dict; callers will receive a raw json.JSONDecodeError on malformed JSON rather than a domain-level ValueError, which is inconsistent with the ValueError raised by from_dict for semantic errors — but this is a modest inconsistency rather than a design flaw requiring changes.

Suggestions

  • In from_json, validate that json.loads(value) returns a dict before passing to from_dict, and consider wrapping json.JSONDecodeError into a ValueError for a consistent exception contract with from_dict. Currently, non-object JSON input (e.g., "[1,2,3]") raises a confusing AttributeError on .get("type"), and malformed JSON raises json.JSONDecodeError rather than the ValueError that from_dict uses for semantic errors.
  • Add a test for Content.to_json(exclude=...) — it's an explicit public API parameter with no coverage. For example, serialize a Content with a known field excluded and assert it's absent from the JSON output.
  • Consider adding a test that to_json(**kwargs) forwards options to json.dumps (e.g., to_json(indent=2) produces indented output), since the **kwargs pass-through is part of the public signature.

Automated review by eavanvalkenburg's agents

Copy link
Member Author

@eavanvalkenburg eavanvalkenburg left a comment

Choose a reason for hiding this comment

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

Please move the json.dumps into the durable task

@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Mar 17, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
TOTAL24006264089% 
report-only-changed-files is enabled. No files were changed during this commit :)

Python Unit Test Overview

Tests Skipped Failures Errors Time
5247 20 💤 0 ❌ 0 🔥 1m 25s ⏱️

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class JSON string serialization/deserialization for the Python Content type, and updates Durable Task state handling/tests to ensure MCP-related content can be JSON-serialized (addressing the Azure Durable Functions to_json expectation reported in #4719).

Changes:

  • Add Content.to_json() / Content.from_json() implemented via existing to_dict() / from_dict() logic.
  • Ensure DurableAgentStateUnknownContent stores Content instances as dicts (JSON-serializable) and can restore Content from those dicts.
  • Add unit tests for Content JSON roundtrips and Durable Task JSON serialization scenarios involving MCP content.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
python/packages/core/agent_framework/_types.py Adds to_json/from_json on Content.
python/packages/core/tests/core/test_types.py Adds tests for Content JSON serialization/deserialization.
python/packages/durabletask/agent_framework_durabletask/_durable_agent_state.py Makes unknown durable content JSON-safe by dict-serializing Content and restoring via Content.from_dict.
python/packages/durabletask/tests/test_durable_agent_state.py Adds Durable Task tests reproducing JSON serialization scenarios with MCP content.

You can also share your feedback on Copilot code review. Take the survey.

Copilot and others added 3 commits March 17, 2026 11:53
…tests

- Remove Content.to_json() per reviewer request (comment 3)
- Add type guard in Content.from_json() for non-dict JSON (comments 1, 4)
- Wrap json.JSONDecodeError as ValueError for consistent exception contract
- Add try/except fallback in to_ai_content() for invalid Content dicts (comment 5)
- Add test_content_to_dict_exclude_none and test_content_to_dict_exclude_fields (comment 2)
- Add test_unknown_content_to_ai_content_fallback_on_invalid_type_dict (comment 5)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot and others added 2 commits March 17, 2026 13:14
Remove the from_json convenience method from Content class per review
feedback. This is the same trivial json.loads + from_dict wrapper as
to_json which was already removed. Consumers should call json.loads
and Content.from_dict directly.

Update tests to use Content.from_dict(json.loads(...)) pattern and
remove from_json-specific error handling tests (those errors are
already covered by json.loads and Content.from_dict).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

@giles17 giles17 left a comment

Choose a reason for hiding this comment

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

PR title and description still reference to_json and from_json which were removed

@eavanvalkenburg eavanvalkenburg changed the title Python: Add to_json and from_json methods to the Content class Python: Fix missing methods on the Content class in durable tasks Mar 17, 2026
@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Mar 18, 2026
Merged via the queue into microsoft:main with commit 705ed47 Mar 18, 2026
31 checks passed
@github-project-automation github-project-automation bot moved this from In Review to Done in Agent Framework Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: class 'agent_framework._types.Content'> does not expose a to_json function

5 participants