Skip to content

Commit 4d2589e

Browse files
committed
feat(compat): implement REST and JSONRPC server compatibility with 0.3 clients
1 parent 2e2d431 commit 4d2589e

20 files changed

+2146
-128
lines changed

src/a2a/compat/v0_3/grpc_handler.py

Lines changed: 37 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from a2a.compat.v0_3 import (
1919
types as types_v03,
2020
)
21+
from a2a.compat.v0_3.request_handler import RequestHandler03
2122
from a2a.extensions.common import HTTP_EXTENSION_HEADER
2223
from a2a.server.context import ServerCallContext
2324
from a2a.server.request_handlers.grpc_handler import (
@@ -26,9 +27,8 @@
2627
DefaultCallContextBuilder,
2728
)
2829
from a2a.server.request_handlers.request_handler import RequestHandler
29-
from a2a.types import a2a_pb2
3030
from a2a.types.a2a_pb2 import AgentCard
31-
from a2a.utils.errors import A2AError, InvalidParamsError, TaskNotFoundError
31+
from a2a.utils.errors import A2AError, InvalidParamsError
3232
from a2a.utils.helpers import maybe_await
3333

3434

@@ -60,7 +60,7 @@ def __init__(
6060
agent card before it is served.
6161
"""
6262
self.agent_card = agent_card
63-
self.request_handler = request_handler
63+
self.handler03 = RequestHandler03(request_handler=request_handler)
6464
self.context_builder = context_builder or DefaultCallContextBuilder()
6565
self.card_modifier = card_modifier
6666

@@ -113,38 +113,6 @@ def _extract_task_and_config_id(
113113
)
114114
return m.group(1), m.group(2)
115115

116-
def _event_to_v03_stream_response(
117-
self,
118-
event: a2a_pb2.Message
119-
| a2a_pb2.Task
120-
| a2a_pb2.TaskStatusUpdateEvent
121-
| a2a_pb2.TaskArtifactUpdateEvent,
122-
) -> a2a_v0_3_pb2.StreamResponse:
123-
"""Maps a core streaming event directly to a v0.3 StreamResponse."""
124-
if isinstance(event, a2a_pb2.Task):
125-
return a2a_v0_3_pb2.StreamResponse(
126-
task=proto_utils.ToProto.task(conversions.to_compat_task(event))
127-
)
128-
if isinstance(event, a2a_pb2.Message):
129-
return a2a_v0_3_pb2.StreamResponse(
130-
msg=proto_utils.ToProto.message(
131-
conversions.to_compat_message(event)
132-
)
133-
)
134-
if isinstance(event, a2a_pb2.TaskStatusUpdateEvent):
135-
return a2a_v0_3_pb2.StreamResponse(
136-
status_update=proto_utils.ToProto.task_status_update_event(
137-
conversions.to_compat_task_status_update_event(event)
138-
)
139-
)
140-
if isinstance(event, a2a_pb2.TaskArtifactUpdateEvent):
141-
return a2a_v0_3_pb2.StreamResponse(
142-
artifact_update=proto_utils.ToProto.task_artifact_update_event(
143-
conversions.to_compat_task_artifact_update_event(event)
144-
)
145-
)
146-
raise ValueError(f'Unknown event type: {type(event)}')
147-
148116
async def abort_context(
149117
self, error: A2AError, context: grpc.aio.ServicerContext
150118
) -> None:
@@ -187,20 +155,15 @@ async def _handler(
187155
req_v03 = types_v03.SendMessageRequest(
188156
id=0, params=proto_utils.FromProto.message_send_params(request)
189157
)
190-
req_v10 = conversions.to_core_send_message_request(req_v03)
191-
result = await self.request_handler.on_message_send(
192-
req_v10, server_context
158+
result = await self.handler03.on_message_send(
159+
req_v03, server_context
193160
)
194-
if isinstance(result, a2a_pb2.Task):
161+
if isinstance(result, types_v03.Task):
195162
return a2a_v0_3_pb2.SendMessageResponse(
196-
task=proto_utils.ToProto.task(
197-
conversions.to_compat_task(result)
198-
)
163+
task=proto_utils.ToProto.task(result)
199164
)
200165
return a2a_v0_3_pb2.SendMessageResponse(
201-
msg=proto_utils.ToProto.message(
202-
conversions.to_compat_message(result)
203-
)
166+
msg=proto_utils.ToProto.message(result)
204167
)
205168

206169
return await self._handle_unary(
@@ -220,11 +183,12 @@ async def _handler(
220183
req_v03 = types_v03.SendMessageRequest(
221184
id=0, params=proto_utils.FromProto.message_send_params(request)
222185
)
223-
req_v10 = conversions.to_core_send_message_request(req_v03)
224-
async for event in self.request_handler.on_message_send_stream(
225-
req_v10, server_context
186+
async for v03_stream_resp in self.handler03.on_message_send_stream(
187+
req_v03, server_context
226188
):
227-
yield self._event_to_v03_stream_response(event)
189+
yield proto_utils.ToProto.stream_response(
190+
v03_stream_resp.result
191+
)
228192

229193
async for item in self._handle_stream(context, _handler):
230194
yield item
@@ -242,13 +206,8 @@ async def _handler(
242206
req_v03 = types_v03.GetTaskRequest(
243207
id=0, params=proto_utils.FromProto.task_query_params(request)
244208
)
245-
req_v10 = conversions.to_core_get_task_request(req_v03)
246-
task = await self.request_handler.on_get_task(
247-
req_v10, server_context
248-
)
249-
if not task:
250-
raise TaskNotFoundError
251-
return proto_utils.ToProto.task(conversions.to_compat_task(task))
209+
task = await self.handler03.on_get_task(req_v03, server_context)
210+
return proto_utils.ToProto.task(task)
252211

253212
return await self._handle_unary(context, _handler, a2a_v0_3_pb2.Task())
254213

@@ -265,13 +224,8 @@ async def _handler(
265224
req_v03 = types_v03.CancelTaskRequest(
266225
id=0, params=proto_utils.FromProto.task_id_params(request)
267226
)
268-
req_v10 = conversions.to_core_cancel_task_request(req_v03)
269-
task = await self.request_handler.on_cancel_task(
270-
req_v10, server_context
271-
)
272-
if not task:
273-
raise TaskNotFoundError
274-
return proto_utils.ToProto.task(conversions.to_compat_task(task))
227+
task = await self.handler03.on_cancel_task(req_v03, server_context)
228+
return proto_utils.ToProto.task(task)
275229

276230
return await self._handle_unary(context, _handler, a2a_v0_3_pb2.Task())
277231

@@ -288,11 +242,12 @@ async def _handler(
288242
req_v03 = types_v03.TaskResubscriptionRequest(
289243
id=0, params=proto_utils.FromProto.task_id_params(request)
290244
)
291-
req_v10 = conversions.to_core_subscribe_to_task_request(req_v03)
292-
async for event in self.request_handler.on_subscribe_to_task(
293-
req_v10, server_context
245+
async for v03_stream_resp in self.handler03.on_subscribe_to_task(
246+
req_v03, server_context
294247
):
295-
yield self._event_to_v03_stream_response(event)
248+
yield proto_utils.ToProto.stream_response(
249+
v03_stream_resp.result
250+
)
296251

297252
async for item in self._handle_stream(context, _handler):
298253
yield item
@@ -313,15 +268,12 @@ async def _handler(
313268
request
314269
),
315270
)
316-
req_v10 = conversions.to_core_create_task_push_notification_config_request(
317-
req_v03
318-
)
319-
res_v10 = await self.request_handler.on_create_task_push_notification_config(
320-
req_v10, server_context
321-
)
322-
return proto_utils.ToProto.task_push_notification_config(
323-
conversions.to_compat_task_push_notification_config(res_v10)
271+
res_v03 = (
272+
await self.handler03.on_create_task_push_notification_config(
273+
req_v03, server_context
274+
)
324275
)
276+
return proto_utils.ToProto.task_push_notification_config(res_v03)
325277

326278
return await self._handle_unary(
327279
context, _handler, a2a_v0_3_pb2.TaskPushNotificationConfig()
@@ -344,19 +296,10 @@ async def _handler(
344296
id=task_id, push_notification_config_id=config_id
345297
),
346298
)
347-
req_v10 = (
348-
conversions.to_core_get_task_push_notification_config_request(
349-
req_v03
350-
)
351-
)
352-
res_v10 = (
353-
await self.request_handler.on_get_task_push_notification_config(
354-
req_v10, server_context
355-
)
356-
)
357-
return proto_utils.ToProto.task_push_notification_config(
358-
conversions.to_compat_task_push_notification_config(res_v10)
299+
res_v03 = await self.handler03.on_get_task_push_notification_config(
300+
req_v03, server_context
359301
)
302+
return proto_utils.ToProto.task_push_notification_config(res_v03)
360303

361304
return await self._handle_unary(
362305
context, _handler, a2a_v0_3_pb2.TaskPushNotificationConfig()
@@ -379,21 +322,16 @@ async def _handler(
379322
id=task_id
380323
),
381324
)
382-
req_v10 = (
383-
conversions.to_core_list_task_push_notification_config_request(
384-
req_v03
325+
res_v03 = (
326+
await self.handler03.on_list_task_push_notification_configs(
327+
req_v03, server_context
385328
)
386329
)
387-
res_v10 = await self.request_handler.on_list_task_push_notification_configs(
388-
req_v10, server_context
389-
)
390330

391331
return a2a_v0_3_pb2.ListTaskPushNotificationConfigResponse(
392332
configs=[
393-
proto_utils.ToProto.task_push_notification_config(
394-
conversions.to_compat_task_push_notification_config(c)
395-
)
396-
for c in res_v10.configs
333+
proto_utils.ToProto.task_push_notification_config(c)
334+
for c in res_v03
397335
]
398336
)
399337

@@ -433,11 +371,8 @@ async def _handler(
433371
id=task_id, push_notification_config_id=config_id
434372
),
435373
)
436-
req_v10 = conversions.to_core_delete_task_push_notification_config_request(
437-
req_v03
438-
)
439-
await self.request_handler.on_delete_task_push_notification_config(
440-
req_v10, server_context
374+
await self.handler03.on_delete_task_push_notification_config(
375+
req_v03, server_context
441376
)
442377
return empty_pb2.Empty()
443378

0 commit comments

Comments
 (0)