Python: Add A2A channel for agent-framework-hosting#6699
Python: Add A2A channel for agent-framework-hosting#6699eavanvalkenburg wants to merge 3 commits into
Conversation
- Add agent-framework-hosting-a2a package with A2AChannel exposing an AgentFrameworkHost as an A2A-compatible agent endpoint - HostAgentExecutor routes A2A task requests through the hosting channel pipeline (run_hook, context providers, history providers) - Multi-modal content support: text, URI, and data content items converted to A2A Parts via _contents_to_parts - Streaming: A2A progress notifications sent while consuming ResponseStream; TaskUpdater publishes final response on completion - Test layout uses tests/hosting_a2a/ (no tests/__init__.py) - Remove old [tool.mypy] section and mypy poe task; remove empty [dependency-groups] section; source type-checking via pyright - Update uv.lock, pyproject.toml workspace sources, and PACKAGE_STATUS.md Fixes microsoft#6591 Refs microsoft#6265 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a new Python hosting channel package, agent-framework-hosting-a2a, enabling an AgentFrameworkHost to expose its hosted target (agent/workflow) over the A2A (Agent-to-Agent) protocol by publishing an agent card and JSON-RPC routes and bridging A2A task execution through the hosting pipeline.
Changes:
- Adds the new
agent-framework-hosting-a2apackage withA2AChannel(route contribution + agent card) andHostAgentExecutor(A2A task lifecycle → host run/run_stream). - Introduces unit/integration tests for the channel/executor and a small content conversion helper (
Content→ A2APart). - Registers the new package in the Python workspace and package status list.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| python/pyproject.toml | Adds agent-framework-hosting-a2a to the workspace package list. |
| python/packages/hosting-a2a/agent_framework_hosting_a2a/init.py | Exposes the public package surface and version. |
| python/packages/hosting-a2a/agent_framework_hosting_a2a/_channel.py | Implements A2AChannel that contributes agent-card + JSON-RPC routes to the host. |
| python/packages/hosting-a2a/agent_framework_hosting_a2a/_executor.py | Implements HostAgentExecutor and AF Content → A2A Part conversion + streaming behavior. |
| python/packages/hosting-a2a/tests/hosting_a2a/test_channel.py | Adds tests covering card defaults, route contribution, executor behavior, and content conversion. |
| python/packages/hosting-a2a/pyproject.toml | Defines the new package metadata, dependencies, and tooling config. |
| python/packages/hosting-a2a/README.md | Documents usage and behavior (routes, card generation, session mapping, task store persistence). |
| python/packages/hosting-a2a/LICENSE | Adds MIT license file for the new package. |
| python/PACKAGE_STATUS.md | Registers the new package as alpha. |
There was a problem hiding this comment.
Automated Code Review
Reviewers: 5 | Confidence: 60%
✓ Correctness
The A2A channel implementation is well-structured and correct for its stated use cases. The bridging between A2A task lifecycle and AF hosting pipeline is properly implemented: context_id maps to session isolation, streaming publishes incremental artifacts, non-streaming publishes working-state messages, and errors/cancellations are handled. The _contents_to_parts conversion correctly handles text, URI, and data content types for standard media types. No high-severity correctness issues found.
✓ Security Reliability
The new A2A channel package is well-structured and follows established patterns from the existing
agent_framework_a2apackage. The code correctly validates required inputs (context_id, message), handles cancellation, and maps A2A lifecycle to the host pipeline. Thestr(exc)error reporting pattern matches the existingA2AExecutorconvention.
✓ Test Coverage
The test suite provides good coverage of the happy paths for both streaming and non-streaming execution, content-to-parts conversion, and agent card building. However, there are notable gaps in error-path coverage: the
cancelmethod, theCancelledError/Exceptionhandling branches inexecute, the missing-message validation guard, and edge cases in_contents_to_parts(invalid data URIs, unsupported content types) all lack tests.
✓ Failure Modes
No actionable issues found in this dimension.
✗ Design Approach
The overall shape matches the existing hosting and A2A abstractions, but there is one blocking design gap: the executor is documented as exposing hosted Workflows as A2A agents, yet its non-streaming projection only reads
.messages, so workflow outputs are dropped and the task completes with no reply content. I also found a smaller streaming design regression versus the existing A2A executor: chunks withoutmessage_idare not coalesced into a single artifact.
Flagged Issues
-
_executor.py:159-165only projectsresult.result.messages. For hosted Workflow targets the host returnsHostedRunResult[WorkflowRunResult], and the host contract explicitly says channels must project workflow outputs themselves. As written, an A2A call to a hosted Workflow completes without any response content.
Suggestions
- In
_run_stream, generate a stable fallback artifact ID whenupdate.message_idis absent (it is optional perAgentResponseUpdate). The existingA2AExecutoralready uses a generated default ID so clients can coalesce chunks into one progressive message; without it each chunk becomes a separate artifact.
Automated review by eavanvalkenburg's agents
|
Flagged issue
Source: automated DevFlow PR review |
- Fix HostAgentExecutor streaming artifact coalescing by using one stable per-task artifact id, even when update.message_id is missing or varies - Add streaming fallback to text updates when contents are empty - Fix workflow result projection in non-streaming mode by consuming get_outputs() values in addition to response.messages - Add _value_to_parts helper to convert workflow outputs and fallback values into A2A Parts - Expand tests to validate stable artifact ids and workflow output projection from HostedRunResult[WorkflowRunResult] - Harden A2A test fakes for strict typing (agent protocol shape, context/request casts, event queue override) - Add package-local test dependency group with uvicorn to make test execution self-contained Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add _strip_options_hook as the default run_hook for HostAgentExecutor so callers cannot inject model options without an explicit override - Replace getattr(update, 'contents', None) or [] with direct update.contents access; remove dead text-fallback branch - Wire ChannelRunHook type annotation on _run_hook field - Add test_default_hook_strips_options_when_no_run_hook_supplied - Add samples/04-hosting/af-hosting/local_a2a/ sample with: - app.py: WeatherAgent + A2AChannel + FileHistoryProvider + run_hook - call_client.py: A2AAgent client (non-streaming + streaming) - pyproject.toml and README.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Automated Code Review
Reviewers: 3 | Confidence: 66%
✓ Correctness
No actionable issues found in this dimension.
✓ Test Coverage
The test suite covers the happy paths well (non-streaming, streaming, workflow outputs, content conversion, default hook), but mises the error-handling branches of the A2A task lifecycle — specifically the
cancel()method, theCancelledError→TASK_STATE_CANCELEDpath, and theException→TASK_STATE_FAILEDpath. Given the PR rationale explicitly highlights 'A2A task lifecycle mapping (submit/working/completed/failed)' as a reviewer focus area, having no tests for the failure and cancellation states is a notable gap.
✗ Design Approach
The A2A channel mostly follows the hosting abstractions correctly, but the streaming executor currently drops the shaped final response returned by
run_stream(). That makesresponse_hooka no-op for streaming A2A calls even though the hook is explicitly threaded through the API and other channels consume the hooked final response.
Flagged Issues
-
python/packages/hosting-a2a/agent_framework_hosting_a2a/_executor.py:204-221passesresponse_hookintoChannelContext.run_stream(...)and then discards the value fromawait stream.get_final_response(). The host only appliesresponse_hookin_HostResponseStream.get_final_response()(python/packages/hosting/agent_framework_hosting/_host.py:376-386), so any streaming A2A request whose hook rewrites or synthesizes the final reply silently loses that output.
Automated review by eavanvalkenburg's agents
| append=True if appended else None, | ||
| ) | ||
| appended = True | ||
| await stream.get_final_response() |
There was a problem hiding this comment.
In streaming mode the response_hook is effectively ignored. ChannelContext.run_stream(..., response_hook=...) only applies that hook when get_final_response() is awaited and returned (python/packages/hosting/agent_framework_hosting/_host.py:376-386), but this method drops the shaped result after awaiting it. That means a hook that rewrites or adds the final assistant reply works for other channels (see python/packages/hosting-responses/agent_framework_hosting_responses/_channel.py:398-430) but never reaches A2A clients here.
|
Flagged issue
Source: automated DevFlow PR review |
Motivation & Context
Adds Agent-to-Agent (A2A) protocol support for the
agent-framework-hostingstack, allowing anAgentFrameworkHostto be exposed as an A2A-compatible agent endpoint. This enables agents built with Agent Framework to participate in multi-agent A2A workflows as callable service agents.This is part of the channel-by-channel split of the hosting work tracked in #6265.
Description & Review Guide
Major changes:
agent-framework-hosting-a2apackage — two key classes:A2AChannel— mounts the A2A SDK's Starlette app onto theAgentFrameworkHost, registering the agent card and routing A2A task requests through the channel pipeline (run_hook, context providers, history providers)HostAgentExecutor— A2AAgentExecutorimplementation that bridges A2A task lifecycle (submit → working → completed/failed) to the AF hostingHostedRunResult_contents_to_partsconverts AFContentitems (text, URI, data) to A2APartobjectsResponseStream; final response published viaTaskUpdateron completionTest layout already uses
tests/hosting_a2a/with no__init__.pyImpact: New alpha package with no changes to existing packages. Adds
agent-framework-hosting-a2ato the workspace and lockfile.Reviewer focus: A2A task lifecycle mapping (submit/working/completed/failed) in
HostAgentExecutor, content-to-parts conversion completeness, session isolation key choice.Related Issue
Fixes #6591
Refs #6265
Contribution Checklist