Skip to content

Python: Add A2A channel for agent-framework-hosting#6699

Open
eavanvalkenburg wants to merge 3 commits into
microsoft:mainfrom
eavanvalkenburg:eavan/python-hosting-a2a
Open

Python: Add A2A channel for agent-framework-hosting#6699
eavanvalkenburg wants to merge 3 commits into
microsoft:mainfrom
eavanvalkenburg:eavan/python-hosting-a2a

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation & Context

Adds Agent-to-Agent (A2A) protocol support for the agent-framework-hosting stack, allowing an AgentFrameworkHost to 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-a2a package — two key classes:

    • A2AChannel — mounts the A2A SDK's Starlette app onto the AgentFrameworkHost, registering the agent card and routing A2A task requests through the channel pipeline (run_hook, context providers, history providers)
    • HostAgentExecutor — A2A AgentExecutor implementation that bridges A2A task lifecycle (submit → working → completed/failed) to the AF hosting HostedRunResult
    • Multi-modal content: _contents_to_parts converts AF Content items (text, URI, data) to A2A Part objects
    • Streaming: A2A progress notifications dispatched while consuming ResponseStream; final response published via TaskUpdater on completion
    • Session continuity: A2A context_id and task_id used as isolation keys for the AF session
  • Test layout already uses tests/hosting_a2a/ with no __init__.py

Impact: New alpha package with no changes to existing packages. Adds agent-framework-hosting-a2a to 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

  • I have read the CONTRIBUTING guidelines.
  • My changes include tests.
  • My changes include documentation (README).
  • This is not a breaking change.

- 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>
Copilot AI review requested due to automatic review settings June 24, 2026 07:23
@moonbox3 moonbox3 added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/hosting-a2a/agent_framework_hosting_a2a
   _channel.py40197%88
   _executor.py1061783%56–57, 60, 67, 71, 124–127, 134, 154–158, 192, 214
TOTAL42294499888% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
8314 37 💤 0 ❌ 0 🔥 2m 6s ⏱️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-a2a package with A2AChannel (route contribution + agent card) and HostAgentExecutor (A2A task lifecycle → host run/run_stream).
  • Introduces unit/integration tests for the channel/executor and a small content conversion helper (Content → A2A Part).
  • 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.

Comment thread python/packages/hosting-a2a/agent_framework_hosting_a2a/_executor.py Outdated
Comment thread python/packages/hosting-a2a/pyproject.toml

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_a2a package. The code correctly validates required inputs (context_id, message), handles cancellation, and maps A2A lifecycle to the host pipeline. The str(exc) error reporting pattern matches the existing A2AExecutor convention.

✓ 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 cancel method, the CancelledError/Exception handling branches in execute, 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 without message_id are not coalesced into a single artifact.

Flagged Issues

  • _executor.py:159-165 only projects result.result.messages. For hosted Workflow targets the host returns HostedRunResult[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 when update.message_id is absent (it is optional per AgentResponseUpdate). The existing A2AExecutor already 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

@github-actions

Copy link
Copy Markdown
Contributor

Flagged issue

_executor.py:159-165 only projects result.result.messages. For hosted Workflow targets the host returns HostedRunResult[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.


Source: automated DevFlow PR review

eavanvalkenburg and others added 2 commits June 24, 2026 10:35
- 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>
@eavanvalkenburg eavanvalkenburg mentioned this pull request Jun 24, 2026
9 tasks
@eavanvalkenburg eavanvalkenburg self-assigned this Jun 24, 2026
@eavanvalkenburg eavanvalkenburg marked this pull request as ready for review June 24, 2026 10:49

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, the CancelledErrorTASK_STATE_CANCELED path, and the ExceptionTASK_STATE_FAILED path. 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 makes response_hook a 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-221 passes response_hook into ChannelContext.run_stream(...) and then discards the value from await stream.get_final_response(). The host only applies response_hook in _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()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions

Copy link
Copy Markdown
Contributor

Flagged issue

python/packages/hosting-a2a/agent_framework_hosting_a2a/_executor.py:204-221 passes response_hook into ChannelContext.run_stream(...) and then discards the value from await stream.get_final_response(). The host only applies response_hook in _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.


Source: automated DevFlow PR review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosting: A2A channel

3 participants