feat(a2acompat): add HTTP+JSON REST support to a2av0 compat layer#280
feat(a2acompat): add HTTP+JSON REST support to a2av0 compat layer#280yarolegovich merged 8 commits intoa2aproject:mainfrom
Conversation
Adds NewRESTHandler (http.Handler) and NewRESTTransport (a2aclient.Transport) to a2acompat/a2av0, allowing v0.3 HTTP+JSON clients (e.g. Python ADK Agent Engine deployments) to communicate with v1.0 Go servers, and vice versa. The new REST handler serves the same URL routes as a2asrv.NewRESTHandler but accepts and returns v0.3-compatible JSON payloads, reusing all existing type converters from conversions.go. The client transport translates v1.0 client calls to v0.3 HTTP+JSON requests and converts the v0.3 responses back to v1.0 types. Fixes: a2aproject#269
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
The pull request introduces a new a2acompat/a2av0 package, providing a compatibility layer for A2A Go clients and servers to interact with v0.3 REST endpoints. This includes a RESTTransport for v1.0 clients to make v0.3 requests and a RESTHandler for v1.0 servers to process v0.3 requests, along with comprehensive unit tests. Review feedback primarily focuses on improving code style and API consistency: several instances in rest_compat_test.go should use the a2a.NewMessage constructor instead of direct struct literals to adhere to the repository's style guide, and in rest_server.go, the FromV1TaskPushConfigs function should ensure an empty slice is returned for JSON serialization consistency, preventing null responses for empty lists.
- Use a2a.NewMessage constructor in test mocks per style guide - Return empty slice instead of nil in handleListTaskPushConfigs to avoid null JSON serialization
|
JSON parsing is incorrect, 0.3 REST was using lower snake case. |
Thanks for initial review. Agree - json parsing def was using wrong case. On it |
v0.3 REST uses snake_case JSON keys (message_id, task_id, etc.) while the legacy Go types have camelCase JSON tags. Instead of duplicating the types, we add a post-processing JSON key transform layer: - marshalSnakeCase: marshal to JSON then convert all keys camelCase→snake_case - unmarshalSnakeCase: convert keys snake_case→camelCase then unmarshal Both the REST server and client now use these transforms for all JSON encoding/decoding, including SSE streaming events.
yarolegovich
left a comment
There was a problem hiding this comment.
sorry for delaying the review, a busy month
a2acompat/a2av0/rest_client.go
Outdated
| return nil, err | ||
| } | ||
| // Transform snake_case response to camelCase for legacy unmarshal. | ||
| camelData, err := transformJSONKeys(rawResult, snakeToCamel) |
There was a problem hiding this comment.
looks like we're doing the second pass here, it's already called in doRequest->unmarshalSnakeCase
There was a problem hiding this comment.
addressed in latest commit.
a2acompat/a2av0/rest_client.go
Outdated
| func NewRESTTransportFactory(cfg RESTTransportConfig) a2aclient.TransportFactory { | ||
| return a2aclient.TransportFactoryFn(func(ctx context.Context, card *a2a.AgentCard, iface *a2a.AgentInterface) (a2aclient.Transport, error) { | ||
| if cfg.URL == "" { | ||
| cfg.URL = iface.URL |
There was a problem hiding this comment.
I know this is copied from jsonrpc, but can see a bug here now. captured config gets mutated which might cause problems when transport factory is called multiple times
upd. fixed in main for jsonrpc
There was a problem hiding this comment.
I fixed in this part of code. Please advise if hoping for diff.
a2acompat/a2av0/rest_server.go
Outdated
| func writeSnakeCaseJSON(ctx context.Context, rw http.ResponseWriter, v any) { | ||
| data, err := marshalSnakeCase(v) | ||
| if err != nil { | ||
| log.Error(ctx, "failed to encode response", err) |
There was a problem hiding this comment.
we should probably writeRESTCompatError(ctx, rw, fmt.Errorf("failed to marshal: %w, err), a2a.TaskID(""))
- rest_client.go: fix double snake-to-camel transform in SendMessage by using sendRequest directly instead of doRequest (which already calls unmarshalSnakeCase) - rest_client.go: fix config mutation bug in NewRESTTransportFactory by copying cfg before mutating URL (matches jsonrpc fix on main) - rest_server.go: improve writeSnakeCaseJSON error handling to return a proper error response via writeRESTCompatError instead of only logging
The rest.Error struct uses unexported httpStatus field with an HTTPStatus() getter method. Updated writeRESTCompatError to use the correct accessor.
I def understand. No worries at all. Have been glad for some interesting things to dig into. Not just busywork :-) |
🤖 I have created a release *beep* *boop* --- ## [2.0.1](v2.0.0...v2.0.1) (2026-03-24) ### Features * **a2acompat:** add HTTP+JSON REST support to a2av0 compat layer ([#280](#280)) ([bac00e7](bac00e7)) * agent executor cleaner ([#276](#276)) ([9c95980](9c95980)) ### Bug Fixes * ensure correct concurrent cancellation results ([#287](#287)) ([d0f8981](d0f8981)), closes [#245](#245) ### Documentation * **a2aclient:** add Example_* test functions for pkg.go.dev documentation ([#263](#263)) ([f8dae97](f8dae97)) * update diagrams, docs and instructions ([#289](#289)) ([ed33c7a](ed33c7a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Summary
Adds
NewRESTHandlerandNewRESTTransporttoa2acompat/a2av0, completing the compat layer's support for the v0.3 HTTP+JSON protocol binding.Closes #269
Background
The
a2acompat/a2av0package already providesNewJSONRPCHandlerandNewJSONRPCTransportfor v0.3 JSON-RPC compatibility. However, Google's Agent Engine and Python ADK use the HTTP+JSON (REST) transport binding of the v0.3 spec, which was not covered. This PR adds the missing REST half.Changes
a2acompat/a2av0/rest_server.go(new)NewRESTHandler(handler a2asrv.RequestHandler, opts ...)— anhttp.Handlerthat exposes the same URL routes asa2asrv.NewRESTHandlerbut accepts/returns v0.3 JSON payloads.POST /message:send,POST /message:stream,GET /tasks/{id},GET /tasks,POST /tasks/{id}:cancel,POST /tasks/{id}:subscribe, and push notification config CRUD.conversions.gohelpers (ToV1*/FromV1*).StreamResponsewrapper).a2acompat/a2av0/rest_client.go(new)NewRESTTransport(cfg RESTTransportConfig)— ana2aclient.Transportthat calls a v0.3 HTTP+JSON server and converts responses to v1.0 types.NewRESTTransportFactory(cfg)— factory for use witha2aclientauto-discovery.jsonrpc_client.go.a2acompat/a2av0/rest_compat_test.go(new)Testing
Full suite (
go test ./...) passes with no regressions.