Skip to content

Commit cd7a755

Browse files
authored
Merge branch 'main' into feat/gcs-file-data-support
2 parents 6ee4a32 + d760037 commit cd7a755

26 files changed

Lines changed: 2208 additions & 110 deletions

‎.github/workflows/pre-commit.yml‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: pre-commit
16+
17+
on:
18+
push:
19+
branches: [main, v2]
20+
paths:
21+
- '**.py'
22+
- '.pre-commit-config.yaml'
23+
- 'pyproject.toml'
24+
pull_request:
25+
branches: [main, v2]
26+
paths:
27+
- '**.py'
28+
- '.pre-commit-config.yaml'
29+
- 'pyproject.toml'
30+
31+
jobs:
32+
pre-commit:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- name: Checkout Code
36+
uses: actions/checkout@v6
37+
38+
- name: Run pre-commit checks
39+
uses: pre-commit/action@v3.0.1

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ dev = [
9090
"flit>=3.10.0",
9191
"isort>=6.0.0",
9292
"mypy>=1.15.0",
93+
"pre-commit>=4.0.0",
9394
"pyink>=25.12.0",
9495
"pylint>=2.6.0",
9596
# go/keep-sorted end

‎src/google/adk/a2a/converters/from_adk_event.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ def _serialize_value(value: Any) -> Optional[Any]:
257257
try:
258258
dumped = value.model_dump(
259259
exclude_none=True,
260-
exclude_unset=True,
261260
exclude_defaults=True,
262261
by_alias=True,
263262
)

‎src/google/adk/agents/base_agent.py‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@
4242
from ..events.event_actions import EventActions
4343
from ..features import experimental
4444
from ..features import FeatureName
45-
from ..telemetry import tracing
46-
from ..telemetry.tracing import tracer
45+
from ..telemetry import _instrumentation
4746
from ..utils.context_utils import Aclosing
4847
from .base_agent_config import BaseAgentConfig
4948
from .callback_context import CallbackContext
@@ -285,9 +284,8 @@ async def run_async(
285284
Event: the events generated by the agent.
286285
"""
287286

288-
with tracer.start_as_current_span(f'invoke_agent {self.name}') as span:
289-
ctx = self._create_invocation_context(parent_context)
290-
tracing.trace_agent_invocation(span, self, ctx)
287+
ctx = self._create_invocation_context(parent_context)
288+
async with _instrumentation.record_agent_invocation(ctx, self):
291289
if event := await self._handle_before_agent_callback(ctx):
292290
yield event
293291
if ctx.end_invocation:
@@ -318,9 +316,8 @@ async def run_live(
318316
Event: the events generated by the agent.
319317
"""
320318

321-
with tracer.start_as_current_span(f'invoke_agent {self.name}') as span:
322-
ctx = self._create_invocation_context(parent_context)
323-
tracing.trace_agent_invocation(span, self, ctx)
319+
ctx = self._create_invocation_context(parent_context)
320+
async with _instrumentation.record_agent_invocation(ctx, self):
324321
if event := await self._handle_before_agent_callback(ctx):
325322
yield event
326323
if ctx.end_invocation:

‎src/google/adk/features/_feature_registry.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ class FeatureName(str, Enum):
4141
GOOGLE_CREDENTIALS_CONFIG = "GOOGLE_CREDENTIALS_CONFIG"
4242
GOOGLE_TOOL = "GOOGLE_TOOL"
4343
JSON_SCHEMA_FOR_FUNC_DECL = "JSON_SCHEMA_FOR_FUNC_DECL"
44+
# Private (leading underscore): not part of the public API surface.
45+
# GE flips this on by setting the env var
46+
# `ADK_ENABLE_MCP_GRACEFUL_ERROR_HANDLING=1`; nothing should import this
47+
# enum member by name. Keeping it private avoids a backward-compat
48+
# obligation for what is intended as a temporary, internal kill-switch.
49+
_MCP_GRACEFUL_ERROR_HANDLING = "MCP_GRACEFUL_ERROR_HANDLING"
4450
PROGRESSIVE_SSE_STREAMING = "PROGRESSIVE_SSE_STREAMING"
4551
PUBSUB_TOOL_CONFIG = "PUBSUB_TOOL_CONFIG"
4652
PUBSUB_TOOLSET = "PUBSUB_TOOLSET"
@@ -131,6 +137,9 @@ class FeatureConfig:
131137
FeatureName.JSON_SCHEMA_FOR_FUNC_DECL: FeatureConfig(
132138
FeatureStage.WIP, default_on=False
133139
),
140+
FeatureName._MCP_GRACEFUL_ERROR_HANDLING: FeatureConfig(
141+
FeatureStage.EXPERIMENTAL, default_on=False
142+
),
134143
FeatureName.PROGRESSIVE_SSE_STREAMING: FeatureConfig(
135144
FeatureStage.EXPERIMENTAL, default_on=True
136145
),

‎src/google/adk/flows/llm_flows/functions.py‎

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from concurrent.futures import ThreadPoolExecutor
2323
import contextvars
2424
import copy
25-
import functools
2625
import inspect
2726
import logging
2827
import threading
@@ -43,8 +42,8 @@
4342
from ...auth.auth_tool import AuthToolArguments
4443
from ...events.event import Event
4544
from ...events.event_actions import EventActions
45+
from ...telemetry import _instrumentation
4646
from ...telemetry.tracing import trace_merged_tool_calls
47-
from ...telemetry.tracing import trace_tool_call
4847
from ...telemetry.tracing import tracer
4948
from ...tools.base_tool import BaseTool
5049
from ...tools.tool_confirmation import ToolConfirmation
@@ -591,22 +590,11 @@ async def _run_with_trace():
591590
)
592591
return function_response_event
593592

594-
with tracer.start_as_current_span(f'execute_tool {tool.name}'):
595-
function_response_event = None
596-
caught_error = None
597-
try:
598-
function_response_event = await _run_with_trace()
599-
return function_response_event
600-
except Exception as e:
601-
caught_error = e
602-
raise
603-
finally:
604-
trace_tool_call(
605-
tool=tool,
606-
args=function_args,
607-
function_response_event=function_response_event,
608-
error=caught_error,
609-
)
593+
async with _instrumentation.record_tool_execution(
594+
tool, agent, invocation_context, function_args
595+
) as tel_ctx:
596+
tel_ctx.function_response_event = await _run_with_trace()
597+
return tel_ctx.function_response_event
610598

611599

612600
async def handle_function_calls_live(
@@ -830,22 +818,11 @@ async def _run_with_trace():
830818
)
831819
return function_response_event
832820

833-
with tracer.start_as_current_span(f'execute_tool {tool.name}'):
834-
function_response_event = None
835-
caught_error = None
836-
try:
837-
function_response_event = await _run_with_trace()
838-
return function_response_event
839-
except Exception as e:
840-
caught_error = e
841-
raise
842-
finally:
843-
trace_tool_call(
844-
tool=tool,
845-
args=function_args,
846-
function_response_event=function_response_event,
847-
error=caught_error,
848-
)
821+
async with _instrumentation.record_tool_execution(
822+
tool, agent, invocation_context, function_args
823+
) as tel_ctx:
824+
tel_ctx.function_response_event = await _run_with_trace()
825+
return tel_ctx.function_response_event
849826

850827

851828
async def _process_function_live_helper(

‎src/google/adk/plugins/bigquery_agent_analytics_plugin.py‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,7 @@ def __init__(
19491949
table_id: Optional[str] = None,
19501950
config: Optional[BigQueryLoggerConfig] = None,
19511951
location: str = "US",
1952+
credentials: Optional[google.auth.credentials.Credentials] = None,
19521953
**kwargs,
19531954
) -> None:
19541955
"""Initializes the instance.
@@ -1959,6 +1960,8 @@ def __init__(
19591960
table_id: BigQuery table ID (optional, overrides config).
19601961
config: BigQueryLoggerConfig (optional).
19611962
location: BigQuery location (default: "US").
1963+
credentials: Google Auth credentials (optional). If None, uses
1964+
Application Default Credentials.
19621965
**kwargs: Additional configuration parameters for BigQueryLoggerConfig.
19631966
"""
19641967
super().__init__(name="bigquery_agent_analytics")
@@ -1985,6 +1988,7 @@ def __init__(
19851988
self._startup_error: Optional[Exception] = None
19861989
self._is_shutting_down = False
19871990
self._setup_lock = None
1991+
self._credentials = credentials
19881992
self.client = None
19891993
self._loop_state_by_loop: dict[asyncio.AbstractEventLoop, _LoopState] = {}
19901994
self._write_stream_name = None # Resolved stream name
@@ -2097,15 +2101,16 @@ async def _get_loop_state(self) -> _LoopState:
20972101
# grpc.aio clients are loop-bound, so we create one per event loop.
20982102

20992103
def get_credentials():
2100-
creds, project_id = google.auth.default(
2104+
creds, _ = google.auth.default(
21012105
scopes=["https://www.googleapis.com/auth/cloud-platform"]
21022106
)
2103-
return creds, project_id
2107+
return creds
21042108

2105-
creds, project_id = await loop.run_in_executor(
2106-
self._executor, get_credentials
2107-
)
2108-
quota_project_id = getattr(creds, "quota_project_id", None)
2109+
if self._credentials is None:
2110+
self._credentials = await loop.run_in_executor(
2111+
self._executor, get_credentials
2112+
)
2113+
quota_project_id = getattr(self._credentials, "quota_project_id", None)
21092114
options = (
21102115
client_options.ClientOptions(quota_project_id=quota_project_id)
21112116
if quota_project_id
@@ -2119,7 +2124,7 @@ def get_credentials():
21192124
client_info = gapic_client_info.ClientInfo(user_agent=" ".join(user_agents))
21202125

21212126
write_client = BigQueryWriteAsyncClient(
2122-
credentials=creds,
2127+
credentials=self._credentials,
21232128
client_info=client_info,
21242129
client_options=options,
21252130
)
@@ -2173,7 +2178,9 @@ async def _lazy_setup(self, **kwargs) -> None:
21732178
self.client = await loop.run_in_executor(
21742179
self._executor,
21752180
lambda: bigquery.Client(
2176-
project=self.project_id, location=self.location
2181+
project=self.project_id,
2182+
location=self.location,
2183+
credentials=self._credentials,
21772184
),
21782185
)
21792186

@@ -2193,7 +2200,9 @@ async def _lazy_setup(self, **kwargs) -> None:
21932200
self.project_id,
21942201
self.config.gcs_bucket_name,
21952202
self._executor,
2196-
storage_client=kwargs.get("storage_client"),
2203+
storage_client=storage.Client(
2204+
project=self.project_id, credentials=self._credentials
2205+
),
21972206
)
21982207

21992208
self.parser = HybridContentParser(

‎src/google/adk/sessions/vertex_ai_session_service.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848
_USAGE_METADATA_CUSTOM_METADATA_KEY = '_usage_metadata'
4949

5050

51+
def _quote_filter_literal(value: str) -> str:
52+
"""Quotes filter values so embedded metacharacters stay inside the literal."""
53+
escaped_value = value.replace('\\', '\\\\').replace('"', '\\"')
54+
return f'"{escaped_value}"'
55+
56+
5157
def _set_internal_custom_metadata(
5258
metadata_dict: dict[str, Any], *, key: str, value: dict[str, Any]
5359
) -> None:
@@ -228,7 +234,7 @@ async def list_sessions(
228234
sessions = []
229235
config = {}
230236
if user_id is not None:
231-
config['filter'] = f'user_id="{user_id}"'
237+
config['filter'] = f'user_id={_quote_filter_literal(user_id)}'
232238
sessions_iterator = await api_client.agent_engines.sessions.list(
233239
name=f'reasoningEngines/{reasoning_engine_id}',
234240
config=config,

0 commit comments

Comments
 (0)