Repository navigation
fix(workflow): stop a resume failing on fenced JSON that passed live - #7972
Closed
anmolg1997 wants to merge 1 commit into
Closed
anmolg1997 wants to merge 1 commit into
anmolg1997 wants to merge 1 commit into
Conversation
When a session holds only the reply text of a finished LlmAgent node with an output_schema, a resume rebuilds the node's output from that text in _process_rehydrated_output. Sessions saved by ADK 2.10 and earlier store schema replies this way. The function parsed the text without stripping a Markdown code fence, so a fenced reply that validate_schema accepted live failed the resumed run with WorkflowDataError. Strip the fence there with the same helper validate_schema uses. Fixes google#7971
copybara-service Bot
pushed a commit
that referenced
this pull request
Nov 4, 2026
Merge #7972 Sessions stored by ADK 2.10 and earlier keep a schema agent's reply as message text only, so resume rebuilds the output from that text. It now strips a Markdown code fence first, as the live path does, so a fenced reply accepted live no longer fails resume with WorkflowDataError. Part of #7971 PiperOrigin-RevId: 1013369198
Collaborator
|
Thank you @anmolg1997 for your contribution! 🎉 Your changes have been successfully imported and merged via Copybara in commit f4fa73e. Closing this PR as the changes are now in the main branch. |
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.
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):
Problem:
A session paused on ADK 2.10 or earlier cannot be resumed on 2.11.0 or later when an
LlmAgentnode with anoutput_schemahad replied with fenced JSON (```json ... ```). Those versions stored the node's reply as message text only, withoutput=None. On resume,_process_rehydrated_outputrebuilds the output from that text without stripping the fence (_rehydration_utils.py#L194-L214), so the resumed run fails withWorkflowDataErroron a reply thatvalidate_schemaaccepted live.Since 2.11.0 (6b96ba1, #6100) new sessions store the parsed output, so this only hits sessions stored by earlier versions.
Solution:
_process_rehydrated_outputnow calls_strip_json_code_fence, the helpervalidate_schemaalready uses, before it parses the text against the schema (_rehydration_utils.py#L198-L199). The live path and the replay path then share one fence helper.Only the schema branch changes. Unfenced text comes back from the helper unchanged, and nodes with no schema or with
output_schema=strstill get the raw text.Testing Plan
Unit Tests:
New tests:
test_resume_replays_schema_agent_output_as_parsed_liveintests/unittests/workflow/test_workflow_hitl.py: aWorkflowwith a schema-typedLlmAgenton a scripted model, then aRequestInputpause. It runs to the pause, resumes, and checks that the next node gets the parsed dict and that the model was called once. It covers fenced and unfenced replies, resumable and non-resumable apps, and both storage shapes: the parsed output that 2.11+ stores, and the message-only event that 2.10 and earlier stored (message_only).TestProcessRehydratedOutput::test_strips_markdown_fence_like_the_live_pathintests/unittests/workflow/utils/test_rehydration_utils.py.The new tests against
origin/main:With the fix:
9 passed.The same 14 tests fail on unmodified main in that environment, for reasons outside this change: 13 in
integrations/vmaas/test_agent_engine_environment.py(macOSstathas no-c), andtest_release_dependencies.py::test_inclusive_upper_bounds_name_an_installed_release, because that environment resolved OpenTelemetry 1.42.1, below the release the declared bound names.pre-commit runpasses on the changed files.mypy ., compared with main the way CI does it, reports no new errors.Manual End-to-End (E2E) Tests:
The script in #7971 pauses a workflow on one version and resumes it on another, with a SQLite
DatabaseSessionServiceand a scripted model that returns fenced JSON. I paused the session on 2.10.0 and resumed copies of the same database:A new session paused and resumed on the same version fails on 2.9.2 and 2.10.0 and passes on 2.11.0 and 2.13.0, which matches 6b96ba1.
Checklist
Additional context
I kept the change to the fence. Sending replay through
validate_schemawould also change how the output is dumped: live usesmodel_dump(exclude_none=True)(_schema_utils.py#L261) and replay usesmodel_dump()(_base_node.py#L176), so an unset optional field replays as an extraNonekey. That is a separate difference, and I left it alone.