Skip to content

Commit 0f010ba

Browse files
authored
Merge branch '1.0-dev' into ishymko/client-factory-refactor
2 parents cecb28d + cc094aa commit 0f010ba

44 files changed

Lines changed: 1216 additions & 883 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

itk/main.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,11 @@ async def main_async(http_port: int, grpc_port: int) -> None:
289289
name='ITK v10 Agent',
290290
description='Python agent using SDK 1.0.',
291291
version='1.0.0',
292-
capabilities=AgentCapabilities(streaming=True),
292+
capabilities=AgentCapabilities(
293+
streaming=True,
294+
push_notifications=True,
295+
extended_agent_card=True,
296+
),
293297
default_input_modes=['text/plain'],
294298
default_output_modes=['text/plain'],
295299
supported_interfaces=interfaces,
@@ -299,18 +303,25 @@ async def main_async(http_port: int, grpc_port: int) -> None:
299303
handler = DefaultRequestHandler(
300304
agent_executor=V10AgentExecutor(),
301305
task_store=task_store,
306+
agent_card=agent_card,
302307
queue_manager=InMemoryQueueManager(),
303308
)
304309

310+
handler_extended = DefaultRequestHandler(
311+
agent_executor=V10AgentExecutor(),
312+
task_store=task_store,
313+
agent_card=agent_card,
314+
queue_manager=InMemoryQueueManager(),
315+
extended_agent_card=agent_card,
316+
)
317+
305318
app = FastAPI()
306319

307320
agent_card_routes = create_agent_card_routes(
308321
agent_card=agent_card, card_url='/.well-known/agent-card.json'
309322
)
310323
jsonrpc_routes = create_jsonrpc_routes(
311-
agent_card=agent_card,
312-
request_handler=handler,
313-
extended_agent_card=agent_card,
324+
request_handler=handler_extended,
314325
rpc_url='/',
315326
enable_v0_3_compat=True,
316327
)
@@ -320,17 +331,16 @@ async def main_async(http_port: int, grpc_port: int) -> None:
320331
)
321332

322333
rest_routes = create_rest_routes(
323-
agent_card=agent_card,
324334
request_handler=handler,
325335
enable_v0_3_compat=True,
326336
)
327337
app.mount('/rest', FastAPI(routes=rest_routes + agent_card_routes))
328338

329339
server = grpc.aio.server()
330340

331-
compat_servicer = CompatGrpcHandler(agent_card, handler)
341+
compat_servicer = CompatGrpcHandler(handler)
332342
a2a_v0_3_pb2_grpc.add_A2AServiceServicer_to_server(compat_servicer, server)
333-
servicer = GrpcHandler(agent_card, handler)
343+
servicer = GrpcHandler(handler)
334344
a2a_pb2_grpc.add_A2AServiceServicer_to_server(servicer, server)
335345

336346
server.add_insecure_port(f'127.0.0.1:{grpc_port}')

samples/hello_world_agent.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,17 @@ async def serve(
191191

192192
task_store = InMemoryTaskStore()
193193
request_handler = DefaultRequestHandler(
194-
agent_executor=SampleAgentExecutor(), task_store=task_store
194+
agent_executor=SampleAgentExecutor(),
195+
task_store=task_store,
196+
agent_card=agent_card,
195197
)
196198

197199
rest_routes = create_rest_routes(
198-
agent_card=agent_card,
199200
request_handler=request_handler,
200201
path_prefix='/a2a/rest',
201202
enable_v0_3_compat=True,
202203
)
203204
jsonrpc_routes = create_jsonrpc_routes(
204-
agent_card=agent_card,
205205
request_handler=request_handler,
206206
rpc_url='/a2a/jsonrpc',
207207
enable_v0_3_compat=True,
@@ -216,12 +216,12 @@ async def serve(
216216

217217
grpc_server = grpc.aio.server()
218218
grpc_server.add_insecure_port(f'{host}:{grpc_port}')
219-
servicer = GrpcHandler(agent_card, request_handler)
219+
servicer = GrpcHandler(request_handler)
220220
a2a_pb2_grpc.add_A2AServiceServicer_to_server(servicer, grpc_server)
221221

222222
compat_grpc_server = grpc.aio.server()
223223
compat_grpc_server.add_insecure_port(f'{host}:{compat_grpc_port}')
224-
compat_servicer = CompatGrpcHandler(agent_card, request_handler)
224+
compat_servicer = CompatGrpcHandler(request_handler)
225225
a2a_v0_3_pb2_grpc.add_A2AServiceServicer_to_server(
226226
compat_servicer, compat_grpc_server
227227
)

src/a2a/compat/v0_3/grpc_handler.py

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from a2a.compat.v0_3 import (
1313
a2a_v0_3_pb2,
1414
a2a_v0_3_pb2_grpc,
15-
conversions,
1615
proto_utils,
1716
)
1817
from a2a.compat.v0_3 import (
@@ -27,9 +26,7 @@
2726
GrpcServerCallContextBuilder,
2827
)
2928
from a2a.server.request_handlers.request_handler import RequestHandler
30-
from a2a.types.a2a_pb2 import AgentCard
3129
from a2a.utils.errors import A2AError, InvalidParamsError
32-
from a2a.utils.helpers import maybe_await, validate
3330

3431

3532
logger = logging.getLogger(__name__)
@@ -42,29 +39,21 @@ class CompatGrpcHandler(a2a_v0_3_pb2_grpc.A2AServiceServicer):
4239

4340
def __init__(
4441
self,
45-
agent_card: AgentCard,
4642
request_handler: RequestHandler,
4743
context_builder: GrpcServerCallContextBuilder | None = None,
48-
card_modifier: Callable[[AgentCard], Awaitable[AgentCard] | AgentCard]
49-
| None = None,
5044
):
5145
"""Initializes the CompatGrpcHandler.
5246
5347
Args:
54-
agent_card: The AgentCard describing the agent's capabilities (v1.0).
5548
request_handler: The underlying `RequestHandler` instance to
5649
delegate requests to.
5750
context_builder: The CallContextBuilder object. If none the
5851
DefaultCallContextBuilder is used.
59-
card_modifier: An optional callback to dynamically modify the public
60-
agent card before it is served.
6152
"""
62-
self.agent_card = agent_card
6353
self.handler03 = RequestHandler03(request_handler=request_handler)
6454
self._context_builder = (
6555
context_builder or DefaultGrpcServerCallContextBuilder()
6656
)
67-
self.card_modifier = card_modifier
6857

6958
async def _handle_unary(
7059
self,
@@ -179,10 +168,6 @@ async def SendStreamingMessage(
179168
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
180169
"""Handles the 'SendStreamingMessage' gRPC method (v0.3)."""
181170

182-
@validate(
183-
lambda _: self.agent_card.capabilities.streaming,
184-
'Streaming is not supported by the agent',
185-
)
186171
async def _handler(
187172
server_context: ServerCallContext,
188173
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -242,10 +227,6 @@ async def TaskSubscription(
242227
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
243228
"""Handles the 'TaskSubscription' gRPC method (v0.3)."""
244229

245-
@validate(
246-
lambda _: self.agent_card.capabilities.streaming,
247-
'Streaming is not supported by the agent',
248-
)
249230
async def _handler(
250231
server_context: ServerCallContext,
251232
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -269,10 +250,6 @@ async def CreateTaskPushNotificationConfig(
269250
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:
270251
"""Handles the 'CreateTaskPushNotificationConfig' gRPC method (v0.3)."""
271252

272-
@validate(
273-
lambda _: self.agent_card.capabilities.push_notifications,
274-
'Push notifications are not supported by the agent',
275-
)
276253
async def _handler(
277254
server_context: ServerCallContext,
278255
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:
@@ -360,12 +337,19 @@ async def GetAgentCard(
360337
request: a2a_v0_3_pb2.GetAgentCardRequest,
361338
context: grpc.aio.ServicerContext,
362339
) -> a2a_v0_3_pb2.AgentCard:
363-
"""Get the agent card for the agent served (v0.3)."""
364-
card_to_serve = self.agent_card
365-
if self.card_modifier:
366-
card_to_serve = await maybe_await(self.card_modifier(card_to_serve))
367-
return proto_utils.ToProto.agent_card(
368-
conversions.to_compat_agent_card(card_to_serve)
340+
"""Get the extended agent card for the agent served (v0.3)."""
341+
342+
async def _handler(
343+
server_context: ServerCallContext,
344+
) -> a2a_v0_3_pb2.AgentCard:
345+
req_v03 = types_v03.GetAuthenticatedExtendedCardRequest(id=0)
346+
res_v03 = await self.handler03.on_get_extended_agent_card(
347+
req_v03, server_context
348+
)
349+
return proto_utils.ToProto.agent_card(res_v03)
350+
351+
return await self._handle_unary(
352+
context, _handler, a2a_v0_3_pb2.AgentCard()
369353
)
370354

371355
async def DeleteTaskPushNotificationConfig(

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
@@ -11,7 +11,6 @@
1111
from starlette.requests import Request
1212

1313
from a2a.server.request_handlers.request_handler import RequestHandler
14-
from a2a.types.a2a_pb2 import AgentCard
1514

1615
_package_starlette_installed = True
1716
else:
@@ -24,7 +23,6 @@
2423

2524
_package_starlette_installed = False
2625

27-
from a2a.compat.v0_3 import conversions
2826
from a2a.compat.v0_3 import types as types_v03
2927
from a2a.compat.v0_3.request_handler import RequestHandler03
3028
from a2a.server.context import ServerCallContext
@@ -42,8 +40,7 @@
4240
ServerCallContextBuilder,
4341
)
4442
from a2a.utils import constants
45-
from a2a.utils.errors import ExtendedAgentCardNotConfiguredError
46-
from a2a.utils.helpers import maybe_await, validate_version
43+
from a2a.utils.helpers import validate_version
4744

4845

4946
logger = logging.getLogger(__name__)
@@ -65,19 +62,11 @@ class JSONRPC03Adapter:
6562
'agent/getAuthenticatedExtendedCard': types_v03.GetAuthenticatedExtendedCardRequest,
6663
}
6764

68-
def __init__( # noqa: PLR0913
65+
def __init__(
6966
self,
70-
agent_card: 'AgentCard',
7167
http_handler: 'RequestHandler',
72-
extended_agent_card: 'AgentCard | None' = None,
7368
context_builder: 'ServerCallContextBuilder | None' = None,
74-
card_modifier: 'Callable[[AgentCard], Awaitable[AgentCard] | AgentCard] | None' = None,
75-
extended_card_modifier: 'Callable[[AgentCard, ServerCallContext], Awaitable[AgentCard] | AgentCard] | None' = None,
7669
):
77-
self.agent_card = agent_card
78-
self.extended_agent_card = extended_agent_card
79-
self.card_modifier = card_modifier
80-
self.extended_card_modifier = extended_card_modifier
8170
self.handler = RequestHandler03(
8271
request_handler=http_handler,
8372
)
@@ -227,7 +216,7 @@ async def _process_non_streaming_request(
227216
)
228217
)
229218
elif method == 'agent/getAuthenticatedExtendedCard':
230-
res_card = await self.get_authenticated_extended_card(
219+
res_card = await self.handler.on_get_extended_agent_card(
231220
request_obj, context
232221
)
233222
result = types_v03.GetAuthenticatedExtendedCardResponse(
@@ -244,31 +233,6 @@ async def _process_non_streaming_request(
244233
)
245234
)
246235

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

0 commit comments

Comments
 (0)