Skip to content

Upgrade fasta2a to A2A v0.2.3 and Enable Dependency Injection #2103

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

physicsrob
Copy link

Summary

The goal of this PR is to bring pydantic-ai's A2A support up to the current protocol specification and makes it practically usable for real-world applications.

Key improvements:

  1. Protocol Compatibility: Upgrades from the deprecated A2A v0.1 to v0.2.3, enabling compatibility with the broader A2A ecosystem
  2. Dependency Injection: Adds deps_factory support to Agent.to_a2a(), allowing agents that use pydantic-ai's core dependency injection feature to work with A2A

Background

The initial A2A support in pydantic-ai implemented v0.1 of the protocol, which was superseded by the non-backwards-compatible v0.2 just days after the implementation landed. This unfortunate timing meant that pydantic-ai's A2A support was incompatible with the rest of the ecosystem from the start.

Additionally, the initial implementation only worked with agents that didn't use dependency injection - a significant limitation given that dependency injection is a core pydantic-ai feature used in most real-world applications.

What Changed

1. A2A Protocol v0.2.3 Upgrade

Before:

  • Implemented the now-obsolete A2A v0.1 protocol that effectively no other tools support
  • Used deprecated methods like tasks/send
  • Incompatible with official A2A tools and examples

After:

  • Implements the current A2A v0.2.3 specification with full streaming support
  • Uses modern protocol methods (message/send and message/stream)
  • Compatible with the official A2A ecosystem - tested with clients from a2a-samples

Key protocol changes:

  • Method names: tasks/sendmessage/send
  • Field rename: typekind in all message parts
  • Server-generated task IDs (more secure and standard-compliant)
  • Support for both Task and Message response types

2. Dependency Injection Support

Before:

  • to_a2a() only worked with agents that had no dependencies
  • No way to pass user context, database connections, or other runtime dependencies
  • Limited the practical use cases

After:

  • New deps_factory parameter enables full dependency injection support
  • Agents can access user context, authentication, and other dependencies from A2A task metadata

3. Streaming and Structured Output Support

New capabilities:

  • Incremental message streaming: Uses agent.iter() to stream partial responses as they're generated
  • Structured output handling: When agents return non-string outputs (Pydantic models, dataclasses, etc.), the framework creates proper A2A artifacts with:
    • DataPart containing the serialized data
    • JSON schema metadata for type information
    • Proper handling of complex types

Implementation Details

Protocol Upgrade

  • Updated schema types to match v0.2.3 specification
  • Modified client, worker, and application layers for new protocol
  • Maintained clean separation between protocol types and internal framework types
  • Chose clean implementation without backwards compatibility since v0.1 has effectively no adoption.

Dependency Injection

  • Added optional deps_factory parameter to Agent.to_a2a()
  • Factory receives the A2A Task and returns typed dependencies
  • Fully type-safe implementation maintaining pydantic-ai's guarantees
  • Backwards compatible - existing agents without dependencies continue to work

Testing

  • Comprehensive test suite for both protocol compliance and dependency injection
  • Added bank_support_a2a example demonstrating real-world usage with authentication
  • Cross-compatibility tested: Verified the example works with official A2A sample clients
  • All existing tests updated for new protocol

Usage Example

See the bank_support_a2a example for a complete demonstration of using deps_factory with A2A. This example shows how to:

  • Extract user authentication from A2A task metadata
  • Create typed dependencies for the agent
  • Implement a real-world customer support scenario with account access controls

Migration Guide

For the few early adopters who may have tried the v0.1 implementation:

  • Update client code to use message/send instead of tasks/send
  • Task IDs are now server-generated (don't pass them from client)
  • Part types use kind field instead of type

Related Issues

Fixes #1795 - Implements the core A2A v0.2.3 protocol methods message/send and message/stream to replace the deprecated tasks/send method from v0.1.

Related Links

Enable pydantic-ai agents that use dependency injection to work with
the A2A protocol by adding a deps_factory parameter to to_a2a().

Changes:
- Add deps_factory parameter to Agent.to_a2a() method
- Update AgentWorker to use deps_factory when creating dependencies
- Add comprehensive tests for the feature
- Add bank_support_a2a example showing real-world usage
- Update documentation with examples

The deps_factory receives the A2A Task object and returns dependencies
matching the agent's deps_type. This enables agents to get context
from task metadata (e.g., user authentication, database connections).
- Change Part types to use 'kind' field instead of 'type'
- Add new fields to Message type: message_id, context_id, task_id, kind
- Add MessageSendConfiguration and MessageSendParams types
- Replace tasks/send with message/send and message/stream methods
- Update JSON-RPC request/response types for new protocol
- Remove deprecated File-related types

This is a breaking change that updates fasta2a to comply with the
current A2A protocol specification, focusing on clean implementation
without backward compatibility.
- Replace tasks/send with message/send in routing
- Add send_message method to TaskManager
- Remove deprecated send_task method
- Add TODO for message/stream implementation
- Update imports for new message types

Minimal changes to support the new protocol while maintaining
the existing code style and architecture.
- Add TaskSendParams back as internal framework type
- Update AgentWorker to use 'kind' field instead of 'type'
- Update file part handling for new schema structure
- Maintain type safety in worker interface
- Preserve deps_factory functionality

TaskSendParams is now explicitly marked as an internal type for
broker/worker communication, separate from the A2A protocol.
- Replace send_task with send_message method
- Update to use MessageSendParams and MessageSendConfiguration
- Return Task  < /dev/null |  Message union type from send_message
- Remove client-generated task IDs (now server-generated)
- Update imports for new protocol types

The client now uses message/send instead of tasks/send and properly
handles the response which can be either a Task or Message.
- Add type guards (is_task/is_message) for handling Task  < /dev/null |  Message union types
- Update all tests to use new send_message API that returns SendMessageResponse
- Fix all test assertions to check for both 'error' and 'result' fields
- Update Part type access to check 'kind' field before accessing type-specific fields
- Fix GetTaskResponse handling where 'result' is NotRequired
- Clean up unused imports after protocol migration
- Update event docstrings to reference message/stream instead of legacy methods
- Add proper imports and type annotations for streaming support
- Fix task_manager to use properly typed TaskSendParams for broker calls
- Handle AgentWorker deps with conditional logic based on deps_factory presence
- Fix bank_support_a2a.py example with proper Task type import
- Add required context_id field to Task (spec line 89)
- Add required kind: 'task' field to Task (spec line 94)
- Update streaming events to use taskId instead of id (spec lines 175-176)
- Add contextId field to streaming events
- Add kind field to TaskArtifactUpdateEvent for event type identification
- Add InvalidAgentResponseError (-32006) error code
- Remove session_id throughout codebase (not in v0.2.3 spec)
- Replace all session_id usage with context_id
- Update type guard to check for new required Task fields
- Update all tests to expect new Task structure
- Message structure updates:
  - Made kind: 'message' field required (was NotRequired)
  - Added reference_task_ids array field
  - Added extensions array field
  - Update all Message creation to include required kind field

- Artifact structure updates:
  - Added required artifact_id field (spec line 123)
  - Removed index field (not in spec, was implementation detail)
  - Added extensions array field
  - Generate unique artifact_id using uuid

- FilePart structure updates:
  - Changed from flat structure to nested file: FileWithBytes  < /dev/null |  FileWithUri
  - Created FileWithBytes and FileWithUri types matching spec
  - Updated all FilePart usage to use nested structure
  - Updated _map_request_parts to handle nested file content

- Updated tests:
  - All Message instances now include kind: 'message'
  - All artifacts include artifact_id and no longer have index
  - All FilePart usage converted to nested structure
  - Fixed message history entries to include kind field
- Add append and last_chunk fields to TaskArtifactUpdateEvent for chunk-based streaming
- Implement StreamEvent union type with proper discriminator for broker communication
- Enhance broker with streaming capabilities and proper event routing
- Add streaming support to task manager with chunk handling
- Update storage layer to handle appending artifacts
- Extend application layer with streaming functionality
- Add comprehensive tests for streaming scenarios
  - Add proper exception logging in Worker._handle_task_operation to aid debugging
  - Fix pyright type errors in _a2a.py:
    - Properly extract text content from ModelRequest parts
    - Add type annotation for metadata dict
    - Fix current_message type inference
  - Rename Worker.build_message_history parameter from 'task_history' to 'history' for clarity
  - Remove exception re-raise in worker to allow graceful task failure without crashing the worker
Copy link
Contributor

hyperlint-ai bot commented Jul 1, 2025

PR Change Summary

Upgraded A2A support to version 0.2.3 and introduced dependency injection capabilities for enhanced usability in real-world applications.

  • Upgraded A2A protocol from v0.1 to v0.2.3 for improved compatibility with the A2A ecosystem.
  • Introduced deps_factory support in Agent.to_a2a() for dependency injection.
  • Enabled incremental message streaming and structured output handling.
  • Updated documentation with examples demonstrating new features.

Modified Files

  • docs/a2a.md

Added Files

  • docs/examples/bank-support-a2a.md

How can I customize these reviews?

Check out the Hyperlint AI Reviewer docs for more information on how to customize the review.

If you just want to ignore it on this PR, you can add the hyperlint-ignore label to the PR. Future changes won't trigger a Hyperlint review.

Note specifically for link checks, we only check the first 30 links in a file and we cache the results for several hours (for instance, if you just added a page, you might experience this). Our recommendation is to add hyperlint-ignore to the PR to ignore the link check for this PR.

@physicsrob
Copy link
Author

cc @Kludex

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.

A2A - "message/send" and "message/stream" RPC methods are not implemented and they seem to be core of A2A concept
2 participants