-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Python: Reorganize A2A samples and use package A2AExecutor #6165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Evan Mattson (moonbox3)
merged 2 commits into
microsoft:main
from
giles17:a2a-samples-reorganize
Jun 1, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # A2A Client Samples | ||
|
|
||
| These samples demonstrate how to **consume** remote A2A-compliant agents using the Agent Framework's `A2AAgent` class. | ||
|
|
||
| For hosting your own agents as A2A servers, see [`samples/04-hosting/a2a/`](../../04-hosting/a2a/). | ||
|
|
||
| ## Samples | ||
|
|
||
| | Sample | Concept | | ||
| |--------|---------| | ||
| | [`agent_with_a2a.py`](agent_with_a2a.py) | Basic consumption — non-streaming and streaming | | ||
| | [`a2a_agent_as_function_tools.py`](a2a_agent_as_function_tools.py) | Expose A2A skills as function tools for a host agent | | ||
| | [`a2a_polling.py`](a2a_polling.py) | Poll a long-running task with continuation tokens | | ||
| | [`a2a_stream_reconnection.py`](a2a_stream_reconnection.py) | Resume an interrupted stream via continuation token | | ||
| | [`a2a_protocol_selection.py`](a2a_protocol_selection.py) | Configure preferred protocol bindings (JSONRPC, GRPC, HTTP+JSON) | | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - A running A2A-compliant agent server (see `samples/04-hosting/a2a/` to start one) | ||
| - Set `A2A_AGENT_HOST` environment variable to the server URL | ||
| - For `a2a_agent_as_function_tools.py`: also set `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` | ||
|
|
||
| ## Running | ||
|
|
||
| ```bash | ||
| cd python/samples/02-agents/a2a | ||
|
|
||
| # Start an A2A server in another terminal first: | ||
| # cd python/samples/04-hosting/a2a && uv run python a2a_server.py | ||
|
|
||
| export A2A_AGENT_HOST="http://localhost:5001/" | ||
| uv run python agent_with_a2a.py | ||
| ``` | ||
|
|
||
| ## Key APIs | ||
|
|
||
| ```python | ||
| from agent_framework.a2a import A2AAgent | ||
|
|
||
| # Connect to a remote agent | ||
| async with A2AAgent(url="http://localhost:5001/", agent_card=card) as agent: | ||
| # Non-streaming | ||
| response = await agent.run("Hello") | ||
|
|
||
| # Streaming | ||
| stream = agent.run("Hello", stream=True) | ||
| async for update in stream: | ||
| print(update.text) | ||
|
|
||
| # Background + polling | ||
| response = await agent.run("Long task", background=True) | ||
| while response.continuation_token: | ||
| response = await agent.poll_task(response.continuation_token) | ||
| ``` |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| import asyncio | ||
| import os | ||
|
|
||
| import httpx | ||
| from a2a.client import A2ACardResolver | ||
| from agent_framework.a2a import A2AAgent | ||
| from dotenv import load_dotenv | ||
|
|
||
| load_dotenv() | ||
|
|
||
| """ | ||
| A2A Polling for Task Completion | ||
|
|
||
| This sample demonstrates how to poll a long-running A2A task for completion | ||
| using continuation tokens. When `background=True`, the agent returns immediately | ||
| with a continuation token that you can use to check progress later. | ||
|
|
||
| Key concepts demonstrated: | ||
| - Starting a background A2A task with `background=True` | ||
| - Receiving a continuation token for in-progress tasks | ||
| - Polling with `poll_task()` until the task reaches a terminal state | ||
|
|
||
| This is the A2A equivalent of the .NET A2AAgent_PollingForTaskCompletion sample. | ||
|
|
||
| Prerequisites: | ||
| - Set A2A_AGENT_HOST to the URL of a running A2A server | ||
|
|
||
| To run this sample: | ||
| cd python/samples/02-agents/a2a | ||
| uv run python a2a_polling.py | ||
| """ | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| """Demonstrates polling a long-running A2A task for completion.""" | ||
| a2a_agent_host = os.getenv("A2A_AGENT_HOST") | ||
| if not a2a_agent_host: | ||
| raise ValueError("A2A_AGENT_HOST environment variable is not set") | ||
|
|
||
| # 1. Resolve agent card and create agent. | ||
| async with httpx.AsyncClient(timeout=60.0) as http_client: | ||
| resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host) | ||
| agent_card = await resolver.get_agent_card() | ||
|
|
||
| async with A2AAgent( | ||
| name=agent_card.name, | ||
| agent_card=agent_card, | ||
| url=a2a_agent_host, | ||
| ) as agent: | ||
| # 2. Start a background task — the agent returns immediately. | ||
| print("Starting background task...") | ||
| response = await agent.run( | ||
| "Write a detailed research report on quantum computing advances in 2025", | ||
| background=True, | ||
| ) | ||
|
|
||
| # 3. Check if we got a continuation token (task still in progress). | ||
| if response.continuation_token is None: | ||
| # Task completed immediately — no polling needed. | ||
| print("Task completed immediately:") | ||
| print(f" {response.text}") | ||
| return | ||
|
|
||
| # 4. Poll until the task completes. | ||
| token = response.continuation_token | ||
| poll_count = 0 | ||
| while token is not None: | ||
| poll_count += 1 | ||
| print(f" Poll #{poll_count} — task still in progress, waiting 2s...") | ||
| await asyncio.sleep(2) | ||
|
|
||
| response = await agent.poll_task(token) # type: ignore[arg-type] | ||
| token = response.continuation_token | ||
|
|
||
| # 5. Task is done — print the final response. | ||
| print(f"\nTask completed after {poll_count} poll(s):") | ||
| print(f" {response.text[:200]}...") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) | ||
|
|
||
|
|
||
| """ | ||
| Sample output: | ||
|
|
||
| Starting background task... | ||
| Poll #1 — task still in progress, waiting 2s... | ||
| Poll #2 — task still in progress, waiting 2s... | ||
| Poll #3 — task still in progress, waiting 2s... | ||
|
|
||
| Task completed after 3 poll(s): | ||
| Quantum computing has seen remarkable progress in 2025, with breakthroughs in... | ||
| """ | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| import asyncio | ||
| import os | ||
|
|
||
| import httpx | ||
| from a2a.client import A2ACardResolver | ||
| from agent_framework.a2a import A2AAgent | ||
| from dotenv import load_dotenv | ||
|
|
||
| load_dotenv() | ||
|
|
||
| """ | ||
| A2A Protocol Selection | ||
|
|
||
| This sample demonstrates how to configure which protocol binding the A2A client | ||
| uses when connecting to a remote agent. The A2A specification defines three | ||
| standard bindings: JSONRPC, GRPC, and HTTP+JSON. Agents declare their supported | ||
| bindings in their AgentCard, and clients can express a preference. | ||
|
|
||
| Key concepts demonstrated: | ||
| - Configuring `supported_protocol_bindings` on A2AAgent | ||
| - The client selects a binding that matches the remote agent's capabilities | ||
| - Fallback behavior when preferred binding is unavailable | ||
|
|
||
| This is the A2A equivalent of the .NET A2AAgent_ProtocolSelection sample. | ||
|
|
||
| Prerequisites: | ||
| - Set A2A_AGENT_HOST to the URL of a running A2A server | ||
|
|
||
| To run this sample: | ||
| cd python/samples/02-agents/a2a | ||
| uv run python a2a_protocol_selection.py | ||
| """ | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| """Demonstrates configuring A2A protocol binding preferences.""" | ||
| a2a_agent_host = os.getenv("A2A_AGENT_HOST") | ||
| if not a2a_agent_host: | ||
| raise ValueError("A2A_AGENT_HOST environment variable is not set") | ||
|
|
||
| # 1. Resolve agent card to see what bindings are available. | ||
| async with httpx.AsyncClient(timeout=60.0) as http_client: | ||
| resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host) | ||
| agent_card = await resolver.get_agent_card() | ||
|
|
||
| print(f"Agent: {agent_card.name}") | ||
| print("Supported interfaces:") | ||
| for interface in agent_card.supported_interfaces: | ||
| print(f" - {interface.protocol_binding} @ {interface.url}") | ||
|
|
||
| # 2. Create agent with explicit protocol binding preference. | ||
| # The list is ordered by preference — the SDK will select the first | ||
| # binding that matches a supported interface on the agent card. | ||
| # | ||
| # This matters when a server exposes multiple interfaces (e.g. JSONRPC | ||
| # on / and HTTP+JSON on /api/). If only one binding is available, the | ||
| # client uses it regardless of your preference list. | ||
| async with A2AAgent( | ||
| name=agent_card.name, | ||
| agent_card=agent_card, | ||
| url=a2a_agent_host, | ||
| supported_protocol_bindings=["HTTP+JSON", "JSONRPC"], | ||
| ) as agent: | ||
| print("\nConfigured bindings: ['HTTP+JSON', 'JSONRPC']") | ||
| response = await agent.run("Tell me a short joke") | ||
| print(f"Response: {response.text}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) | ||
|
|
||
|
|
||
| """ | ||
| Sample output: | ||
|
|
||
| Agent: PolicyAgent | ||
| Supported interfaces: | ||
| - JSONRPC @ http://localhost:5001/ | ||
|
|
||
| Configured bindings: ['HTTP+JSON', 'JSONRPC'] | ||
| Response: Here's a short joke for you... | ||
| """ |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.