Python: Reorganize A2A samples and use package A2AExecutor#6165
Conversation
…cutor - Move client samples (agent_with_a2a, a2a_agent_as_function_tools) to samples/02-agents/a2a/ - Add new concept samples: polling, stream reconnection, protocol selection - Replace sample agent_executor.py with package-level A2AExecutor (stream=True) - Update 04-hosting/a2a to focus on server-side, point to 02-agents for clients - Add README.md for the new 02-agents/a2a/ sample collection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Reorganizes the Python A2A (Agent2Agent) samples to separate client consumption examples from server hosting examples, and updates the hosting sample to use the package-provided A2AExecutor instead of a sample-local executor implementation.
Changes:
- Moved/updated A2A client samples under
python/samples/02-agents/a2a/and added new client scenarios (polling, stream reconnection, protocol selection). - Updated the A2A hosting server sample to use
agent_framework.a2a.A2AExecutor(streaming-enabled) and removed the sample-local executor. - Refreshed READMEs to reflect the new client/server split and cross-link between the sample sets.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| python/samples/04-hosting/a2a/README.md | Refocuses documentation on server hosting and links to the new client samples location. |
| python/samples/04-hosting/a2a/agent_executor.py | Removes the sample-local executor in favor of the package-level A2AExecutor. |
| python/samples/04-hosting/a2a/a2a_server.py | Switches server to A2AExecutor(agent, stream=True) from agent_framework.a2a. |
| python/samples/02-agents/a2a/README.md | Adds an overview README for the A2A client sample collection. |
| python/samples/02-agents/a2a/agent_with_a2a.py | Updates client sample output/notes to align with the new sample set. |
| python/samples/02-agents/a2a/a2a_polling.py | New sample demonstrating background submission + polling with continuation tokens. |
| python/samples/02-agents/a2a/a2a_stream_reconnection.py | New sample demonstrating resuming an interrupted stream using a continuation token. |
| python/samples/02-agents/a2a/a2a_protocol_selection.py | New sample demonstrating protocol binding preference/selection. |
| python/samples/02-agents/a2a/a2a_agent_as_function_tools.py | Updates run instructions to reflect the new client sample location. |
Comments suppressed due to low confidence (3)
python/samples/04-hosting/a2a/README.md:31
- The quick start says “All commands below should be run from this directory”, but step 2 immediately switches to
python/samples/02-agents/a2a. Please reword this so it’s clear which steps run from the server directory vs. the client samples directory.
## Quick Start
All commands below should be run from this directory:
python/samples/02-agents/a2a/agent_with_a2a.py:75
- Using
"".join(...)will concatenate multiple assistant messages without any separator, which can make output hard to read (and differs from the previous per-message printing). Consider joining with a delimiter (e.g., newline) or iterating and printing each assistant message separately.
python/samples/02-agents/a2a/agent_with_a2a.py:91 - Same as earlier in the file: concatenating assistant message texts with
"".join(...)can smash multiple messages together. Consider adding a separator or printing messages individually to preserve readability.
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 93%
✓ Correctness
The PR correctly reorganizes A2A samples and replaces a local executor with the package-level A2AExecutor. All API usages in new samples (background=True, poll_task(), continuation_token, stream reconnection, supported_protocol_bindings) are verified against the actual package implementation. The deleted agent_executor.py was only referenced by a2a_server.py which is properly updated. No correctness issues found.
✓ Security Reliability
This PR reorganizes A2A samples and replaces a local executor with the package-level A2AExecutor. The changes are well-structured: all httpx clients and A2AAgent instances use async context managers (no resource leaks), environment variables are validated before use, and the package A2AExecutor has proper error handling (CancelledError, generic exceptions with task status updates). The new samples correctly use the A2AContinuationToken TypedDict. No security or reliability issues found.
✓ Test Coverage
This PR reorganizes A2A samples and replaces a local agent_executor.py with the package-level A2AExecutor. From a test coverage perspective, this is well-supported: the package-level A2AExecutor has 36+ tests in test_a2a_executor.py covering initialization, streaming/non-streaming execution, cancellation, error handling, and event processing. The A2AAgent class (used by the new client samples) has extensive tests in test_a2a_agent.py (2,146 lines) covering poll_task, continuation tokens, background mode, supported_protocol_bindings, and streaming. The deleted local AgentFrameworkExecutor had no dedicated tests, but its replacement is thoroughly tested at the package level. No new test gaps are introduced.
✗ Design Approach
I found one design-level issue in the new stream reconection sample. The resumed
agent.run(...)call dropsbackground=True, but the A2A client implementation only surfaces in-progress resumed-task updates and continuation tokens whenbackground=True; otherwise those updates are silently consumed until a terminal event arrives. That means the sample no longer demonstrates true interrupted-stream resubscription behavior for tasks that are still running at reconnect time.
Flagged Issues
-
python/samples/02-agents/a2a/a2a_stream_reconnection.py:74-77resumes withstream=Truebut notbackground=True. The A2A client's_map_a2a_streamsilently consumes in-progress task updates whenbackground=False, and_updates_from_taskonly emits continuation tokens for in-progress tasks in background mode. The package's own resume test (test_resume_streaming_via_continuation_token) explicitly usesbackground=True, confirming this contract. Without it the sample no longer demonstrates true interrupted-stream resubscription for still-running tasks.
Automated review by giles17's agents
A2AExecutor fix: - Generate a stable artifact_id per stream in _run_stream so all streaming chunks share the same ID, enabling proper append=True coalescing per the A2A spec (TaskArtifactUpdateEvent with same artifactId). - Previously, item.message_id was None for OpenAI/Foundry streaming updates, causing the SDK to generate a new random UUID per token (100+ separate artifacts instead of 1 appended artifact). Sample improvements: - Replace join workaround with response.text now that coalescing works - Add background=True to stream reconnection resume call (required for continuation token emission on in-progress tasks) - Fix type ignore specificity in polling sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
Summary
Reorganizes A2A samples to separate client-side demos from server-side hosting, replaces the sample-local executor with the package-level A2AExecutor, and fixes a streaming artifact coalescing bug.
Changes
A2AExecutor Bug Fix
Fixed streaming artifact coalescing in the A2AExecutor. A stable artifact_id is now generated per stream so all chunks share the same ID with append=True, per the A2A spec. Previously item.message_id was None for OpenAI/Foundry streaming updates, causing the SDK to generate a new random UUID per token — resulting in 100+ separate artifacts instead of 1 appended artifact.
Sample Reorganization
New Samples (samples/02-agents/a2a/)
Documentation
Motivation