44
44
Union ,
45
45
)
46
46
47
- import httpx
48
-
49
47
import proto
50
48
51
49
from google .api_core import exceptions
106
104
Session = Any
107
105
108
106
109
- try :
110
- from a2a .types import (
111
- AgentCard ,
112
- TransportProtocol ,
113
- Message ,
114
- TaskIdParams ,
115
- TaskQueryParams ,
116
- )
117
- from a2a .client import ClientConfig , ClientFactory
118
-
119
- AgentCard = AgentCard
120
- TransportProtocol = TransportProtocol
121
- Message = Message
122
- ClientConfig = ClientConfig
123
- ClientFactory = ClientFactory
124
- TaskIdParams = TaskIdParams
125
- TaskQueryParams = TaskQueryParams
126
- except (ImportError , AttributeError ):
127
- AgentCard = None
128
- TransportProtocol = None
129
- Message = None
130
- ClientConfig = None
131
- ClientFactory = None
132
- TaskIdParams = None
133
- TaskQueryParams = None
134
-
135
107
_ACTIONS_KEY = "actions"
136
108
_ACTION_APPEND = "append"
137
109
_AGENT_FRAMEWORK_ATTR = "agent_framework"
176
148
_REQUIREMENTS_FILE = "requirements.txt"
177
149
_STANDARD_API_MODE = ""
178
150
_STREAM_API_MODE = "stream"
179
- _A2A_EXTENSION_MODE = "a2a_extension"
180
- _A2A_AGENT_CARD = "a2a_agent_card"
181
151
_WARNINGS_KEY = "warnings"
182
152
_WARNING_MISSING = "missing"
183
153
_WARNING_INCOMPATIBLE = "incompatible"
@@ -536,32 +506,11 @@ def _generate_class_methods_spec_or_raise(
536
506
537
507
class_method = _to_proto (schema_dict )
538
508
class_method [_MODE_KEY_IN_SCHEMA ] = mode
539
- if hasattr (agent , "agent_card" ):
540
- class_method [_A2A_AGENT_CARD ] = getattr (
541
- agent , "agent_card"
542
- ).model_dump_json ()
543
509
class_methods_spec .append (class_method )
544
510
545
511
return class_methods_spec
546
512
547
513
548
- def _is_pydantic_serializable (param : inspect .Parameter ) -> bool :
549
- """Checks if the parameter is pydantic serializable."""
550
-
551
- if param .annotation == inspect .Parameter .empty :
552
- return True
553
-
554
- if isinstance (param .annotation , str ):
555
- return False
556
-
557
- pydantic = _import_pydantic_or_raise ()
558
- try :
559
- pydantic .TypeAdapter (param .annotation )
560
- return True
561
- except Exception :
562
- return False
563
-
564
-
565
514
def _generate_schema (
566
515
f : Callable [..., Any ],
567
516
* ,
@@ -623,7 +572,7 @@ def _generate_schema(
623
572
)
624
573
# For a bidi endpoint, it requires an asyncio.Queue as the input, but
625
574
# it is not JSON serializable. We hence exclude it from the schema.
626
- and param .annotation != asyncio .Queue and _is_pydantic_serializable ( param )
575
+ and param .annotation != asyncio .Queue
627
576
}
628
577
parameters = pydantic .create_model (f .__name__ , ** fields_dict ).schema ()
629
578
# Postprocessing
@@ -941,7 +890,6 @@ def _register_api_methods_or_raise(
941
890
_ASYNC_API_MODE : _wrap_async_query_operation ,
942
891
_STREAM_API_MODE : _wrap_stream_query_operation ,
943
892
_ASYNC_STREAM_API_MODE : _wrap_async_stream_query_operation ,
944
- _A2A_EXTENSION_MODE : _wrap_a2a_operation ,
945
893
}
946
894
if isinstance (wrap_operation_fn , dict ) and api_mode in wrap_operation_fn :
947
895
# Override the default function with user-specified function if it exists.
@@ -958,13 +906,7 @@ def _register_api_methods_or_raise(
958
906
)
959
907
960
908
# Bind the method to the object.
961
- if api_mode == _A2A_EXTENSION_MODE :
962
- agent_card = operation_schema .get (_A2A_AGENT_CARD )
963
- method = _wrap_operation (
964
- method_name = method_name , agent_card = agent_card
965
- ) # type: ignore[call-arg]
966
- else :
967
- method = _wrap_operation (method_name = method_name ) # type: ignore[call-arg]
909
+ method = _wrap_operation (method_name = method_name ) # type: ignore[call-arg]
968
910
method .__name__ = method_name
969
911
if method_description and isinstance (method_description , str ):
970
912
method .__doc__ = method_description
@@ -1580,125 +1522,6 @@ async def _method(self: genai_types.AgentEngine, **kwargs) -> AsyncIterator[Any]
1580
1522
return _method
1581
1523
1582
1524
1583
- def _wrap_a2a_operation (method_name : str , agent_card : str ) -> Callable [..., list ]:
1584
- """Wraps an Agent Engine method, creating a callable for A2A API.
1585
-
1586
- Args:
1587
- method_name: The name of the Agent Engine method to call.
1588
- agent_card: The agent card to use for the A2A API call.
1589
- Example:
1590
- {'additionalInterfaces': None,
1591
- 'capabilities': {'extensions': None,
1592
- 'pushNotifications': None,
1593
- 'stateTransitionHistory': None,
1594
- 'streaming': False},
1595
- 'defaultInputModes': ['text'],
1596
- 'defaultOutputModes': ['text'],
1597
- 'description': (
1598
- 'A helpful assistant agent that can answer questions.'
1599
- ),
1600
- 'documentationUrl': None,
1601
- 'iconUrl': None,
1602
- 'name': 'Q&A Agent',
1603
- 'preferredTransport': 'JSONRPC',
1604
- 'protocolVersion': '0.3.0',
1605
- 'provider': None,
1606
- 'security': None,
1607
- 'securitySchemes': None,
1608
- 'signatures': None,
1609
- 'skills': [{
1610
- 'description': (
1611
- 'A helpful assistant agent that can answer questions.'
1612
- ),
1613
- 'examples': ['Who is leading 2025 F1 Standings?',
1614
- 'Where can i find an active volcano?'],
1615
- 'id': 'question_answer',
1616
- 'inputModes': None,
1617
- 'name': 'Q&A Agent',
1618
- 'outputModes': None,
1619
- 'security': None,
1620
- 'tags': ['Question-Answer']}],
1621
- 'supportsAuthenticatedExtendedCard': True,
1622
- 'url': 'http://localhost:8080/',
1623
- 'version': '1.0.0'}
1624
- Returns:
1625
- A callable object that executes the method on the Agent Engine via
1626
- the A2A API.
1627
- """
1628
-
1629
- async def _method (self , ** kwargs ) -> Any :
1630
- """Wraps an Agent Engine method, creating a callable for A2A API."""
1631
- if not self .api_client :
1632
- raise ValueError ("api_client is not initialized." )
1633
- if not self .api_resource :
1634
- raise ValueError ("api_resource is not initialized." )
1635
- a2a_agent_card = AgentCard (** json .loads (agent_card ))
1636
- # A2A + AE integration currently only supports Rest API.
1637
- if (
1638
- a2a_agent_card .preferred_transport
1639
- and a2a_agent_card .preferred_transport != TransportProtocol .http_json
1640
- ):
1641
- raise ValueError (
1642
- "Only HTTP+JSON is supported for preferred transport on agent card "
1643
- )
1644
-
1645
- # Set preferred transport to HTTP+JSON if not set.
1646
- if not hasattr (a2a_agent_card , "preferred_transport" ):
1647
- a2a_agent_card .preferred_transport = TransportProtocol .http_json
1648
-
1649
- # AE cannot support streaming yet. Turn off streaming for now.
1650
- if a2a_agent_card .capabilities and a2a_agent_card .capabilities .streaming :
1651
- raise ValueError (
1652
- "Streaming is not supported in Agent Engine, please change "
1653
- "a2a_agent_card.capabilities.streaming to False."
1654
- )
1655
-
1656
- if not hasattr (a2a_agent_card .capabilities , "streaming" ):
1657
- a2a_agent_card .capabilities .streaming = False
1658
-
1659
- # agent_card is set on the class_methods before set_up is invoked.
1660
- # Ensure that the agent_card url is set correctly before the client is created.
1661
- base_url = self .api_client ._api_client ._http_options .base_url .rstrip ("/" )
1662
- api_version = self .api_client ._api_client ._http_options .api_version
1663
- a2a_agent_card .url = f"{ base_url } /{ api_version } /{ self .api_resource .name } /a2a"
1664
-
1665
- # Using a2a client, inject the auth token from the global config.
1666
- config = ClientConfig (
1667
- supported_transports = [
1668
- TransportProtocol .http_json ,
1669
- ],
1670
- use_client_preference = True ,
1671
- httpx_client = httpx .AsyncClient (
1672
- headers = {
1673
- "Authorization" : (
1674
- f"Bearer { self .api_client ._api_client ._credentials .token } "
1675
- )
1676
- }
1677
- ),
1678
- )
1679
- factory = ClientFactory (config )
1680
- client = factory .create (a2a_agent_card )
1681
-
1682
- if method_name == "on_message_send" :
1683
- response = client .send_message (Message (** kwargs ))
1684
- chunks = []
1685
- async for chunk in response :
1686
- chunks .append (chunk )
1687
- return chunks
1688
- elif method_name == "on_get_task" :
1689
- response = await client .get_task (TaskQueryParams (** kwargs ))
1690
- elif method_name == "on_cancel_task" :
1691
- response = await client .cancel_task (TaskIdParams (** kwargs ))
1692
- elif method_name == "handle_authenticated_agent_card" :
1693
- response = await client .get_card ()
1694
- else :
1695
- raise ValueError (f"Unknown method name: { method_name } " )
1696
-
1697
- return response
1698
-
1699
- return _method
1700
-
1701
-
1702
1525
def _yield_parsed_json (http_response : google_genai_types .HttpResponse ) -> Iterator [Any ]:
1703
1526
"""Converts the body of the HTTP Response message to JSON format.
1704
1527
0 commit comments