Skip to content

refactor: Migrate to the latest A2A proto schema#129

Merged
edenreich merged 11 commits intomainfrom
refactor/migrate-to-the-latest-proto-schema
Dec 30, 2025
Merged

refactor: Migrate to the latest A2A proto schema#129
edenreich merged 11 commits intomainfrom
refactor/migrate-to-the-latest-proto-schema

Conversation

@edenreich
Copy link
Copy Markdown
Contributor

@edenreich edenreich commented Dec 15, 2025

Overview

This PR completes the migration of the Agent Development Kit (ADK) to the A2A (Agent-to-Agent) Protocol Schema v0.3.0. The migration addresses breaking changes in the protocol specification, including the new Part type system, typed role constants, and state-based task management.

Breaking Changes

1. Part Type System Migration

Before (Interface-based discriminated union):

message := types.Message{
    Role: "user",
    Parts: []types.Part{
        types.TextPart{
            Kind: "text",
            Text: "Hello",
        },
    },
}

// Access via type assertion
if textPart, ok := part.(types.TextPart); ok {
    fmt.Println(textPart.Text)
}

After (Struct with optional fields and helper functions):

message := types.Message{
    Role: types.RoleUser,
    Parts: []types.Part{
        types.CreateTextPart("Hello"),
    },
}

// Access via field check
if part.Text != nil {
    fmt.Println(*part.Text)
}

2. Role Type System

Before (String literals):

Role: "user"
Role: "assistant"

After (Typed constants):

Role: types.RoleUser        // "ROLE_USER"
Role: types.RoleAgent       // "ROLE_AGENT"
Role: types.RoleUnspecified // "ROLE_UNSPECIFIED"

Note: A2A protocol has NO ROLE_SYSTEM - system prompts are agent configuration, not messages.

3. Input-Required State Management

Before (Checking non-existent Kind field):

if msg.Kind == "input_required" {  // ❌ Field doesn't exist!
    // handle input required
}

After (State-based checking):

if task.Status.State == types.TaskStateInputRequired {
    // Task is paused waiting for user input
    // Check task.Status.Message for the prompt sent to user
}

4. Message Converter Role Mapping

Established proper bidirectional mapping between A2A protocol (typed roles) and OpenAI SDK (string roles):

A2A → SDK:

  • types.RoleUsersdk.User
  • types.RoleAgentsdk.Assistant (or sdk.Tool if tool_call_id present)
  • types.RoleUnspecifiedsdk.User

SDK → A2A:

  • "user"types.RoleUser
  • "assistant", "tool", "system"types.RoleAgent

Files Changed

Core SDK Files

  • server/utils/message_converter.go: Updated role mapping between A2A and OpenAI SDK formats, removed legacy string literal support

Example Files (30+ files updated)

AI-Powered Examples:

  • examples/ai-powered/client/main.go
  • examples/ai-powered/server/main.go
  • examples/ai-powered-streaming/client/main.go
  • examples/ai-powered-streaming/server/main.go

Input-Required Examples:

  • examples/input-required/non-streaming/client/main.go
  • examples/input-required/non-streaming/server/main.go
  • examples/input-required/streaming/client/main.go
  • examples/input-required/streaming/server/main.go

Artifact Examples:

  • examples/artifacts-autonomous-tool/server/main.go
  • examples/artifacts-filesystem/server/main.go
  • examples/artifacts-minio/server/main.go
  • examples/artifacts-with-default-handlers/server/main.go

Other Examples:

  • examples/callbacks/server/main.go
  • examples/default-handlers/server/main.go
  • examples/minimal/server/main.go
  • examples/queue-storage/in-memory/server/main.go
  • examples/queue-storage/redis/server/main.go
  • examples/static-agent-card/server/main.go
  • examples/streaming/client/main.go
  • examples/streaming/server/main.go
  • examples/tls-server/server/main.go

Configuration:

  • .markdownlint-cli2.jsonc: Updated line length from 80 to 140 characters

Key Fixes

1. Part Construction

  • Replaced manual struct construction with helper functions:
    • types.CreateTextPart(text)
    • types.CreateDataPart(data, mimeType)
    • types.CreateFilePart(file)

2. Part Access Patterns

  • Changed from type assertions to field presence checks:
    • if part.Text != nil { *part.Text }
    • if part.File != nil { part.File.Name }
    • if part.Data != nil { part.Data.MimeType }

3. Role Usage

  • Enforced typed role constants throughout all examples
  • Maintained OpenAI SDK compatibility in internal converter

4. Input-Required Flow

  • Fixed fundamental misunderstanding in input-required examples:
    • Input-required is a task state, not a message property
    • Context determined by examining task.Status.Message
    • Removed fragile text pattern matching

5. Streaming Event Processing

  • Fixed event discrimination to use field presence:
    • Check task.Status.Message != nil instead of task.Kind == "task"
    • Check statusUpdate.TaskID != "" instead of statusUpdate.Kind != "status-update"

Testing

All verification steps completed successfully:

Code Quality:

task lint           # 0 issues found
task lint:examples  # 0 issues found

Tests:

task test  # All tests passing

Examples:

  • All 30+ example applications build successfully
  • Docker builds verified for all examples
  • Integration tests passing

Migration Guide

For users upgrading to A2A v0.3.0:

  1. Update Part construction:

    // Old
    types.TextPart{Kind: "text", Text: "message"}
    
    // New
    types.CreateTextPart("message")
  2. Update Role usage:

    // Old
    Role: "user"
    Role: "assistant"
    
    // New
    Role: types.RoleUser
    Role: types.RoleAgent
  3. Update Part access:

    // Old
    if textPart, ok := part.(types.TextPart); ok {
        text := textPart.Text
    }
    
    // New
    if part.Text != nil {
        text := *part.Text
    }
  4. Update input-required handling:

    // Old
    if msg.Kind == "input_required" { ... }
    
    // New
    if task.Status.State == types.TaskStateInputRequired {
        prompt := getMessageText(task.Status.Message)
    }

TODOs

  • Regression - ensure examples are still working as expected
  • Docs - ensure documentation is up to date

Signed-off-by: Eden Reich <eden.reich@gmail.com>
…ypes to types.go

Those types are deprecated due to latest A2A project schema, so I'm keeping them to avoid breaking changes and I'll be using the generated types gradually.

Signed-off-by: Eden Reich <eden.reich@gmail.com>
@edenreich edenreich changed the title refactor/migrate to the latest proto schema refactor: Migrate to the latest proto schema Dec 15, 2025
@edenreich edenreich marked this pull request as draft December 15, 2025 23:52
@edenreich edenreich changed the title refactor: Migrate to the latest proto schema refactor: Migrate to the latest A2A proto schema Dec 15, 2025
edenreich and others added 6 commits December 16, 2025 12:48
Signed-off-by: Eden Reich <eden.reich@gmail.com>
…ial)

- Fix agent_streamable_test.go to use message ID prefix check instead of Kind field
- Replace map[string]any Parts with types.CreateTextPart in some tests
- Fix AgentCard.URL field to use *string (pointer) type
- Add helper functions for creating pointers (stringPtr, boolPtr)
- Fix TaskState type conversions to use string() casting
- Create shared test_helpers_test.go for common test utilities

Co-authored-by: Eden Reich <edenreich@users.noreply.github.com>
- Replace all map[string]any Parts with types.CreateTextPart
- Fix all TaskState assignments to use string() casting
- Fix Part type assertions to use struct field access instead of map

Co-authored-by: Eden Reich <edenreich@users.noreply.github.com>
- Convert all TaskState enum assignments to use string() casting
- Fix TaskState comparisons in assertions

Co-authored-by: Eden Reich <edenreich@users.noreply.github.com>
Signed-off-by: Eden Reich <eden.reich@gmail.com>
@edenreich edenreich marked this pull request as ready for review December 16, 2025 17:07
@inference-gateway inference-gateway deleted a comment from claude bot Dec 16, 2025
@inference-gateway inference-gateway deleted a comment from claude bot Dec 16, 2025
@edenreich
Copy link
Copy Markdown
Contributor Author

edenreich commented Dec 18, 2025

I think those are way too many changes for review and the changes are not breaking too much, they actually consolidate the implementation, so I'll just merge and worst case will fix whatever requires a fix.

I've went through all the examples and everything seem to work as expected.

Future changes would be more easy to sync directly from A2A project proto file.

@edenreich edenreich merged commit 02c2bc1 into main Dec 30, 2025
1 of 2 checks passed
@edenreich edenreich deleted the refactor/migrate-to-the-latest-proto-schema branch December 30, 2025 19:36
ig-semantic-release-bot bot added a commit that referenced this pull request Dec 30, 2025
## [0.17.0](v0.16.2...v0.17.0) (2025-12-30)

### ✨ Features

* **metadata:** Add token usage and execution metrics to A2A task responses ([#127](#127)) ([1f49f97](1f49f97)), closes [#126](#126)

### ♻️ Improvements

* Migrate to the latest A2A proto schema ([#129](#129)) ([02c2bc1](02c2bc1))

### 👷 CI

* Bump generator tool to 0.2.2 ([fc58ab9](fc58ab9))

### 🔧 Miscellaneous

* **deps:** Bump claude-code 2.0.65 to 2.0.76 ([47f0a79](47f0a79))
* **deps:** Bump generator tool ([659aec4](659aec4))
* **deps:** Update dependencies and infer configuration ([#128](#128)) ([0fa9d33](0fa9d33))
* **deps:** Update infer config ([76589af](76589af))
@ig-semantic-release-bot
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.17.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants