Python: fix(ag-ui): clean confirm_changes approval payloads in snapshot - #7316
Python: fix(ag-ui): clean confirm_changes approval payloads in snapshot#7316Henry Su (hsusul) wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes AG-UI’s confirm_changes human-in-the-loop flow so that end-of-turn MESSAGES_SNAPSHOT no longer retains the raw approval payload ({"accepted": ...}) and instead preserves the executed tool result, preventing clients (e.g., CopilotKit) from reverting completed tool chips back to “Running” on subsequent turns.
Changes:
- Updated
_clean_resolved_approvals_from_snapshotto handleconfirm_changessynthetic tool call IDs when replacing approval payloads in snapshot tool messages. - Added new unit tests to validate snapshot cleaning behavior for
confirm_changesapproval and rejection cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py | Adjusts snapshot post-processing to replace approval JSON payloads with tool results, including a fallback path for confirm_changes. |
| python/packages/ag-ui/tests/ag_ui/test_confirm_changes_snapshot.py | Adds tests ensuring confirm_changes approval/rejection payloads are cleaned and replaced appropriately in snapshot messages. |
| if replacement is None: | ||
| # For confirm_changes synthetic tool calls, tool_call_id is the confirm_id | ||
| # while result_by_call_id is keyed by the original tool call id. | ||
| if parsed.get("accepted"): | ||
| replacement = ( | ||
| "\n\n".join(result_by_call_id.values()) | ||
| if result_by_call_id | ||
| else "Changes confirmed and applied successfully." | ||
| ) | ||
| else: | ||
| replacement = "Changes declined." |
| { | ||
| "id": confirm_call_id, | ||
| "type": "function", | ||
| "function": {"name": "confirm_changes", "arguments": "{}"}, | ||
| }, |
|
Henry Su (@hsusul), thank you for the confirm_changes snapshot cleanup work. The consolidated draft PR #7345 incorporates the fix while correlating the synthetic confirmation back to its original function_call_id so unrelated results are never joined. Closing this PR in favor of the consolidated work. |
Motivation & Context
In AG-UI, when human-in-the-loop tool execution is approved via the
confirm_changesflow (e.g., CopilotKituseHumanInTheLoop), the approval response payload ({"accepted": true}) remains insnapshot_messagesbecause_clean_resolved_approvals_from_snapshotlooks up tool results inresult_by_call_idusingsnap_msg.toolCallId. Forconfirm_changessynthetic tool calls,toolCallIdis theconfirm_id, which does not match the original tool call ID inresult_by_call_id.As a result, the raw approval JSON payload persists in
snapshot_messagesand the original tool call result is omitted. On subsequent turns, clients that reconcile UI state fromMESSAGES_SNAPSHOT(such as CopilotKit) see an un-resulted tool call and revert the completed tool chip back to "Running" / "in progress".Description & Review Guide
_clean_resolved_approvals_from_snapshotinagent_framework_ag_ui/_agent_run.pyso that whentoolCallIddoes not directly matchresult_by_call_id(e.g.confirm_changessynthetic approval tool calls), it still cleans the{"accepted": ...}approval payload and replaces it with the executed tool result (or confirmation summary).tests/ag_ui/test_confirm_changes_snapshot.pyverifying thatconfirm_changesapproval and rejection payloads are cleaned up and replaced with actual tool results in snapshot messages._clean_resolved_approvals_from_snapshotfor matching synthetic approval tool calls whentool_call_idis not present directly inresult_by_call_id.Related Issue
Fixes #6828
Contribution Checklist