Skip to content

Commit 3296ae8

Browse files
committed
add GetExtendedCard method to compat request handler
1 parent 90a49c6 commit 3296ae8

13 files changed

Lines changed: 181 additions & 176 deletions

src/a2a/compat/v0_3/grpc_handler.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from a2a.server.request_handlers.request_handler import RequestHandler
3030
from a2a.types.a2a_pb2 import AgentCard
3131
from a2a.utils.errors import A2AError, InvalidParamsError
32-
from a2a.utils.helpers import maybe_await, validate
32+
from a2a.utils.helpers import maybe_await
3333

3434

3535
logger = logging.getLogger(__name__)
@@ -177,10 +177,6 @@ async def SendStreamingMessage(
177177
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
178178
"""Handles the 'SendStreamingMessage' gRPC method (v0.3)."""
179179

180-
@validate(
181-
lambda _: self.agent_card.capabilities.streaming,
182-
'Streaming is not supported by the agent',
183-
)
184180
async def _handler(
185181
server_context: ServerCallContext,
186182
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -240,10 +236,6 @@ async def TaskSubscription(
240236
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
241237
"""Handles the 'TaskSubscription' gRPC method (v0.3)."""
242238

243-
@validate(
244-
lambda _: self.agent_card.capabilities.streaming,
245-
'Streaming is not supported by the agent',
246-
)
247239
async def _handler(
248240
server_context: ServerCallContext,
249241
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -267,10 +259,6 @@ async def CreateTaskPushNotificationConfig(
267259
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:
268260
"""Handles the 'CreateTaskPushNotificationConfig' gRPC method (v0.3)."""
269261

270-
@validate(
271-
lambda _: self.agent_card.capabilities.push_notifications,
272-
'Push notifications are not supported by the agent',
273-
)
274262
async def _handler(
275263
server_context: ServerCallContext,
276264
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:
@@ -389,3 +377,23 @@ async def _handler(
389377
return empty_pb2.Empty()
390378

391379
return await self._handle_unary(context, _handler, empty_pb2.Empty())
380+
381+
async def GetExtendedCard(
382+
self,
383+
request: a2a_v0_3_pb2.GetAgentCardRequest,
384+
context: grpc.aio.ServicerContext,
385+
) -> a2a_v0_3_pb2.AgentCard:
386+
"""Get the authenticated extended agent card (v0.3)."""
387+
388+
async def _handler(
389+
server_context: ServerCallContext,
390+
) -> a2a_v0_3_pb2.AgentCard:
391+
req_v03 = types_v03.GetAuthenticatedExtendedCardRequest(id=0)
392+
res_v03 = await self.handler03.on_get_extended_agent_card(
393+
req_v03, server_context
394+
)
395+
return proto_utils.ToProto.agent_card(res_v03)
396+
397+
return await self._handle_unary(
398+
context, _handler, a2a_v0_3_pb2.AgentCard()
399+
)

src/a2a/compat/v0_3/jsonrpc_adapter.py

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22

3-
from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable
3+
from collections.abc import AsyncIterable, AsyncIterator
44
from typing import TYPE_CHECKING, Any
55

66
from sse_starlette.sse import EventSourceResponse
@@ -12,7 +12,6 @@
1212

1313
from a2a.server.request_handlers.request_handler import RequestHandler
1414
from a2a.server.routes import CallContextBuilder
15-
from a2a.types.a2a_pb2 import AgentCard
1615

1716
_package_starlette_installed = True
1817
else:
@@ -25,7 +24,6 @@
2524

2625
_package_starlette_installed = False
2726

28-
from a2a.compat.v0_3 import conversions
2927
from a2a.compat.v0_3 import types as types_v03
3028
from a2a.compat.v0_3.request_handler import RequestHandler03
3129
from a2a.server.context import ServerCallContext
@@ -39,8 +37,7 @@
3937
JSONRPCError as CoreJSONRPCError,
4038
)
4139
from a2a.utils import constants
42-
from a2a.utils.errors import ExtendedAgentCardNotConfiguredError
43-
from a2a.utils.helpers import maybe_await, validate_version
40+
from a2a.utils.helpers import validate_version
4441

4542

4643
logger = logging.getLogger(__name__)
@@ -62,19 +59,11 @@ class JSONRPC03Adapter:
6259
'agent/authenticatedExtendedCard': types_v03.GetAuthenticatedExtendedCardRequest,
6360
}
6461

65-
def __init__( # noqa: PLR0913
62+
def __init__(
6663
self,
67-
agent_card: 'AgentCard',
6864
http_handler: 'RequestHandler',
69-
extended_agent_card: 'AgentCard | None' = None,
7065
context_builder: 'CallContextBuilder | None' = None,
71-
card_modifier: 'Callable[[AgentCard], Awaitable[AgentCard] | AgentCard] | None' = None,
72-
extended_card_modifier: 'Callable[[AgentCard, ServerCallContext], Awaitable[AgentCard] | AgentCard] | None' = None,
7366
):
74-
self.agent_card = agent_card
75-
self.extended_agent_card = extended_agent_card
76-
self.card_modifier = card_modifier
77-
self.extended_card_modifier = extended_card_modifier
7867
self.handler = RequestHandler03(
7968
request_handler=http_handler,
8069
)
@@ -226,7 +215,7 @@ async def _process_non_streaming_request(
226215
)
227216
)
228217
elif method == 'agent/authenticatedExtendedCard':
229-
res_card = await self.get_authenticated_extended_card(
218+
res_card = await self.handler.on_get_extended_agent_card(
230219
request_obj, context
231220
)
232221
result = types_v03.GetAuthenticatedExtendedCardResponse(
@@ -243,31 +232,6 @@ async def _process_non_streaming_request(
243232
)
244233
)
245234

246-
async def get_authenticated_extended_card(
247-
self,
248-
request: types_v03.GetAuthenticatedExtendedCardRequest,
249-
context: ServerCallContext,
250-
) -> types_v03.AgentCard:
251-
"""Handles the 'agent/authenticatedExtendedCard' JSON-RPC method."""
252-
if not self.agent_card.capabilities.extended_agent_card:
253-
raise ExtendedAgentCardNotConfiguredError(
254-
message='Authenticated card not supported'
255-
)
256-
257-
base_card = self.extended_agent_card
258-
if base_card is None:
259-
base_card = self.agent_card
260-
261-
card_to_serve = base_card
262-
if self.extended_card_modifier and context:
263-
card_to_serve = await maybe_await(
264-
self.extended_card_modifier(base_card, context)
265-
)
266-
elif self.card_modifier:
267-
card_to_serve = await maybe_await(self.card_modifier(base_card))
268-
269-
return conversions.to_compat_agent_card(card_to_serve)
270-
271235
@validate_version(constants.PROTOCOL_VERSION_0_3)
272236
async def _process_streaming_request(
273237
self,

src/a2a/compat/v0_3/request_handler.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
from a2a.server.request_handlers.request_handler import RequestHandler
1010
from a2a.types.a2a_pb2 import Task
1111
from a2a.utils import proto_utils as core_proto_utils
12-
from a2a.utils.errors import (
13-
TaskNotFoundError,
14-
)
12+
from a2a.utils.errors import TaskNotFoundError
1513

1614

1715
logger = logging.getLogger(__name__)
@@ -170,3 +168,15 @@ async def on_delete_task_push_notification_config(
170168
await self.request_handler.on_delete_task_push_notification_config(
171169
v10_req, context
172170
)
171+
172+
async def on_get_extended_agent_card(
173+
self,
174+
request: types_v03.GetAuthenticatedExtendedCardRequest,
175+
context: ServerCallContext,
176+
) -> types_v03.AgentCard:
177+
"""Gets the authenticated extended agent card using v0.3 protocol types."""
178+
v10_req = conversions.to_core_get_extended_agent_card_request(request)
179+
v10_card = await self.request_handler.on_get_extended_agent_card(
180+
v10_req, context
181+
)
182+
return conversions.to_compat_agent_card(v10_card)

src/a2a/compat/v0_3/rest_adapter.py

Lines changed: 6 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from starlette.requests import Request
1212
from starlette.responses import JSONResponse, Response
1313

14+
from a2a.server.context import ServerCallContext
1415
from a2a.server.request_handlers.request_handler import RequestHandler
15-
from a2a.types.a2a_pb2 import AgentCard
1616

1717
_package_starlette_installed = True
1818
else:
@@ -31,19 +31,15 @@
3131
_package_starlette_installed = False
3232

3333

34-
from a2a.compat.v0_3 import conversions
3534
from a2a.compat.v0_3.rest_handler import REST03Handler
36-
from a2a.server.context import ServerCallContext
3735
from a2a.server.routes import CallContextBuilder, DefaultCallContextBuilder
3836
from a2a.utils.error_handlers import (
3937
rest_error_handler,
4038
rest_stream_error_handler,
4139
)
4240
from a2a.utils.errors import (
43-
ExtendedAgentCardNotConfiguredError,
4441
InvalidRequestError,
4542
)
46-
from a2a.utils.helpers import maybe_await
4743

4844

4945
logger = logging.getLogger(__name__)
@@ -55,22 +51,12 @@ class REST03Adapter:
5551
Defines v0.3 REST request processors and their routes, as well as managing response generation including Server-Sent Events (SSE).
5652
"""
5753

58-
def __init__( # noqa: PLR0913
54+
def __init__(
5955
self,
60-
agent_card: 'AgentCard',
6156
http_handler: 'RequestHandler',
62-
extended_agent_card: 'AgentCard | None' = None,
6357
context_builder: 'CallContextBuilder | None' = None,
64-
card_modifier: 'Callable[[AgentCard], Awaitable[AgentCard] | AgentCard] | None' = None,
65-
extended_card_modifier: 'Callable[[AgentCard, ServerCallContext], Awaitable[AgentCard] | AgentCard] | None' = None,
6658
):
67-
self.agent_card = agent_card
68-
self.extended_agent_card = extended_agent_card
69-
self.card_modifier = card_modifier
70-
self.extended_card_modifier = extended_card_modifier
71-
self.handler = REST03Handler(
72-
agent_card=agent_card, request_handler=http_handler
73-
)
59+
self.handler = REST03Handler(request_handler=http_handler)
7460
self._context_builder = context_builder or DefaultCallContextBuilder()
7561

7662
@rest_error_handler
@@ -108,39 +94,6 @@ async def event_generator(
10894
event_generator(method(request, call_context))
10995
)
11096

111-
async def handle_get_agent_card(
112-
self, request: Request, call_context: ServerCallContext
113-
) -> dict[str, Any]:
114-
"""Handles GET requests for the agent card endpoint."""
115-
card_to_serve = self.agent_card
116-
if self.card_modifier:
117-
card_to_serve = await maybe_await(self.card_modifier(card_to_serve))
118-
v03_card = conversions.to_compat_agent_card(card_to_serve)
119-
return v03_card.model_dump(mode='json', exclude_none=True)
120-
121-
async def handle_authenticated_agent_card(
122-
self, request: Request, call_context: ServerCallContext
123-
) -> dict[str, Any]:
124-
"""Hook for per credential agent card response."""
125-
if not self.agent_card.capabilities.extended_agent_card:
126-
raise ExtendedAgentCardNotConfiguredError(
127-
message='Authenticated card not supported'
128-
)
129-
card_to_serve = self.extended_agent_card
130-
131-
if not card_to_serve:
132-
card_to_serve = self.agent_card
133-
134-
if self.extended_card_modifier:
135-
card_to_serve = await maybe_await(
136-
self.extended_card_modifier(card_to_serve, call_context)
137-
)
138-
elif self.card_modifier:
139-
card_to_serve = await maybe_await(self.card_modifier(card_to_serve))
140-
141-
v03_card = conversions.to_compat_agent_card(card_to_serve)
142-
return v03_card.model_dump(mode='json', exclude_none=True)
143-
14497
def routes(self) -> dict[tuple[str, str], Callable[[Request], Any]]:
14598
"""Constructs a dictionary of API routes and their corresponding handlers."""
14699
routes: dict[tuple[str, str], Callable[[Request], Any]] = {
@@ -186,10 +139,9 @@ def routes(self) -> dict[tuple[str, str], Callable[[Request], Any]]:
186139
('/v1/tasks', 'GET'): functools.partial(
187140
self._handle_request, self.handler.list_tasks
188141
),
142+
('/v1/card', 'GET'): functools.partial(
143+
self._handle_request, self.handler.on_get_extended_agent_card
144+
),
189145
}
190-
if self.agent_card.capabilities.extended_agent_card:
191-
routes[('/v1/card', 'GET')] = functools.partial(
192-
self._handle_request, self.handle_authenticated_agent_card
193-
)
194146

195147
return routes

src/a2a/compat/v0_3/rest_handler.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from starlette.requests import Request
1111

1212
from a2a.server.request_handlers.request_handler import RequestHandler
13-
from a2a.types.a2a_pb2 import AgentCard
1413

1514
_package_starlette_installed = True
1615
else:
@@ -30,7 +29,6 @@
3029
from a2a.server.context import ServerCallContext
3130
from a2a.utils import constants
3231
from a2a.utils.helpers import (
33-
validate,
3432
validate_version,
3533
)
3634
from a2a.utils.telemetry import SpanKind, trace_class
@@ -45,16 +43,13 @@ class REST03Handler:
4543

4644
def __init__(
4745
self,
48-
agent_card: 'AgentCard',
4946
request_handler: 'RequestHandler',
5047
):
5148
"""Initializes the REST03Handler.
5249
5350
Args:
54-
agent_card: The AgentCard describing the agent's capabilities (v1.0).
5551
request_handler: The underlying `RequestHandler` instance to delegate requests to (v1.0).
5652
"""
57-
self.agent_card = agent_card
5853
self.handler03 = RequestHandler03(request_handler=request_handler)
5954

6055
@validate_version(constants.PROTOCOL_VERSION_0_3)
@@ -84,10 +79,6 @@ async def on_message_send(
8479
return MessageToDict(pb2_v03_resp)
8580

8681
@validate_version(constants.PROTOCOL_VERSION_0_3)
87-
@validate(
88-
lambda self: self.agent_card.capabilities.streaming,
89-
'Streaming is not supported by the agent',
90-
)
9182
async def on_message_send_stream(
9283
self,
9384
request: Request,
@@ -142,10 +133,6 @@ async def on_cancel_task(
142133
return MessageToDict(pb2_v03_task)
143134

144135
@validate_version(constants.PROTOCOL_VERSION_0_3)
145-
@validate(
146-
lambda self: self.agent_card.capabilities.streaming,
147-
'Streaming is not supported by the agent',
148-
)
149136
async def on_subscribe_to_task(
150137
self,
151138
request: Request,
@@ -208,10 +195,6 @@ async def get_push_notification(
208195
return MessageToDict(pb2_v03_config)
209196

210197
@validate_version(constants.PROTOCOL_VERSION_0_3)
211-
@validate(
212-
lambda self: self.agent_card.capabilities.push_notifications,
213-
'Push notifications are not supported by the agent',
214-
)
215198
async def set_push_notification(
216199
self,
217200
request: Request,
@@ -317,3 +300,16 @@ async def list_tasks(
317300
) -> dict[str, Any]:
318301
"""Handles the 'tasks/list' REST method."""
319302
raise NotImplementedError('list tasks not implemented')
303+
304+
@validate_version(constants.PROTOCOL_VERSION_0_3)
305+
async def on_get_extended_agent_card(
306+
self,
307+
request: Request,
308+
context: ServerCallContext,
309+
) -> dict[str, Any]:
310+
"""Handles the 'v1/agent/authenticatedExtendedAgentCard' REST method."""
311+
rpc_req = types_v03.GetAuthenticatedExtendedCardRequest(id=0)
312+
v03_resp = await self.handler03.on_get_extended_agent_card(
313+
rpc_req, context
314+
)
315+
return v03_resp.model_dump(mode='json', exclude_none=True)

0 commit comments

Comments
 (0)