Commit Graph

2126 Commits

Author SHA1 Message Date
George Weale 2aa2b469b0 perf: run the Google credential refresh off the event loop
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967420389
2026-08-19 14:46:52 -07:00
George Weale 5b59139e0e fix(workflow): resume node auth from the node's own auth config
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967419219
2026-08-19 14:44:21 -07:00
George Weale 3f9e6bec37 fix: sanitize state deltas before writing them to JSON state columns
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414738
2026-08-19 14:37:13 -07:00
George Weale f3b59fd62e fix(workflow): declare abc.ABC on BaseNode so abstract subclasses type-check
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414592
2026-08-19 14:36:39 -07:00
George Weale 51231cd4ac perf: key the Pub/Sub publisher cache on its options' value
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414460
2026-08-19 14:35:16 -07:00
George Weale 94475c9a76 fix(artifacts): Publish file artifact versions atomically
FileArtifactService chose the next version by listing the versions directory
and then created that directory as a separate step. Two saves racing on the
same artifact read the same version list, and the loser's mkdir raised
FileExistsError. An interrupted save was worse: the version directory existed
but held no payload, and because version discovery counts any integer-named
directory, that empty directory became the latest version and load_artifact
returned None instead of the previous good content.

A save now reserves a version by creating a hidden `.{version}.pending`
directory, which mkdir makes atomic and mutually exclusive, writes the payload
and metadata inside it, and publishes the result with a single os.replace onto
`versions/{version}`. Version discovery ignores the staging directories, so a
version becomes visible only once it is complete, and a save that raises
removes its own staging directory. A save whose version list went stale
re-checks before publishing and takes the next free version instead, so
os.replace always lands on a name that does not exist. That last part matters
on Windows, where os.replace maps to MoveFileEx and reports an error when the
destination names an existing directory.

The guarantee is scoped to process-level faults. An exception or a signal never
publishes a partial version, but nothing is fsynced, so a power loss can still
make the rename durable ahead of the file contents and expose an empty
`versions/{version}`. Full durability would need explicit fsyncs and is not
addressed here, and version directories that earlier releases already left
empty are not repaired.

Behavior change: published version numbers are no longer guaranteed to be
contiguous. A reservation abandoned by a signal or a host failure leaves its
`.{version}.pending` directory behind; that directory is never read and never
published, but it holds its version number permanently. There is deliberately
no in-process sweep, because nothing distinguishes an abandoned reservation
from a slow concurrent write, so reclaiming those directories is left to the
operator. The class-level layout comment records the staging directory and the
non-contiguity.

The public artifact-service API and the completed on-disk version layout are
unchanged.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967413806
2026-08-19 14:33:45 -07:00
Liang Wu 7616c78a1e fix: trigger Gemini 3.x Live response after sending conversation history
Gemini 3.x Live does not begin generating from `send_client_content` alone; it
starts only once it receives realtime input. As a result, seeding a new live
connection with prior conversation history left the model silent until the user
spoke again. This is most visible right after an agent transfer, where the
sub-agent is expected to reply immediately.

`GeminiLlmConnection.send_history` now follows the replayed history with a
minimal placeholder realtime input for Gemini 3.x Live models, gated on the
history ending with a user turn (the same condition that sets `turn_complete`).
Histories that end on a model turn still leave the model waiting for new user
input, preserving the documented `send_history` behavior. Other model families
are unaffected.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 967411170
2026-08-19 14:28:55 -07:00
Chris Kinzel bb86bdd737 fix: prevent contextvars leak across async generators
Merge https://github.com/google/adk-python/pull/5725

Fixes #5722

PiperOrigin-RevId: 967359464
2026-08-19 12:53:29 -07:00
Ashutosh0x 8d2f2779e6 fix: validate SQL identifiers in Spanner search tool
Merge https://github.com/google/adk-python/pull/5952

Fixes #5913

PiperOrigin-RevId: 967358801
2026-08-19 12:52:20 -07:00
Kathy Wu 8989aeadce fix(auth): take the auth scheme from the request, not the client's response
The auth preprocessor built the token exchange out of whatever the client echoed
back, including the `auth_scheme`. That scheme names the token endpoint the
exchange posts to, so a client could point it at a server it controls. It also
honoured responses for function call IDs the session never issued, where there
is no request to check them against at all.

Both now come from the `adk_request_credential` call this server issued. A
response with no matching call is dropped.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 967358755
2026-08-19 12:51:21 -07:00
Gaurav Gandhi 023f45c3e5 fix: resolve NameError in legacy create-eval-set route
Merge https://github.com/google/adk-python/pull/6681

PiperOrigin-RevId: 967357331
2026-08-19 12:49:41 -07:00
Anas Khan 1ed8d48620 fix: guard against Content with no parts in _content_to_message_param
Merge https://github.com/google/adk-python/pull/6312

Avoid raising TypeError when types.Content has parts=None or parts=[] in LiteLLM adapter.

PiperOrigin-RevId: 967357294
2026-08-19 12:48:40 -07:00
prasanna8585 924d802f5b fix: block yaml and ruamel deserialization in agent-config code references
Merge https://github.com/google/adk-python/pull/6646

Reject yaml and ruamel unsafe/full loaders in config agent to prevent RCE.
Note: ruamel is a transitive dev/test dependency, but is blocked to prevent RCE if present in the environment.
PiperOrigin-RevId: 967340296
2026-08-19 12:12:55 -07:00
aryanpatel2121 0dbb88c7c4 feat: parallelize LLM-as-judge evaluation using asyncio.gather()
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Merge https://github.com/google/adk-python/pull/3960

This PR improves the performance of `LlmAsJudge.evaluate_invocations()` by
running LLM evaluation calls concurrently instead of executing them one-by-one.

PiperOrigin-RevId: 967276232
2026-08-19 10:14:10 -07:00
Google Team Member d0b33a0569 fix: cache read write token counts in LiteLLM and Anthropic models
Extract cache creation (write) tokens from LiteLLM and Anthropic model
usage metadata and map them to the GenerateContentResponseUsageMetadata
object. This ensures they are recorded in telemetry, allowing correct cost
calculations for prompt caching with providers like Bedrock.

Close #5835

PiperOrigin-RevId: 967259812
2026-08-19 09:44:47 -07:00
prasanna8585 1cd6f464e5 fix: redact credentials from generate_content_config.http_options in debug logs
Merge https://github.com/google/adk-python/pull/6546

PiperOrigin-RevId: 967026896
2026-08-19 00:16:10 -07:00
Nikhil Chaudhary ff4567df38 fix: pass file metadata tuple to Azure for PDF uploads
Merge https://github.com/google/adk-python/pull/6548

Fixes #6539

PiperOrigin-RevId: 967023375
2026-08-19 00:07:02 -07:00
Tony Coconate eaad2f83b9 fix: prevent duplicate OAuth prompts and fix tool resumption
Merge https://github.com/google/adk-python/pull/5985

PiperOrigin-RevId: 967017952
2026-08-18 23:53:38 -07:00
Jinni Gu 0fd681e7d2 feat: support sub-agent escalation event in ParallelAgent
Merge https://github.com/google/adk-python/pull/5105

Closes: #5104
PiperOrigin-RevId: 967017854
2026-08-18 23:52:29 -07:00
Kathy Wu aa6a07bc97 refactor: share the credential-to-HTTP-header conversion
`McpToolset._get_auth_headers` held the only complete mapping from a resolved
`AuthCredential` to request headers: OAuth2 access tokens, HTTP bearer, HTTP
basic, other HTTP schemes, `additional_headers`, and header-located API keys.
`McpTool._get_headers` already duplicates it, and the next component that needs
to authenticate an outgoing request would be writing the third copy.

Move it to `google.adk.auth._auth_headers.build_auth_headers` and have
`McpToolset` delegate. The extracted function is the existing body with the
auth scheme passed in as an argument instead of read off `self._auth_config`.
The only differences are the text of the API-key warning and the logger it is
emitted on; no test asserts either. The existing `test_mcp_toolset_auth.py`
cases still exercise the mapping through the toolset, and the new tests cover
the function directly, including branches those cases cannot reach: a
non-bearer HTTP scheme, `additional_headers` with no usable scheme or token,
and an API key with no scheme at all.

`McpTool._get_headers` is deliberately left alone: it raises where the toolset
logs and warns on unexchanged service-account credentials, so folding it in
would not be a pure refactor.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 966867565
2026-08-18 16:28:24 -07:00
Fnu Abdullah 26381552c0 fix: disable Windows glob expansion for CLI args
This prevents Click from expanding wildcard arguments on Windows (e.g. `*` in `--allow_origins "*"`), avoiding errors when Click attempts to parse expanded filenames as unexpected positional arguments.

Fixes #6248

PiperOrigin-RevId: 966756317
2026-08-18 12:57:19 -07:00
Jonathan Hill 2876987a78 refactor: introduce safe_json_loads helper and migrate selected callsites
This CL introduces `_json_utils.safe_json_loads` to provide a uniform ValueError
when JSON parsing fails, wrapping the underlying json.JSONDecodeError.
Initial callsites in evaluations, sessions, and some models have been migrated.

Merge https://github.com/google/adk-python/pull/5858

PiperOrigin-RevId: 966722835
2026-08-18 11:54:55 -07:00
Aarav Mittal a30858b11c feat: preserve field descriptions in set_model_response schema
Merge https://github.com/google/adk-python/pull/6715

Fixes #6707

PiperOrigin-RevId: 966698658
2026-08-18 11:15:20 -07:00
Kathy Wu 9ffe8be6f9 fix: fence relayed agent output so it cannot pose as instructions
When one agent hands off to another, `_present_other_agent_message` replays the
first agent's turn to the second as a `role="user"` message -- the same channel
the real user speaks on -- interpolating the text straight into
`[agent] said: ...`. Nothing marks where the quoted transcript ends, so a
payload the first agent was talked into emitting reads to the second agent as a
fresh directive. Anyone who can chat to a low-privilege front-end agent can
therefore aim instructions at whatever tools the agent it transfers to holds.

Every relayed payload -- text, thoughts, tool arguments, tool results -- is now
quoted between explicit markers, and the leading part of the message states
that what sits between them is data to read and not instructions to follow.
Markers occurring inside a payload are elided first, so quoted content cannot
close its own block and carry on speaking as the framework.

The markers, the preamble and the quoting helpers live in
`flows/llm_flows/_fencing.py`. The unit tests and the conformance harness both
have to spell the expected framing, so it sits in a module of its own rather
than inside `contents.py`, where they would have to reach for private names.

This raises the bar rather than closing the class: a model can still be talked
round by text it was told to distrust. What it removes is the structural
ambiguity that made a relayed payload indistinguishable from a user turn.

Relayed turns now cost the preamble plus two marker lines per part, and
anything matching on the old `For context: [x] said: y` shape needs updating.
The conformance replay harness is one such matcher, and now reduces a relayed
turn to the payload it carries before comparing, so recordings cut before the
fencing still replay.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 966694665
2026-08-18 11:08:56 -07:00
chelsealong deda5b30e8 fix: keep non-text static_instruction as a stable request prefix
Merge https://github.com/google/adk-python/pull/6653

Fixes #6652

PiperOrigin-RevId: 966680116
2026-08-18 10:42:54 -07:00
Asjad Abbas 42a4a5f0e7 fix: resolve Claude 5 model names in the LLM registry
Merge https://github.com/google/adk-python/pull/6558

PiperOrigin-RevId: 966679423
2026-08-18 10:41:28 -07:00
George Weale 39f43eb4c0 test(telemetry): assert spans and metrics report the same facts
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 966599365
2026-08-18 08:07:07 -07:00
Max Ind 0514657998 test(telemetry): Record every scenario under both instrumentations
ADK instruments inference itself, unless it detects
opentelemetry-instrumentation-google-genai, in which case it stands down and
that library's telemetry is what users get. Only the first was ever tested.

Every case now runs twice -- once over a MockModel with ADK's own
instrumentation, once over a real Gemini with the instrumentor and a
mocked-out google.genai SDK. The goldens stay what they were, ADK's own
recording pinned in full. How the instrumentor's recording of the same run
differs is in functional_divergences.json: one group per span, log or metric,
one entry per slot within it, with an example of what each side recorded, the
tests it turns up in, and the kind (adk_bug, otel_bug or desired_behavior)
and reason a developer owes for it. A gap that is new, or recorded without an
explanation, fails the case that produces it, so a new one cannot appear
silently.

The gaps therefore live in one reviewable file rather than smeared through
every golden. The cost is that the instrumentor's recording cannot be rebuilt
from disk: only the native path is pinned value for value, and the other is
held to having no unexplained gap against it.

functional/_divergences.py is the whole of it. It overlays the two recordings
as JSON, so it descends into JSON-valued attributes too: two payloads that
differ in one field diverge in that field rather than wholesale. Divergences
are named owner-relative -- the span, log or metric they sit on, plus the
route within it -- so one gap is one entry however deep it sits, however
often it recurs, and in however many goldens.

25 divergences, all explained. The first adk_bug among them: ADK leaves
error.type off the inference span on a failed call, reporting the failure
only on the enclosing spans and the duration metric.

The MCP scenario's server now resolves the same tool the canned conversation
calls, so one conversation drives every scenario and the MCP recording covers
a tool call arriving through MCP rather than only the tool definitions.

test_smoke.py pins the private internals the harness reaches into -- the
semconv stability cache it resets per case, the SDK call the instrumentor has
to wrap, and the MCP session manager it patches -- so an upgrade that renames
one fails there rather than as a puzzling golden diff.

Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 966591641
2026-08-18 07:51:23 -07:00
Max Ind f25daed85e test(telemetry): Order the recorded telemetry by when it was emitted
The functional goldens sorted a span's children by name and its log records
by their own contents. That is deterministic, but it reads nothing like the
run it records: the tool call sorts before the inference that asked for it,
and a golden diff moves records around for reasons that have nothing to do
with the change under review.

The scenarios are single-threaded, so emission order is deterministic too.
Child spans are now ordered by start time and log records by their observed
timestamp, and the goldens read down the run.

Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 966554122
2026-08-18 06:17:08 -07:00
Google Team Member 5d84905cec refactor: consolidate a2a compat well-known-type imports
Import timestamp_pb2 / struct_pb2 once at module scope on the 1.x.x path,
instead of lazily at each call site.

PiperOrigin-RevId: 966541598
2026-08-18 05:47:06 -07:00
Osamaali313 029c17b338 fix: read long-running function name from data, not metadata
Merge https://github.com/google/adk-python/pull/6296

Fixes #6295

PiperOrigin-RevId: 966470802
2026-08-18 02:36:45 -07:00
Google Team Member 6e0facf937 feat(telemetry): Expand telemetry for load_skill_resource span
PiperOrigin-RevId: 966463730
2026-08-18 02:18:42 -07:00
Google Team Member d6290a0b2e feat: guard against SQL injection vulnerabilities in BigQuery tools
This change introduces several security enhancements to BigQuery query tools:
- Strict identifier validation for table and column names to prevent injection.
- Secure escaping of string literals.
- Subquery validation using dry-run execution to ensure only SELECT statements are executed in dynamic contexts.
- Improved exception handling to distinguish between query validation errors and infrastructure failures.
- Input type validation for parameters like horizon and anomaly thresholds.
- Comprehensive parameterized test suites for security boundary cases.

PiperOrigin-RevId: 966430775
2026-08-18 00:57:14 -07:00
nikkie babb11c83c feat(eval): support custom metrics in AgentEvaluator
Merge https://github.com/google/adk-python/pull/4344

**Please ensure you have read the [contribution guide](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) before creating a pull request.**

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: #4343

**Problem:**
`AgentEvaluator.evaluate` did not register custom metrics from `EvalConfig`, so custom metrics worked in `adk eval` but not in pytest-based evals.

**Solution:**
Align `AgentEvaluator` with the CLI eval flow by registering custom metrics via a per-run metric registry and a shared default `MetricInfo` helper. The per-run registry is a fork of `DEFAULT_METRIC_EVALUATOR_REGISTRY` (new `MetricEvaluatorRegistry.fork()`), so the custom metrics declared by one eval config never leak into another run.

### Testing Plan

Add unit coverage for the registration behavior and a lightweight integration example that uses a custom metric.

**Unit Tests:**

- [x] I have added or updated unit tests for my change.
- [x] All unit tests pass locally.

```
% pytest tests/unittests/evaluation tests/unittests/cli

614 passed, 308 warnings in 12.74s
```

**Manual End-to-End (E2E) Tests:**

```
% pytest tests/integration/test_with_test_file.py::test_with_custom_metric

tests/integration/test_with_test_file.py .                               [100%]

1 passed, 12 warnings in 3.57s
```

### Checklist

- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [x] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing unit tests pass locally with my changes.
- [x] I have manually tested my changes end-to-end.
- [x] Any dependent changes have been merged and published in downstream modules.

### Additional context

This change keeps `AgentEvaluator` behavior consistent with `adk eval` while avoiding CLI-layer dependencies.

`fork()` returns an isolated copy seeded with the source registry's contents, rather than a bare `MetricEvaluatorRegistry()`. The seeding is what keeps this backwards compatible: registering an `Evaluator` subclass on `DEFAULT_METRIC_EVALUATOR_REGISTRY` is the only way to plug one in, since an eval config can only name a scoring function. Callers who do that today (including ones replacing the evaluator behind a standard metric name) would otherwise silently fall back to the stock evaluator, with no error and a different score. Covered by `test_evaluate_eval_set_keeps_evaluators_from_the_default_registry`.

Co-authored-by: Yi Liu <yiliuly@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4344 from ftnext:agent-evaluator-support-custom-metric 3c844a9817d16954c683b164a88a86a82e8f9cf1
PiperOrigin-RevId: 966416454
2026-08-18 00:17:15 -07:00
Shangjie Chen c9323d5861 fix(sessions): dedupe InMemorySessionService events by equality not id
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 966353096
2026-08-17 21:24:37 -07:00
chelsealong 81873e28c5 test: add test for final chunk replacing accumulated artifact
Verify that the final non-partial event of a stream replaces the artifact (append=False) because it contains the accumulated content.

PiperOrigin-RevId: 966350716
2026-08-17 21:17:26 -07:00
chelsealong caac070837 fix: skip non-agent directories in AgentLoader.list_agents()
Merge https://github.com/google/adk-python/pull/6668

PiperOrigin-RevId: 966348937
2026-08-17 21:12:06 -07:00
Aarav Mittal 989721746a fix: use OAuth2 client-credentials scheme for OpenAPI SA helpers
Merge https://github.com/google/adk-python/pull/6660

Change service-account OpenAPI helpers to return an OAuth2 client-credentials scheme so CredentialManager can perform token exchange.

Also, bypass the credential service caching for all SERVICE_ACCOUNT credentials. This ensures we don't cache exchanged tokens that cannot be refreshed, but means token exchange will run on each tool execution if the manager/exchanger is not reused.

Fixes #6656

PiperOrigin-RevId: 966305100
2026-08-17 19:00:50 -07:00
Liang Wu 0b39e7280a fix(live): stop live runs from writing back into the caller's RunConfig
Two places in the live path wrote into configuration the caller still owns, so
a `RunConfig` came back out of a run holding fields the caller never set, and
a `RunConfig` reused for a later run carried them into it.

The basic request processor aliased two `RunConfig` sub-models straight into
`LiveConnectConfig` rather than copying them, and live request assembly then
mutates both while the session runs: `BaseLlmFlow.run_live` stamps every
server-issued handle onto `session_resumption` when it reconnects and sets
`transparent` there on a Vertex reconnect, and sets
`initial_history_in_client_content` on `history_config` when it seeds a fresh
connection with history. Deep copy both sub-models in `_build_basic_request`.
This matches the treatment `_copy_request_scoped_fields` already gives
`llm_request.config` in the same function, and for the same reason: request
assembly must not write through into configuration the caller still owns. An
absent sub-config stays `None` rather than becoming an empty object.

`Runner.run_live` filled in its AUDIO default by assigning to the caller's
`response_modalities`, so a config that expressed no preference came back out
of the run pinned to AUDIO, and a config reused for a later text run would ask
for audio. Write that default to a copy instead. The copy is shallow: deep
copying a `RunConfig` raises `TypeError: cannot pickle` when `http_options`
holds a live httpx client, and nothing there writes through into a sub-model.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 966257920
2026-08-17 16:56:30 -07:00
George Weale 1d0b7a197f test: allow the standard-library stand-ins Python 3.10 installs
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 966245462
2026-08-17 16:31:07 -07:00
George Weale e4f59004f7 fix: stop re-sending thought summaries as conversation history
ADK already treats a display-only thought part as invisible for LLM context, but
it only checks that for a whole event, so a summary that shares an event with a
function call or an answer is handed back to the model on every later request of
the session. The NL planning request processor now drops those parts from the
outbound request, keeping any thought that carries a thought signature, a
function call or response, or

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 966239711
2026-08-17 16:18:46 -07:00
George Weale 15e090da49 test(deps): require an inclusive dependency ceiling to name an installed release
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 966225997
2026-08-17 15:51:19 -07:00
DABH 8f85107cca fix: route workflow IDs and retry jitter through platform seams
Merge https://github.com/google/adk-python/pull/6468

PiperOrigin-RevId: 966214760
2026-08-17 15:29:13 -07:00
Liang Wu bf2efd71d6 feat: allow streaming tools to set response scheduling per yield
A streaming tool could only use one Live response scheduling mode for its
entire stream, taken from the tool-wide `response_scheduling`. A tool that
mixes quiet progress updates with an urgent alert had no way to say so.

A yielded `types.FunctionResponse` now carries the payload in `response` and
the mode for that one chunk in `scheduling`. Anything yielded plainly still
falls back to the tool-wide default, so existing tools are unaffected.

```python
async def monitor_threshold(query: str):
  # Adds to context without interrupting the model.
  yield types.FunctionResponse(
      response={'status': 'fetching data...'},
      scheduling=types.FunctionResponseScheduling.SILENT,
  )
  # Interrupts ongoing generation.
  yield types.FunctionResponse(
      response={'alert': 'threshold exceeded'},
      scheduling=types.FunctionResponseScheduling.INTERRUPT,
  )
  # Falls back to the tool-wide default.
  yield {'final_summary': 'done'}
```

Only `response` and `scheduling` are read from the yielded object. `id` and
`name` have to address the function call being answered, which a tool cannot
know, so ADK keeps owning them. The unwrapping runs before media extraction,
so a chunk that names a scheduling can still return inline media parts.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 966209467
2026-08-17 15:18:17 -07:00
Layau Eulizier Jr 9e9eaa69bd fix: reject tool confirmations arriving over A2A
Merge https://github.com/google/adk-python/pull/6462

A remote peer could self-approve a pending dangerous tool call via A2A because
inbound messages are treated as user role. This change ignores confirmations
arriving over A2A channels.

Closes #6461

PiperOrigin-RevId: 966208121
2026-08-17 15:15:34 -07:00
prasanna8585 25f53bdc81 fix: percent-encode path parameter values in RestApiTool
Merge https://github.com/google/adk-python/pull/6676

This prevents path parameter values from introducing new path segments, query strings, or fragments.

PiperOrigin-RevId: 966140356
2026-08-17 13:11:07 -07:00
George Weale 3c1b1bc236 fix: stop re-sending thought summaries as conversation history
ADK already treats a display-only thought part as invisible for LLM context, but
it only checks that for a whole event, so a summary that shares an event with a
function call or an answer is handed back to the model on every later request of
the session. The NL planning request processor now drops those parts from the
outbound request, keeping any thought that carries a thought signature, a
function call or response, or a server-side tool call. Stored sessions are
unchanged, so a caller that displays the model's reasoning still gets it.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 966074428
2026-08-17 11:12:56 -07:00
Ryota Yoda b8ea1e8eba fix: use Unicode-aware keyword extraction in InMemoryMemoryService
Merge https://github.com/google/adk-python/pull/5502

Fixes #5501

PiperOrigin-RevId: 966073869
2026-08-17 11:11:30 -07:00
Google Team Member 92d00aa937 feat: add update_data_agent tool to data_agent toolset
Implements the update_data_agent tool mimicking the MCP toolbox implementation,
allowing the updating of Gemini Data Agents.

PiperOrigin-RevId: 966070636
2026-08-17 11:06:04 -07:00
Liang Wu eac32c3ccc feat(live): use RunConfig.session_resumption.handle when opening a live session
A caller that resumes an earlier live session by passing a handle in
`RunConfig.session_resumption.handle` only got that handle onto the wire. The
basic request processor forwards it to `LiveConnectConfig`, but every other
part of the run keys off `InvocationContext.live_session_resumption_handle`,
which was populated exclusively from a server-issued
`session_resumption_update`. The run therefore behaved as if the session were
new: it replayed the whole conversation through `send_history()` even though
the server already held that state, declared that history as
`initial_history_in_client_content`, left `transparent` unset on the Vertex AI
backend, and raised instead of reconnecting when the socket dropped before the
server issued its first handle.

Seed `InvocationContext.live_session_resumption_handle` in
`BaseLlmFlow.run_live` from the handle request assembly has already put on
`llm_request.live_connect_config.session_resumption`, before the connect loop,
so the first connection is treated as a resumption in the same way as any
mid-session reconnect. Reading the assembled request rather than the
`RunConfig` keeps the seed on the same object the reconnect path goes on to
write, and honors a handle set by any request processor rather than only one
set through `RunConfig`. Agent transfer is unaffected because it already
clears both the invocation handle and the deep-copied run config handle, so a
child agent still starts a fresh live session.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 966066295
2026-08-17 10:58:39 -07:00