Skip to content

fix(a2a): do not convert a resumed task's earlier artifacts again - #7604

Open
Yatsuiii wants to merge 1 commit into
google:mainfrom
Yatsuiii:fix/a2a-resumed-task-repeats-artifacts
Open

Yatsuiii wants to merge 1 commit into
google:mainfrom
Yatsuiii:fix/a2a-resumed-task-repeats-artifacts

Conversation

@Yatsuiii

Copy link
Copy Markdown
Contributor

A non-streaming response to a resumed A2A task carries every earlier turn's artifacts, and both task converters flatten them, so RemoteA2aAgent emitted the earlier turns again: after a tool confirmation, the original call and its "requires confirmation" response came back with the result.

Before converting a task, RemoteA2aAgent now drops the artifact parts it already converted for that task. Each event it yields records the raw task snapshot under a2a:response, so they are read from the session. _compat.task_without_seen_artifact_parts makes the copy on both SDK versions:

  • Parts are compared by content against the last snapshot of each artifact, so an artifact replaced under the same id is still converted, and only the new parts of an appended one are.
  • When every artifact was already converted, the copy also drops the task history, so the legacy converter cannot fall back to an earlier turn's agent message.

Behaviour change

Only for a task the agent has already received artifacts from. Within one turn every artifact is still converted, as #7201 intended. The pause marker of a task that stays input-required comes from its status message and is unchanged.

A custom a2a_task_converter still receives the whole task, as it always has. Other overrides keep the fix: the snapshots are written after the after-request hooks run, so a hook cannot alter them, and an event that a converter or hook suppresses records nothing, so its artifacts are converted when they come again. This opt-out is narrower than _has_default_response_persistence, which would have left out setups like the one in the consuming quickstart, with a custom message converter and interceptor. Where the earlier events or their custom_metadata are gone, the task is converted whole, as before.

A Workflow node backed by RemoteA2aAgent (#7589) now gets only the resumed turn's content from a non-streaming response, as it does when streaming.

Tests

  • test_a_resumed_task_returns_only_its_new_turn, in both integration suites, streaming and not, scripts the events a require_confirmation=True tool produces. Without the change it fails on 0.3 unary, 1.x unary and 1.x streaming; 0.3 streaming passes either way.
  • test_a_resumed_task_converts_only_artifacts_not_seen_before, for both _handle_a2a_response and _handle_a2a_response_v2: an earlier turn's artifact is skipped, an artifact replaced under the same id is kept, and with nothing new no earlier history message comes back. The first and last fail without the change.
  • With a custom message converter and an after-request hook the fix still applies, and a custom task converter receives the unchanged task.
  • task_without_seen_artifact_parts: seen, appended and replaced artifacts, and when history is dropped.
  • tests/unittests/a2a: 964 passed, 30 skipped on a2a-sdk 0.3.26; 937 passed, 57 skipped on 1.1.4.

I also ran a real LlmAgent behind the executor, with a require_confirmation=True tool and a LongRunningFunctionTool, across a2a-sdk 0.3 and 1.x, both executors, and streaming and unary clients: 12 scenarios, 192 runs, including follow-ups that leave the task paused. 10 runs changed, all of them unary on the new executor with a resumed task, and in each only the repeated earlier turn went away; what the server received was identical.

Fixes #7603

A non-streaming A2A response is the whole task, and a task's artifacts
accumulate over its life. When a task that paused for input, such as a
tool confirmation or a long-running tool, is resumed, the response
carries every earlier turn's artifacts again. Both task converters
flatten all of them, so RemoteA2aAgent emitted the earlier turns again:
after a confirmation, the original tool call and its "requires
confirmation" response arrived again with the result.

Before converting a task, RemoteA2aAgent now drops the artifact parts it
already converted for that task. It reads them from the raw task
snapshots recorded on its earlier events, which are written after the
after-request hooks run, so overridden hooks and message converters
keep the fix; only a custom task converter still receives the whole
task. Parts are compared by content, so an artifact replaced under the
same id is still converted, and only the new parts of an appended one
are. When every artifact was already converted, the task history is
dropped from the copy too, so the legacy converter cannot fall back to
an earlier turn's message.
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.

RemoteA2aAgent emits a resumed task's earlier turns again with a non-streaming client

1 participant