|
110 | 110 |
|
111 | 111 |
|
112 | 112 | try: |
| 113 | + from a2a.utils.constants import TransportProtocol as _TpTest |
| 114 | + _A2A_SDK_VERSION = "v1" |
| 115 | +except ImportError: |
| 116 | + try: |
| 117 | + from a2a.types import TransportProtocol as _TpTest |
| 118 | + _A2A_SDK_VERSION = "v0" |
| 119 | + except ImportError: |
| 120 | + _A2A_SDK_VERSION = None |
| 121 | + |
| 122 | +if _A2A_SDK_VERSION == "v1": |
| 123 | + from a2a.types import AgentCard, Message |
| 124 | + from a2a.client import ClientConfig, ClientFactory |
| 125 | + from a2a.utils.constants import TransportProtocol |
| 126 | + from a2a.compat.v0_3.types import TaskIdParams, TaskQueryParams |
| 127 | +elif _A2A_SDK_VERSION == "v0": |
113 | 128 | from a2a.types import ( |
114 | 129 | AgentCard, |
115 | 130 | TransportProtocol, |
|
118 | 133 | TaskQueryParams, |
119 | 134 | ) |
120 | 135 | from a2a.client import ClientConfig, ClientFactory |
121 | | - |
122 | | - AgentCard = AgentCard |
123 | | - TransportProtocol = TransportProtocol |
124 | | - Message = Message |
125 | | - ClientConfig = ClientConfig |
126 | | - ClientFactory = ClientFactory |
127 | | - TaskIdParams = TaskIdParams |
128 | | - TaskQueryParams = TaskQueryParams |
129 | | -except (ImportError, AttributeError): |
| 136 | +else: |
130 | 137 | AgentCard = None |
131 | 138 | TransportProtocol = None |
132 | 139 | Message = None |
@@ -1737,7 +1744,7 @@ async def _method(self: genai_types.AgentEngine, **kwargs) -> AsyncIterator[Any] |
1737 | 1744 | return _method |
1738 | 1745 |
|
1739 | 1746 |
|
1740 | | -def _wrap_a2a_operation(method_name: str, agent_card: str) -> Callable[..., list[Any]]: |
| 1747 | +def _wrap_a2a_operation_v03(method_name: str, agent_card: str) -> Callable[..., list[Any]]: |
1741 | 1748 | """Wraps an Agent Engine method, creating a callable for A2A API. |
1742 | 1749 |
|
1743 | 1750 | Args: |
@@ -1854,6 +1861,119 @@ async def _method(self, **kwargs) -> Any: # type: ignore[no-untyped-def] |
1854 | 1861 | return _method # type: ignore[return-value] |
1855 | 1862 |
|
1856 | 1863 |
|
| 1864 | +def _wrap_a2a_operation(method_name: str, agent_card: str) -> Callable[..., list[Any]]: |
| 1865 | + """Wraps an Agent Engine method, creating a callable for A2A API (v1.0.0+). |
| 1866 | +
|
| 1867 | + Args: |
| 1868 | + method_name: The name of the Agent Engine method to call. |
| 1869 | + agent_card: The agent card to use for the A2A API call. |
| 1870 | + Example: |
| 1871 | + {'additionalInterfaces': None, |
| 1872 | + 'capabilities': {'extensions': None, |
| 1873 | + 'pushNotifications': None, |
| 1874 | + 'stateTransitionHistory': None, |
| 1875 | + 'streaming': False}, |
| 1876 | + 'defaultInputModes': ['text'], |
| 1877 | + 'defaultOutputModes': ['text'], |
| 1878 | + 'description': ( |
| 1879 | + 'A helpful assistant agent that can answer questions.' |
| 1880 | + ), |
| 1881 | + 'documentationUrl': None, |
| 1882 | + 'iconUrl': None, |
| 1883 | + 'name': 'Q&A Agent', |
| 1884 | + 'preferredTransport': 'JSONRPC', |
| 1885 | + 'protocolVersion': '0.3.0', |
| 1886 | + 'provider': None, |
| 1887 | + 'security': None, |
| 1888 | + 'securitySchemes': None, |
| 1889 | + 'signatures': None, |
| 1890 | + 'skills': [{ |
| 1891 | + 'description': ( |
| 1892 | + 'A helpful assistant agent that can answer questions.' |
| 1893 | + ), |
| 1894 | + 'examples': ['Who is leading 2025 F1 Standings?', |
| 1895 | + 'Where can i find an active volcano?'], |
| 1896 | + 'id': 'question_answer', |
| 1897 | + 'inputModes': None, |
| 1898 | + 'name': 'Q&A Agent', |
| 1899 | + 'outputModes': None, |
| 1900 | + 'security': None, |
| 1901 | + 'tags': ['Question-Answer']}], |
| 1902 | + 'supportsAuthenticatedExtendedCard': True, |
| 1903 | + 'url': 'http://localhost:8080/', |
| 1904 | + 'version': '1.0.0'} |
| 1905 | + Returns: |
| 1906 | + A callable object that executes the method on the Agent Engine via |
| 1907 | + the A2A API. |
| 1908 | + """ |
| 1909 | + |
| 1910 | + async def _method(self, **kwargs) -> Any: # type: ignore[no-untyped-def] |
| 1911 | + if not self.api_client: |
| 1912 | + raise ValueError("api_client is not initialized.") |
| 1913 | + if not self.api_resource: |
| 1914 | + raise ValueError("api_resource is not initialized.") |
| 1915 | + |
| 1916 | + a2a_agent_card = AgentCard() |
| 1917 | + json_format.ParseDict(json.loads(agent_card), a2a_agent_card, ignore_unknown_fields=True) |
| 1918 | + |
| 1919 | + if a2a_agent_card.supported_interfaces: |
| 1920 | + interface = a2a_agent_card.supported_interfaces[0] |
| 1921 | + if interface.protocol_binding != TransportProtocol.HTTP_JSON: |
| 1922 | + raise ValueError("Only HTTP+JSON is supported for preferred transport on agent card") |
| 1923 | + else: |
| 1924 | + raise ValueError("Agent card does not define any supported interfaces.") |
| 1925 | + |
| 1926 | + base_url = self.api_client._api_client._http_options.base_url.rstrip("/") |
| 1927 | + api_version = self.api_client._api_client._http_options.api_version |
| 1928 | + a2a_agent_card.supported_interfaces[0].url = f"{base_url}/{api_version}/{self.api_resource.name}/a2a" |
| 1929 | + |
| 1930 | + config = ClientConfig( |
| 1931 | + supported_protocol_bindings=[ |
| 1932 | + TransportProtocol.HTTP_JSON, |
| 1933 | + ], |
| 1934 | + use_client_preference=True, |
| 1935 | + httpx_client=httpx.AsyncClient( |
| 1936 | + headers={ |
| 1937 | + "Authorization": ( |
| 1938 | + f"Bearer {self.api_client._api_client._credentials.token}" |
| 1939 | + ) |
| 1940 | + }, |
| 1941 | + timeout=( |
| 1942 | + self.api_client._api_client._http_options.timeout / 1000.0 |
| 1943 | + if self.api_client._api_client._http_options.timeout |
| 1944 | + else None |
| 1945 | + ), |
| 1946 | + ), |
| 1947 | + ) |
| 1948 | + factory = ClientFactory(config) |
| 1949 | + client = factory.create(a2a_agent_card) |
| 1950 | + |
| 1951 | + if method_name == "on_message_send": |
| 1952 | + response = client.send_message(Message(**kwargs)) |
| 1953 | + chunks = [] |
| 1954 | + async for chunk in response: |
| 1955 | + chunks.append(chunk) |
| 1956 | + return chunks |
| 1957 | + elif method_name == "on_get_task": |
| 1958 | + return await client.get_task(TaskQueryParams(**kwargs)) |
| 1959 | + elif method_name == "on_cancel_task": |
| 1960 | + return await client.cancel_task(TaskIdParams(**kwargs)) |
| 1961 | + elif method_name == "handle_authenticated_agent_card": |
| 1962 | + return await client.get_card() |
| 1963 | + elif method_name == "on_get_extended_agent_card": |
| 1964 | + from a2a.types import GetExtendedAgentCardRequest |
| 1965 | + req = GetExtendedAgentCardRequest() |
| 1966 | + return await client.get_extended_agent_card(req) |
| 1967 | + else: |
| 1968 | + raise ValueError(f"Unknown method name: {method_name}") |
| 1969 | + |
| 1970 | + return _method # type: ignore[return-value] |
| 1971 | + |
| 1972 | + |
| 1973 | +if _A2A_SDK_VERSION != "v1": |
| 1974 | + _wrap_a2a_operation = _wrap_a2a_operation_v03 |
| 1975 | + |
| 1976 | + |
1857 | 1977 | def _yield_parsed_json(http_response: google_genai_types.HttpResponse) -> Iterator[Any]: |
1858 | 1978 | """Converts the body of the HTTP Response message to JSON format. |
1859 | 1979 |
|
|
0 commit comments