Skip to content

Commit 7691d22

Browse files
authored
Merge branch 'main' into fix-2729-none-inferences
2 parents f9f6ebf + 143ad44 commit 7691d22

File tree

14 files changed

+640
-15
lines changed

14 files changed

+640
-15
lines changed

contributing/samples/spanner/agent.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from google.adk.auth.auth_credential import AuthCredentialTypes
1919
from google.adk.tools.google_tool import GoogleTool
2020
from google.adk.tools.spanner.settings import Capabilities
21+
from google.adk.tools.spanner.settings import QueryResultMode
2122
from google.adk.tools.spanner.settings import SpannerToolSettings
2223
from google.adk.tools.spanner.spanner_credentials import SpannerCredentialsConfig
2324
from google.adk.tools.spanner.spanner_toolset import SpannerToolset
@@ -34,7 +35,10 @@
3435

3536

3637
# Define Spanner tool config with read capability set to allowed.
37-
tool_settings = SpannerToolSettings(capabilities=[Capabilities.DATA_READ])
38+
tool_settings = SpannerToolSettings(
39+
capabilities=[Capabilities.DATA_READ],
40+
query_result_mode=QueryResultMode.DICT_LIST,
41+
)
3842

3943
if CREDENTIALS_TYPE == AuthCredentialTypes.OAUTH2:
4044
# Initialize the tools to do interactive OAuth

src/google/adk/agents/remote_a2a_agent.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
from a2a.client.card_resolver import A2ACardResolver
3232
from a2a.client.client import ClientConfig as A2AClientConfig
3333
from a2a.client.client_factory import ClientFactory as A2AClientFactory
34-
from a2a.client.errors import A2AClientError
34+
from a2a.client.errors import A2AClientHTTPError
35+
from a2a.client.middleware import ClientCallContext
3536
from a2a.types import AgentCard
3637
from a2a.types import Message as A2AMessage
3738
from a2a.types import Part as A2APart
@@ -533,6 +534,7 @@ async def _run_async_impl(
533534
async for a2a_response in self._a2a_client.send_message(
534535
request=a2a_request,
535536
request_metadata=request_metadata,
537+
context=ClientCallContext(state=ctx.session.state),
536538
):
537539
logger.debug(build_a2a_response_log(a2a_response))
538540

@@ -558,6 +560,24 @@ async def _run_async_impl(
558560

559561
yield event
560562

563+
except A2AClientHTTPError as e:
564+
error_message = f"A2A request failed: {e}"
565+
logger.error(error_message)
566+
yield Event(
567+
author=self.name,
568+
error_message=error_message,
569+
invocation_id=ctx.invocation_id,
570+
branch=ctx.branch,
571+
custom_metadata={
572+
A2A_METADATA_PREFIX
573+
+ "request": a2a_request.model_dump(
574+
exclude_none=True, by_alias=True
575+
),
576+
A2A_METADATA_PREFIX + "error": error_message,
577+
A2A_METADATA_PREFIX + "status_code": str(e.status_code),
578+
},
579+
)
580+
561581
except Exception as e:
562582
error_message = f"A2A request failed: {e}"
563583
logger.error(error_message)

src/google/adk/runners.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,11 @@ async def _run_with_trace(
412412
message = self._format_session_not_found_message(session_id)
413413
raise ValueError(message)
414414
if not invocation_id and not new_message:
415-
raise ValueError('Both invocation_id and new_message are None.')
415+
raise ValueError(
416+
'Running an agent requires either a new_message or an '
417+
'invocation_id to resume a previous invocation. '
418+
f'Session: {session_id}, User: {user_id}'
419+
)
416420

417421
if invocation_id:
418422
if (

src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import json
1818
import logging
19+
import ssl
1920
from typing import Any
2021
from typing import Dict
2122
from typing import Final
@@ -68,6 +69,7 @@ def __init__(
6869
auth_scheme: Optional[AuthScheme] = None,
6970
auth_credential: Optional[AuthCredential] = None,
7071
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
72+
ssl_verify: Optional[Union[bool, str, ssl.SSLContext]] = None,
7173
):
7274
"""Initializes the OpenAPIToolset.
7375
@@ -102,10 +104,19 @@ def __init__(
102104
``google.adk.tools.openapi_tool.auth.auth_helpers``
103105
tool_filter: The filter used to filter the tools in the toolset. It can be
104106
either a tool predicate or a list of tool names of the tools to expose.
107+
ssl_verify: SSL certificate verification option for all tools. Can be:
108+
- None: Use default verification (True)
109+
- True: Verify SSL certificates using system CA
110+
- False: Disable SSL verification (insecure, not recommended)
111+
- str: Path to a CA bundle file or directory for custom CA
112+
- ssl.SSLContext: Custom SSL context for advanced configuration
113+
This is useful for enterprise environments where requests go through
114+
a TLS-intercepting proxy with a custom CA certificate.
105115
"""
106116
super().__init__(tool_filter=tool_filter)
107117
if not spec_dict:
108118
spec_dict = self._load_spec(spec_str, spec_str_type)
119+
self._ssl_verify = ssl_verify
109120
self._tools: Final[List[RestApiTool]] = list(self._parse(spec_dict))
110121
if auth_scheme or auth_credential:
111122
self._configure_auth_all(auth_scheme, auth_credential)
@@ -121,6 +132,26 @@ def _configure_auth_all(
121132
if auth_credential:
122133
tool.configure_auth_credential(auth_credential)
123134

135+
def configure_ssl_verify_all(
136+
self, ssl_verify: Optional[Union[bool, str, ssl.SSLContext]] = None
137+
):
138+
"""Configure SSL certificate verification for all tools.
139+
140+
This is useful for enterprise environments where requests go through a
141+
TLS-intercepting proxy with a custom CA certificate.
142+
143+
Args:
144+
ssl_verify: SSL certificate verification option. Can be:
145+
- None: Use default verification (True)
146+
- True: Verify SSL certificates using system CA
147+
- False: Disable SSL verification (insecure, not recommended)
148+
- str: Path to a CA bundle file or directory for custom CA
149+
- ssl.SSLContext: Custom SSL context for advanced configuration
150+
"""
151+
self._ssl_verify = ssl_verify
152+
for tool in self._tools:
153+
tool.configure_ssl_verify(ssl_verify)
154+
124155
@override
125156
async def get_tools(
126157
self, readonly_context: Optional[ReadonlyContext] = None
@@ -154,7 +185,7 @@ def _parse(self, openapi_spec_dict: Dict[str, Any]) -> List[RestApiTool]:
154185

155186
tools = []
156187
for o in operations:
157-
tool = RestApiTool.from_parsed_operation(o)
188+
tool = RestApiTool.from_parsed_operation(o, ssl_verify=self._ssl_verify)
158189
logger.info("Parsed tool: %s", tool.name)
159190
tools.append(tool)
160191
return tools

src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from __future__ import annotations
1616

17+
import ssl
1718
from typing import Any
1819
from typing import Dict
1920
from typing import List
@@ -88,6 +89,7 @@ def __init__(
8889
auth_scheme: Optional[Union[AuthScheme, str]] = None,
8990
auth_credential: Optional[Union[AuthCredential, str]] = None,
9091
should_parse_operation=True,
92+
ssl_verify: Optional[Union[bool, str, ssl.SSLContext]] = None,
9193
):
9294
"""Initializes the RestApiTool with the given parameters.
9395
@@ -114,6 +116,12 @@ def __init__(
114116
(https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object)
115117
auth_credential: The authentication credential of the tool.
116118
should_parse_operation: Whether to parse the operation.
119+
ssl_verify: SSL certificate verification option. Can be:
120+
- None: Use default verification
121+
- True: Verify SSL certificates using system CA
122+
- False: Disable SSL verification (insecure, not recommended)
123+
- str: Path to a CA bundle file or directory for custom CA
124+
- ssl.SSLContext: Custom SSL context for advanced configuration
117125
"""
118126
# Gemini restrict the length of function name to be less than 64 characters
119127
self.name = name[:60]
@@ -136,15 +144,21 @@ def __init__(
136144
# Private properties
137145
self.credential_exchanger = AutoAuthCredentialExchanger()
138146
self._default_headers: Dict[str, str] = {}
147+
self._ssl_verify = ssl_verify
139148
if should_parse_operation:
140149
self._operation_parser = OperationParser(self.operation)
141150

142151
@classmethod
143-
def from_parsed_operation(cls, parsed: ParsedOperation) -> "RestApiTool":
152+
def from_parsed_operation(
153+
cls,
154+
parsed: ParsedOperation,
155+
ssl_verify: Optional[Union[bool, str, ssl.SSLContext]] = None,
156+
) -> "RestApiTool":
144157
"""Initializes the RestApiTool from a ParsedOperation object.
145158
146159
Args:
147160
parsed: A ParsedOperation object.
161+
ssl_verify: SSL certificate verification option.
148162
149163
Returns:
150164
A RestApiTool object.
@@ -163,6 +177,7 @@ def from_parsed_operation(cls, parsed: ParsedOperation) -> "RestApiTool":
163177
operation=parsed.operation,
164178
auth_scheme=parsed.auth_scheme,
165179
auth_credential=parsed.auth_credential,
180+
ssl_verify=ssl_verify,
166181
)
167182
generated._operation_parser = operation_parser
168183
return generated
@@ -218,6 +233,24 @@ def configure_auth_credential(
218233
auth_credential = AuthCredential.model_validate_json(auth_credential)
219234
self.auth_credential = auth_credential
220235

236+
def configure_ssl_verify(
237+
self, ssl_verify: Optional[Union[bool, str, ssl.SSLContext]] = None
238+
):
239+
"""Configures SSL certificate verification for the API call.
240+
241+
This is useful for enterprise environments where requests go through a
242+
TLS-intercepting proxy with a custom CA certificate.
243+
244+
Args:
245+
ssl_verify: SSL certificate verification option. Can be:
246+
- None: Use default verification (True)
247+
- True: Verify SSL certificates using system CA
248+
- False: Disable SSL verification (insecure, not recommended)
249+
- str: Path to a CA bundle file or directory for custom CA
250+
- ssl.SSLContext: Custom SSL context for advanced configuration
251+
"""
252+
self._ssl_verify = ssl_verify
253+
221254
def set_default_headers(self, headers: Dict[str, str]):
222255
"""Sets default headers that are merged into every request."""
223256
self._default_headers = headers
@@ -415,6 +448,8 @@ async def call(
415448

416449
# Got all parameters. Call the API.
417450
request_params = self._prepare_request_params(api_params, api_args)
451+
if self._ssl_verify is not None:
452+
request_params["verify"] = self._ssl_verify
418453
response = requests.request(**request_params)
419454

420455
# Parse API response

0 commit comments

Comments
 (0)