11import asyncio
22import logging
33
4- from collections .abc import AsyncGenerator
4+ from collections .abc import AsyncGenerator , Awaitable , Callable
55from typing import cast
66
77from a2a .server .agent_execution import (
3131 TaskStore ,
3232)
3333from a2a .types .a2a_pb2 import (
34+ AgentCard ,
3435 CancelTaskRequest ,
3536 DeleteTaskPushNotificationConfigRequest ,
37+ GetExtendedAgentCardRequest ,
3638 GetTaskPushNotificationConfigRequest ,
3739 GetTaskRequest ,
3840 ListTaskPushNotificationConfigsRequest ,
4749 TaskState ,
4850)
4951from a2a .utils .errors import (
52+ ExtendedAgentCardNotConfiguredError ,
5053 InternalError ,
5154 InvalidParamsError ,
55+ PushNotificationConfigNotFoundError ,
5256 PushNotificationNotSupportedError ,
5357 TaskNotCancelableError ,
5458 TaskNotFoundError ,
5559 UnsupportedOperationError ,
5660)
61+ from a2a .utils .helpers import maybe_await , validate
5762from a2a .utils .task import (
5863 apply_history_length ,
5964 validate_history_length ,
@@ -88,27 +93,43 @@ def __init__( # noqa: PLR0913
8893 self ,
8994 agent_executor : AgentExecutor ,
9095 task_store : TaskStore ,
96+ agent_card : AgentCard ,
9197 queue_manager : QueueManager | None = None ,
9298 push_config_store : PushNotificationConfigStore | None = None ,
9399 push_sender : PushNotificationSender | None = None ,
94100 request_context_builder : RequestContextBuilder | None = None ,
101+ extended_agent_card : AgentCard | None = None ,
102+ card_modifier : Callable [[AgentCard ], Awaitable [AgentCard ] | AgentCard ]
103+ | None = None ,
104+ extended_card_modifier : Callable [
105+ [AgentCard , ServerCallContext ], Awaitable [AgentCard ] | AgentCard
106+ ]
107+ | None = None ,
95108 ) -> None :
96109 """Initializes the DefaultRequestHandler.
97110
98111 Args:
99112 agent_executor: The `AgentExecutor` instance to run agent logic.
100113 task_store: The `TaskStore` instance to manage task persistence.
114+ agent_card: The AgentCard describing the agent's capabilities.
101115 queue_manager: The `QueueManager` instance to manage event queues. Defaults to `InMemoryQueueManager`.
102116 push_config_store: The `PushNotificationConfigStore` instance for managing push notification configurations. Defaults to None.
103117 push_sender: The `PushNotificationSender` instance for sending push notifications. Defaults to None.
104118 request_context_builder: The `RequestContextBuilder` instance used
105119 to build request contexts. Defaults to `SimpleRequestContextBuilder`.
120+ extended_agent_card: An optional, distinct AgentCard to be served at the authenticated extended card endpoint.
121+ card_modifier: An optional callback to dynamically modify the public agent card before it is served.
122+ extended_card_modifier: An optional callback to dynamically modify the extended agent card before it is served. It receives the call context.
106123 """
107124 self .agent_executor = agent_executor
108125 self .task_store = task_store
126+ self ._agent_card = agent_card
109127 self ._queue_manager = queue_manager or InMemoryQueueManager ()
110128 self ._push_config_store = push_config_store
111129 self ._push_sender = push_sender
130+ self .extended_agent_card = extended_agent_card
131+ self .card_modifier = card_modifier
132+ self .extended_card_modifier = extended_card_modifier
112133 self ._request_context_builder = (
113134 request_context_builder
114135 or SimpleRequestContextBuilder (
@@ -122,6 +143,11 @@ def __init__( # noqa: PLR0913
122143 # asyncio tasks and to surface unexpected exceptions.
123144 self ._background_tasks = set ()
124145
146+ @property
147+ def agent_card (self ) -> AgentCard :
148+ """The agent card to be served by default."""
149+ return self ._agent_card
150+
125151 @validate_request_params
126152 async def on_get_task (
127153 self ,
@@ -396,6 +422,10 @@ async def push_notification_callback(event: Event) -> None:
396422 return result
397423
398424 @validate_request_params
425+ @validate (
426+ lambda self : self .agent_card .capabilities .streaming ,
427+ 'Streaming is not supported by the agent' ,
428+ )
399429 async def on_message_send_stream (
400430 self ,
401431 params : SendMessageRequest ,
@@ -485,6 +515,14 @@ async def _cleanup_producer(
485515 self ._running_agents .pop (task_id , None )
486516
487517 @validate_request_params
518+ @validate (
519+ lambda self : (
520+ self .agent_card .capabilities .push_notifications
521+ and self ._push_config_store
522+ ),
523+ error_message = 'Push notifications are not supported by the agent' ,
524+ error_type = PushNotificationNotSupportedError ,
525+ )
488526 async def on_create_task_push_notification_config (
489527 self ,
490528 params : TaskPushNotificationConfig ,
@@ -494,15 +532,12 @@ async def on_create_task_push_notification_config(
494532
495533 Requires a `PushNotifier` to be configured.
496534 """
497- if not self ._push_config_store :
498- raise PushNotificationNotSupportedError
499-
500535 task_id = params .task_id
501536 task : Task | None = await self .task_store .get (task_id , context )
502537 if not task :
503538 raise TaskNotFoundError
504539
505- await self ._push_config_store .set_info (
540+ await self ._push_config_store .set_info ( # type: ignore[union-attr]
506541 task_id ,
507542 params ,
508543 context ,
@@ -511,6 +546,14 @@ async def on_create_task_push_notification_config(
511546 return params
512547
513548 @validate_request_params
549+ @validate (
550+ lambda self : (
551+ self .agent_card .capabilities .push_notifications
552+ and self ._push_config_store
553+ ),
554+ error_message = 'Push notifications are not supported by the agent' ,
555+ error_type = PushNotificationNotSupportedError ,
556+ )
514557 async def on_get_task_push_notification_config (
515558 self ,
516559 params : GetTaskPushNotificationConfigRequest ,
@@ -520,26 +563,27 @@ async def on_get_task_push_notification_config(
520563
521564 Requires a `PushConfigStore` to be configured.
522565 """
523- if not self ._push_config_store :
524- raise PushNotificationNotSupportedError
525-
526566 task_id = params .task_id
527567 config_id = params .id
528568 task : Task | None = await self .task_store .get (task_id , context )
529569 if not task :
530570 raise TaskNotFoundError
531571
532572 push_notification_configs : list [TaskPushNotificationConfig ] = (
533- await self ._push_config_store .get_info (task_id , context ) or []
573+ await self ._push_config_store .get_info (task_id , context ) or [] # type: ignore[union-attr]
534574 )
535575
536576 for config in push_notification_configs :
537577 if config .id == config_id :
538578 return config
539579
540- raise InternalError ( message = 'Push notification config not found' )
580+ raise PushNotificationConfigNotFoundError
541581
542582 @validate_request_params
583+ @validate (
584+ lambda self : self .agent_card .capabilities .streaming ,
585+ 'Streaming is not supported by the agent' ,
586+ )
543587 async def on_subscribe_to_task (
544588 self ,
545589 params : SubscribeToTaskRequest ,
@@ -583,6 +627,14 @@ async def on_subscribe_to_task(
583627 yield event
584628
585629 @validate_request_params
630+ @validate (
631+ lambda self : (
632+ self .agent_card .capabilities .push_notifications
633+ and self ._push_config_store
634+ ),
635+ error_message = 'Push notifications are not supported by the agent' ,
636+ error_type = PushNotificationNotSupportedError ,
637+ )
586638 async def on_list_task_push_notification_configs (
587639 self ,
588640 params : ListTaskPushNotificationConfigsRequest ,
@@ -592,15 +644,12 @@ async def on_list_task_push_notification_configs(
592644
593645 Requires a `PushConfigStore` to be configured.
594646 """
595- if not self ._push_config_store :
596- raise PushNotificationNotSupportedError
597-
598647 task_id = params .task_id
599648 task : Task | None = await self .task_store .get (task_id , context )
600649 if not task :
601650 raise TaskNotFoundError
602651
603- push_notification_config_list = await self ._push_config_store .get_info (
652+ push_notification_config_list = await self ._push_config_store .get_info ( # type: ignore[union-attr]
604653 task_id , context
605654 )
606655
@@ -609,6 +658,14 @@ async def on_list_task_push_notification_configs(
609658 )
610659
611660 @validate_request_params
661+ @validate (
662+ lambda self : (
663+ self .agent_card .capabilities .push_notifications
664+ and self ._push_config_store
665+ ),
666+ error_message = 'Push notifications are not supported by the agent' ,
667+ error_type = PushNotificationNotSupportedError ,
668+ )
612669 async def on_delete_task_push_notification_config (
613670 self ,
614671 params : DeleteTaskPushNotificationConfigRequest ,
@@ -618,13 +675,35 @@ async def on_delete_task_push_notification_config(
618675
619676 Requires a `PushConfigStore` to be configured.
620677 """
621- if not self ._push_config_store :
622- raise PushNotificationNotSupportedError
623-
624678 task_id = params .task_id
625679 config_id = params .id
626680 task : Task | None = await self .task_store .get (task_id , context )
627681 if not task :
628682 raise TaskNotFoundError
629683
630- await self ._push_config_store .delete_info (task_id , context , config_id )
684+ await self ._push_config_store .delete_info (task_id , context , config_id ) # type: ignore[union-attr]
685+
686+ @validate_request_params
687+ @validate (
688+ lambda self : self .agent_card .capabilities .extended_agent_card ,
689+ error_message = 'The agent does not have an extended agent card configured' ,
690+ error_type = ExtendedAgentCardNotConfiguredError ,
691+ )
692+ async def on_get_extended_agent_card (
693+ self ,
694+ params : GetExtendedAgentCardRequest ,
695+ context : ServerCallContext ,
696+ ) -> AgentCard :
697+ """Default handler for 'GetExtendedAgentCard'.
698+
699+ Requires `capabilities.extended_agent_card` to be true.
700+ """
701+ card = self .extended_agent_card or self .agent_card
702+
703+ if self .extended_card_modifier :
704+ return await maybe_await (self .extended_card_modifier (card , context ))
705+
706+ if self .card_modifier :
707+ return await maybe_await (self .card_modifier (card ))
708+
709+ return card
0 commit comments