@@ -151,6 +151,188 @@ async def cancel(
151151 )
152152
153153
154+ def to_a2a (
155+ agent_engine_app : object ,
156+ agent_card : "AgentCard" ,
157+ user_id : str = None ,
158+ task_store_builder : Callable [..., "TaskStore" ] = None ,
159+ task_store_kwargs : Optional [Mapping [str , Any ]] = None ,
160+ agent_executor_kwargs : Optional [Mapping [str , Any ]] = None ,
161+ agent_executor_builder : Optional [Callable [..., "AgentExecutor" ]] = None ,
162+ request_handler_kwargs : Optional [Mapping [str , Any ]] = None ,
163+ request_handler_builder : Optional [Callable [..., "RequestHandler" ]] = None ,
164+ extended_agent_card : "AgentCard" = None ,
165+ ):
166+ """Converts an existing Agent Engine application to be compatible with A2A.
167+
168+ This function wraps an `agent_engine_app` with A2A functionalities, allowing it
169+ to handle A2A protocol requests. It augments the app's setup and operation
170+ registration to include A2A-specific handlers.
171+
172+ Args:
173+ agent_engine_app (object): The Agent Engine application instance.
174+ agent_card (AgentCard): The AgentCard describing the agent.
175+ user_id (str): The user ID.
176+ task_store_builder (Callable[..., TaskStore], optional): A callable to build the
177+ TaskStore.
178+ task_store_kwargs (Optional[Mapping[str, Any]], optional):
179+ Keyword arguments for the TaskStore builder.
180+ agent_executor_kwargs (Optional[Mapping[str, Any]], optional):
181+ Keyword arguments for the AgentExecutor builder.
182+ agent_executor_builder (Optional[Callable[..., AgentExecutor]], optional):
183+ A callable to build the AgentExecutor.
184+ If not provided, a default `AdkAgentExecutor` will be used.
185+ request_handler_kwargs (Optional[Mapping[str, Any]], optional):
186+ Keyword arguments for the RequestHandler builder.
187+ request_handler_builder (Optional[Callable[..., RequestHandler]], optional):
188+ A callable to build the RequestHandler.
189+ extended_agent_card (AgentCard, optional): An extended AgentCard.
190+
191+ Returns:
192+ object: The augmented Agent Engine application instance.
193+ """
194+ from a2a .server .agent_execution import AgentExecutor , RequestContext
195+ from a2a .server .events import EventQueue
196+ from a2a .server .tasks import TaskUpdater
197+ from google .genai import types
198+ from a2a .types import (
199+ TextPart ,
200+ FilePart ,
201+ FileWithBytes ,
202+ FileWithUri ,
203+ Part ,
204+ )
205+
206+ class DefaultAgentExecutor (AgentExecutor ):
207+ """Agent Executor for adapting an AE application to the A2A protocol."""
208+
209+ def __init__ (self , adk_app_instance ):
210+ self .adk_app = adk_app_instance
211+
212+ def convert_genai_parts_to_a2a (
213+ self , parts : list [types .Part ]
214+ ) -> list [Part ]:
215+ """Convert a list of Google Gen AI Part types into a list of A2A Part types."""
216+ return [
217+ self .convert_genai_part_to_a2a (part )
218+ for part in parts
219+ if (part .text or part .file_data or part .inline_data )
220+ ]
221+
222+ def convert_genai_part_to_a2a (self , part : types .Part ) -> Part :
223+ """Convert a single Google Gen AI Part type into an A2A Part type."""
224+ if part .text :
225+ return TextPart (text = part .text )
226+ if part .file_data :
227+ return FilePart (
228+ file = FileWithUri (
229+ uri = part .file_data .file_uri ,
230+ mime_type = part .file_data .mime_type ,
231+ )
232+ )
233+ if part .inline_data :
234+ return Part (
235+ root = FilePart (
236+ file = FileWithBytes (
237+ bytes = part .inline_data .data ,
238+ mime_type = part .inline_data .mime_type ,
239+ )
240+ )
241+ )
242+ raise ValueError (f"Unsupported part type: { part } " )
243+
244+ async def execute (
245+ self , context : RequestContext , event_queue : EventQueue
246+ ) -> None :
247+ from google .adk .events .event import Event
248+
249+ query = context .get_user_input ()
250+
251+ updater = TaskUpdater (
252+ event_queue , context .task_id , context .context_id
253+ )
254+
255+ if not context .current_task :
256+ await updater .submit ()
257+
258+ await updater .start_work ()
259+
260+ parts = []
261+ for stream_event in self .adk_app .stream_query (
262+ user_id = user_id , message = query
263+ ):
264+ event = Event (** stream_event )
265+ parts .extend (
266+ self .convert_genai_parts_to_a2a (event .content .parts )
267+ )
268+
269+ await updater .add_artifact (
270+ parts ,
271+ name = "result" ,
272+ )
273+ await updater .complete ()
274+
275+ async def cancel (
276+ self , context : RequestContext , event_queue : EventQueue
277+ ) -> None :
278+ raise Exception ("Cancel not supported for this ADK agent" )
279+
280+ if agent_executor_builder :
281+ agent_executor_kwargs ["adk_app_instance" ] = agent_engine_app
282+ a2a_mixin = A2aAgent (
283+ agent_card = agent_card ,
284+ task_store_builder = task_store_builder ,
285+ task_store_kwargs = task_store_kwargs ,
286+ agent_executor_builder = agent_executor_builder ,
287+ agent_executor_kwargs = agent_executor_kwargs ,
288+ request_handler_builder = request_handler_builder ,
289+ request_handler_kwargs = request_handler_kwargs ,
290+ extended_agent_card = extended_agent_card ,
291+ )
292+ else :
293+ a2a_mixin = A2aAgent (
294+ agent_card = agent_card ,
295+ agent_executor_builder = DefaultAgentExecutor ,
296+ agent_executor_kwargs = {"adk_app_instance" : agent_engine_app },
297+ )
298+
299+ def augmented_register_operations ():
300+ routes = agent_engine_app .register_operations ()
301+ a2a_routes = a2a_mixin .register_operations ()
302+ for group , ops in a2a_routes .items ():
303+ if group not in routes :
304+ routes [group ] = []
305+ for op in ops :
306+ if op not in routes [group ]:
307+ routes [group ].append (op )
308+ return routes
309+
310+ original_setup = agent_engine_app .set_up
311+
312+ def augmented_setup ():
313+ original_setup ()
314+ a2a_mixin .set_up ()
315+
316+ agent_engine_app .set_up = augmented_setup
317+
318+ agent_engine_app .register_operations = augmented_register_operations
319+ agent_engine_app .a2a_mixin = a2a_mixin
320+
321+ _original_getattr = agent_engine_app .__class__ .__getattribute__
322+
323+ def __getattr__ (instance , name ):
324+ try :
325+ return _original_getattr (instance , name )
326+ except AttributeError :
327+ if hasattr (instance .a2a_mixin , name ):
328+ return getattr (instance .a2a_mixin , name )
329+ raise
330+
331+ agent_engine_app .__class__ .__getattr__ = __getattr__
332+
333+ return agent_engine_app
334+
335+
154336class A2aAgent :
155337 """A class to initialize and set up an Agent-to-Agent application."""
156338
0 commit comments