3910 Commits

Author SHA1 Message Date
George Weale 2716ad55b8 fix(artifacts): reject rooted, drive-qualified and traversing artifact paths
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963696345
2026-08-12 15:35:50 -07:00
Adnan Vahora 0897bee6a0 fix(flows): inject transfer_to_agent tool for HITL confirmation resume
Merge https://github.com/google/adk-python/pull/5669

Closes #5633

Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/5669 from settler-av:fix/transfer-to-agent-confirmation 91a210dd7cd5b1dcbc5a98c7c5068c73701d6325
PiperOrigin-RevId: 963694454
2026-08-12 15:33:19 -07:00
chelsealong 374aab372a fix: add .adk/ to the .gitignore generated by adk create
Merge https://github.com/google/adk-python/pull/6649

Fixes #6647

PiperOrigin-RevId: 963678453
2026-08-12 15:00:36 -07:00
George Weale 4da8dd7f8b fix: lower oneOf to anyOf so a union schema reaches Gemini intact
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963674878
2026-08-12 14:53:18 -07:00
George Weale b8c099d37f docs: add artifact service unit guide
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963673192
2026-08-12 14:50:26 -07:00
George Weale cf42e866cd fix(samples): fail the maintenance run when issue discovery dies mid-page
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963672754
2026-08-12 14:49:15 -07:00
Shangjie Chen d64f1afb62 docs: add a test-file placement rule to the testing style guide
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 963670995
2026-08-12 14:46:05 -07:00
George Weale ac717091f6 chore(deps)!: move pyarrow out of the gcp extra
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963655242
2026-08-12 14:17:19 -07:00
George Weale 22f55462fd fix: lift file references and nested parts out of tool results
Close #2577

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963644081
2026-08-12 13:57:24 -07:00
George Weale a5f02820f0 chore: cover the pre-commit runner's handling of a missing hook tool
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963643310
2026-08-12 13:56:31 -07:00
Jason Zhang b0fff3f02d fix: validate urls before computer use navigate opens them
ComputerUseToolset passed the url the model supplied straight to the
browser driver, without checking it. Now navigate runs the same checks
load_web_page does. A url that fails returns an error to the model
instead of reaching the driver.

If the agent is meant to drive the browser against an internal host,
pass allow_private_network_access=True

Co-authored-by: Jason Zhang <jasoncz@google.com>
PiperOrigin-RevId: 963643277
2026-08-12 13:55:35 -07:00
George Weale 64dddf2bf3 fix(samples): repair integrations samples that no longer run against the current API
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963642287
2026-08-12 13:53:54 -07:00
George Weale fbeab00010 fix(memory): scope Vertex RAG retrieval to the requesting app and user
VertexAiRagMemoryService ran top-k retrieval across the whole configured
corpus and dropped the other tenants' contexts afterwards. The response filter
made the result correct, but ranking still competed against every app and user
in the corpus, so a busy corpus could crowd a caller's own memories out of the
top-k entirely, and foreign context was transferred only to be discarded. This
is a recall and data transfer problem, not a disclosure one: no memory
belonging to another app or user was ever returned to the caller.

search_memory now lists the corpus, keeps the files whose display name names
the requesting app and user, and passes those file ids to VertexRagStore so
ranking happens inside that set. When the caller owns no files, retrieval is
skipped and an empty response is returned. Callers can now see memories that
the previous ranking crowded out, so result counts can go up.

Scoping is best effort and adds no permission requirement, but it does add up
to 10 list calls to each search. Those calls run on the SDK async surface, so
they are awaited rather than blocking the event loop. Scoping is abandoned,
rather than applied to the files listed so far, whenever the listing cannot be
completed: either a listing failure, such as a deployment whose credentials
can retrieve but not list, or a corpus larger than the roughly 1000 files that
page budget covers. Retrieval then runs unscoped exactly as it did before,
which the response filter still makes correct, and the reason is logged.
Applying a partial listing would instead hide the caller's own memories.
Corpora past that size therefore keep the old ranking behavior permanently.

Server-side metadata filtering was considered and rejected: it matches on the
RagFile user_metadata field, which is output only on uploaded files and which
this service has never populated, so it would exclude every memory already
stored.

No public interface changes.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963641027
2026-08-12 13:51:53 -07:00
Shangjie Chen 1ad05439e0 fix(adk): share a single component-owner map between the triaging agents
The PR and issue triaging agents each kept their own copy of the component
-> owner map and they had drifted: the PR agent was missing several
components (skills, auth, bq, cli, integrations, workflow), and its
ALLOWED_LABELS gate -- which controls what the agent may apply -- was also
stale, so those labels could be neither applied nor assigned (a skills PR
was labeled "core" and assigned to the wrong owner).

Move the map into component_owners.py as the single source of truth and
import it verbatim as LABEL_TO_OWNER in both agents, so the two are always
identical and cannot drift. Derive the PR agent's ALLOWED_LABELS from
LABEL_TO_OWNER so a newly-owned component is allowed automatically.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 963611217
2026-08-12 12:57:22 -07:00
George Weale 40ccbeec6f fix: preserve turns when stale compaction loses
Concurrent session writers can append against a stale revision, and the different session backends surfaced that conflict inconsistently. A stale post-response compaction write could then fail a turn that had already completed. This raises a consistent StaleSessionError (still a ValueError subclass) across the database, SQLite, and Firestore services, and discards only the stale compaction summary while keeping the raw turns.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963599677
2026-08-12 12:34:52 -07:00
George Weale d8d8a6ef16 fix(samples): separate skipped from failed issues in the monitoring agent
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963583295
2026-08-12 12:02:55 -07:00
George Weale 30f32e3a95 fix: coerce non-string enum values to strings on string-typed Gemini schemas
Some MCP servers declare a string-typed field whose enum lists integer
values. Gemini requires enum members to match the declared string type, so
the tool declaration was rejected and the integration failed. Normalize enum
values to their string form when the effective (non-null) type is string,
leaving numeric enums on numeric types untouched.

Close #3401

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963579568
2026-08-12 11:55:43 -07:00
George Weale 899500510d fix: restrict builder YAML code references to the app being edited
Close #5292

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963578247
2026-08-12 11:53:35 -07:00
George Weale f324d1beed fix: select the mTLS endpoint only when a client certificate exists
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963560219
2026-08-12 11:24:18 -07:00
George Weale 1e051fc06f chore(deps): allow OpenTelemetry 1.43
Close #6421

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963558965
2026-08-12 11:22:18 -07:00
George Weale c840dbe991 fix(samples): make the MCP auth sample actually enforce auth, move MCP samples off the deprecated toolset name, and correct code execution claims
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963555685
2026-08-12 11:17:12 -07:00
George Weale 2353dde8e9 docs: add model registry unit guide
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963549916
2026-08-12 11:07:29 -07:00
Google Team Member 08bd589055 feat: update skill model to include its origin
Before, this information was lost after creating skill, but is needed for skill-related telemetry changes.

PiperOrigin-RevId: 963549069
2026-08-12 11:06:05 -07:00
Liang Wu 8b9d22228c fix: stop live tool execution from bypassing run_async
Live tool execution (`FunctionTool._call_live` and `__call_tool_live` in
flows/llm_flows/functions.py) called the wrapped function directly instead of
going through `BaseTool.run_async`. Every guardrail and preprocessing step the
async path applies was silently skipped in live sessions.

Remove `_call_live` and route live tool execution through `__call_tool_async` /
`tool.run_async`. Behavior changes that follow from the unification:

- A tool gated behind `require_confirmation` is no longer executed unattended in
  a live session. The check was previously skipped and the tool body ran; the
  call is now refused and a confirmation request is recorded. See the limitation
  below -- this is the request half only.
- Parameter preprocessing (Pydantic model coercion) now applies in live mode.
- `BaseTool` subclasses overriding `run_async` now execute polymorphically in
  live mode instead of being invoked as plain functions.
- A streaming tool that raises now returns an error FunctionResponse instead of
  leaving the live session waiting for a response that never arrives.
- `_get_mandatory_args` no longer counts `_ignore_params` (`tool_context`,
  `input_stream`) as mandatory, so schema generation and validation report only
  the parameters actually required from the caller.

Known limitation: human-in-the-loop confirmation is still not end-to-end in live
mode. The request is raised but cannot be answered, because the live flow never
emits an `adk_request_confirmation` function call, the confirmation request
processor only runs once before the live connection opens, and the live
execution path does not accept a `ToolConfirmation`. A confirmation-gated tool
therefore cannot be approved and resumed inside a live session. TODOs in the
code mark the sites that need to change; closing the loop is follow-up work.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 963548842
2026-08-12 11:04:52 -07:00
George Weale f4b432db11 fix(ci): stop CI resolving against half-published PyPI releases
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963546374
2026-08-12 11:01:09 -07:00
Kathy Wu 77d4647c8e fix: refuse MCP tools that take a reserved ADK tool name
`McpTool` registered under the verbatim name the remote server advertised, with
no check against the names the framework itself puts on the wire. A server that
advertised `adk_request_credential`, `adk_request_confirmation`,
`adk_request_input` or `transfer_to_agent` therefore had its own tool dispatched
in place of the framework's, so it could harvest the credentials meant for an
auth callback or route the conversation to an agent of its choosing.

`McpToolset.get_tools` now drops any tool carrying one of those four names and
logs that it did, and `McpTool.__init__` refuses the name outright. The listing
skips rather than raises because a single reserved name would otherwise fail the
whole `list_tools` call and take the server's honest tools down with it; the
constructor check is the backstop for anything that builds an `McpTool`
directly. Only exact matches are refused, so `transfer_to_agent_v2` still
registers.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 963544587
2026-08-12 10:58:01 -07:00
George Weale 5418b73156 fix(samples): report failed stale agent audits and exit non-zero
Close #6520

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963539924
2026-08-12 10:50:19 -07:00
George Weale b4dc92de57 test: assert the all extra stays the union of the runtime extras
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963527870
2026-08-12 10:31:20 -07:00
Xuan Yang 470d59e4a3 fix: prevent update_constraints.sh from rewriting files when dependencies are unchanged
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 963527637
2026-08-12 10:30:29 -07:00
George Weale bcce415ee8 fix(eval): grade judge metrics against the criterion's threshold
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963525519
2026-08-12 10:27:05 -07:00
Google Team Member b66cba2809 feat: Allow clients using the load_artifacts_tool to customize how attachment data is fed to the LLM
PiperOrigin-RevId: 963516890
2026-08-12 10:12:39 -07:00
Mukunda Rao Katta e03dbab2d4 fix(cli): normalize trigger user ids for sessions
Merge https://github.com/google/adk-python/pull/5402

## Summary
- normalize Pub/Sub subscription and Eventarc source metadata before reusing them as session user ids
- replace slash-separated resource paths with path-safe -- delimiters while preserving the full resource identity
- add trigger endpoint regression tests that verify the created sessions are stored under the normalized user ids

## Testing
- python3 -m py_compile src/google/adk/cli/trigger_routes.py tests/unittests/cli/test_trigger_routes.py
- python3 -m pytest tests/unittests/cli/test_trigger_routes.py -k "path_safe or with_subscription_metadata or source_from_ce_header" (fails during collection in this environment: ModuleNotFoundError: No module named 'fastapi')

Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/5402 from MukundaKatta:codex/trigger-user-id-path-safe a97467903bcadc92fc7a916ccef95638c58b3a65
PiperOrigin-RevId: 963505905
2026-08-12 10:02:07 -07:00
Google Team Member 6f18257117 ADK changes
Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/6544 from allen-stephen:feat/live-workflow-evals 1f4a15fe22cf7a9231fd808ca4835e7d30dce155
PiperOrigin-RevId: 963504694
2026-08-12 09:56:00 -07:00
Max Ind 7c71542392 test(telemetry): Split the functional test helpers into a package
Pure code motion, ahead of two changes that would otherwise be hard to read.
The one module holding all of the scaffolding becomes one module per concern:

  functional_test_helpers.py -> functional/_scenarios.py (what gets driven)
                                functional/_digests.py   (what it recorded)
                                functional/_recording.py (driving one case)
                                functional/_aclosing.py  (the aclosing check)

Everything else keeps the path it had, so a change in flight against the
cases, the tests or the goldens still applies.

Recording a case moves out of the tests and out of regenerate.py, which had
a copy each, into the one record_case() they now share.

No behaviour change: every golden is byte-identical to what it was, and
regenerating them all reproduces them exactly.

Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 963418069
2026-08-12 06:59:34 -07:00
Yi Liu a56f6e13ae fix(evaluation): validate path segments in GCS eval set/result managers
Reject `app_name`, `eval_set_id`, and `eval_set_result_id` values that
contain path separators, traversal segments, or null bytes before using
them to build GCS blob names in `GcsEvalSetsManager` and
`GcsEvalSetResultsManager`. Without this, a caller who controls these
identifiers on a shared evaluation bucket could compose a blob key that
addresses another app's eval sets or results.

This applies the same `_path_validation.validate_path_segment` guard that
already protects the local eval set/result managers, closing the gap for
the GCS-backed implementations.

Co-authored-by: Yi Liu <yiliuly@google.com>
PiperOrigin-RevId: 963199199
2026-08-11 21:56:57 -07:00
Haran Rajkumar 6ed484dc87 feat(labs/antigravity): allow mode='single_turn' AntigravityAgents to be sub-agents
Adds the mode field and relaxes the root-only restriction to depend on it.
mode='single_turn' makes LlmAgent.model_post_init's duck-typed lookup wrap this
agent in _SingleTurnAgentTool, so the parent calls it as an inline tool with a
request the parent composes, rather than transferring the conversation to it.
The field is a narrow Literal because AntigravityAgent is not an LlmAgent, so
LlmAgent's other modes have no meaning here; _managed_agent.py narrows it the
same way.

The root-only restriction exists because the SDK harness runs its own agent
loop and owns its own conversation, so it cannot take part in ADK's
multi-agent delegation. That is a statement about how the agent is invoked,
not about how it is configured: under mode='single_turn' the parent composes a
self-contained request, no session history has to reach the harness, and the
harness's conversation does not outlive the call. So the parent guard now
defers to mode, and mode is frozen so an adopted agent cannot be mutated back
into a state the guard would have rejected.

Giving the agent sub_agents stays blocked in every mode -- the harness would
never dispatch to an ADK child regardless of how the agent was invoked. The
two restrictions now raise separate messages, because a caller who passed
sub_agents cannot act on advice about mode.

Trajectory bookkeeping is skipped under single_turn, and config.save_dir is no
longer required, since it exists only to persist and resume trajectories.
Skipping it also fixes a latent bug: conversation_id is derived from the ADK
session, not the call, and _run_impl leaves the session unchanged, so without
this a second single-turn call in one session would find the first call's
trajectory and resume it -- silently, defeating the isolation the mode exists
to provide.

The two pre-existing run-path tests keep their assertions: the mode=None path
is unchanged. test_resumed_replayed_steps_are_skipped gains one assertion that
pins the persistence branch, which had no coverage before.

README gains a Single-Turn Sub-Agents section, and three pre-existing claims
are corrected: trajectory files are named by a sha256 digest rather than
'<session_id>_<agent_name>', save_dir is no longer unconditionally required,
and omitting it makes the SDK allocate a temporary directory per call that
nothing removes.

Co-authored-by: Haran Rajkumar <haranrk@google.com>
PiperOrigin-RevId: 963194529
2026-08-11 21:44:21 -07:00
Haran Rajkumar fd7df0e75b fix(labs/antigravity): report node input and output from AntigravityAgent
AntigravityAgent inherits BaseAgent._run_impl, the default adapter that lets
an agent run as a workflow node. That adapter leaves two gaps for any node
whose caller passes an input and reads an output:

- input: it accepts node_input and discards it, so a caller's composed request
  is silently replaced by the original end-user message. This override threads
  it into user_content, as ManagedAgent does.
- output: it never sets event.output, and the node runner reads results only
  from event.output or node_info.message_as_output with no fallback to event
  content, so the caller receives None.

Output is the last complete response, not the first: the SDK emits one per
model turn between tool calls, so RemoteA2aAgent's first-event promotion would
return the model's opening remark. Matches AgentTool's last_content behavior.
It is emitted on a trailing event rather than promoted in place, because which
response is final is unknowable until the stream ends and Context.output
raises on a second assignment.

A completed run that produced no model text emits output='' rather than
skipping the event. A text-less run is a real outcome -- a cancelled turn drops
its SYSTEM_MESSAGE step and yields nothing -- and returning None there would be
indistinguishable from the framework gap this override exists to close.
AgentTool makes the same choice with 'last_error_message or ""'. A run that
fails still raises and emits no output event.

No behavior change for an AntigravityAgent run as a root agent, which is the
only way it can be used until the next CL in this stack.

Co-authored-by: Haran Rajkumar <haranrk@google.com>
PiperOrigin-RevId: 963172936
2026-08-11 20:41:16 -07:00
Google Team Member dd0de5229f feat(agent): add native task mode support to root LlmAgent
- Support mode="task" in the root LlmAgent to register task completion tools.
- Document the A2A runner/executor completion contract where task-mode agents trigger termination via the finish_task tool.
- Add tests verifying that A2aAgentExecutor publishes COMPLETED task status upon receiving finish_task event from root task-mode agent.

PiperOrigin-RevId: 963135460
2026-08-11 18:58:12 -07:00
Google Team Member aec7aa33c8 fix(a2a): flatten human-input responses on resume to avoid mixing them with text
RemoteA2aAgent now rewrites human-input pause responses (adk_request_input,
adk_request_confirmation, adk_request_credential, and the mock input/auth calls)
to text before forwarding them to a remote agent on resume, matched by the
function call name. This stops Runner._validate_new_message from rejecting a
resumed message that mixes function responses with text. Credential (AuthConfig)
payloads are dropped instead of forwarded, and real long-running tool responses
are preserved so the peer can resume them by id.

PiperOrigin-RevId: 963124368
2026-08-11 18:29:16 -07:00
Xuan Yang d8f03153e9 chore: Move test_local_environment to mirror its source directory
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 963123620
2026-08-11 18:27:07 -07:00
George Weale 4cad8cc958 docs: correct the list of built-in agent skills
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963115427
2026-08-11 18:06:52 -07:00
Kathy Wu 3f21e891d7 fix(skills): load binary references and assets as bytes instead of skipping them
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 962989008
2026-08-11 14:06:14 -07:00
Surajit Nandi aa9c187f46 fix(cli): stream_reasoning_engine raises StopIteration RuntimeError on sync generators
Merge https://github.com/google/adk-python/pull/6114

## Link to Issue or Description of Change
Closes : #6093
  **Problem:**
  On Agent Engine deployments served by the ADK API server, every call to the
  `/api/stream_reasoning_engine` route with a synchronous streaming `class_method`
  (e.g. `stream_query`) ends with `RuntimeError: coroutine raised StopIteration`
  after the last chunk is streamed.

  The cause is the sync-to-async adapter `_aiter_from_iter` in
  `src/google/adk/cli/fast_api.py` (lines 916–922 in v2.2.0):

      async def _aiter_from_iter(iterator):
        while True:
          try:
            chunk = await run_in_threadpool(next, iterator)
            yield chunk
          except StopIteration:
            break

  The `except StopIteration` is unreachable. When the iterator is exhausted,
  `next()` raises `StopIteration` inside the worker thread, anyio sets it on a
  future, and it propagates out of the `run_in_threadpool` coroutine frame.
  Python (PEP 479) forbids `StopIteration` escaping a coroutine and converts it
  to `RuntimeError("coroutine raised StopIteration")` before the `except` clause
  ever sees it.

  **Affected versions:** Regression introduced in v2.2.0 — the route and the
  buggy adapter were added in the same commit. Not present in the v1.x line
  (verified absent at v1.35.0).

  **Solution:**
  Stop relying on `StopIteration` crossing the await boundary; use a sentinel
  default so iterator exhaustion never raises across it:

      _SENTINEL = object()

      async def _aiter_from_iter(iterator):
        while True:
          chunk = await run_in_threadpool(next, iterator, _SENTINEL)
          if chunk is _SENTINEL:
            break
          yield chunk

  This is the minimal, idiomatic fix; the stream now terminates cleanly when the
  sync generator is exhausted.

  ## Testing Plan

  **Unit Tests:**

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

  Added `test_gemini_stream_reasoning_engine_sync_generator` plus a
  `test_app_with_gemini_enterprise_sync_stream` fixture in
  `tests/unittests/cli/test_fast_api.py`. The pre-existing stream test used an
  *async* generator (the `isasyncgenfunction` branch) and never exercised the
  buggy sync-generator path. The new test fails on the unpatched code with
  `RuntimeError` and passes with the fix.

  pytest summary:

      $ pytest tests/unittests/cli/test_fast_api.py -k stream_reasoning_engine -q
      3 passed, 79 deselected

      $ pytest tests/unittests/cli/test_fast_api.py -q
      82 passed

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

  The failure and the fix reproduce standalone in ~15 lines, independent of any
  model or deployment:

      import asyncio
      from starlette.concurrency import run_in_threadpool

      async def _aiter_from_iter(iterator):  # old, buggy version
          while True:
              try:
                  chunk = await run_in_threadpool(next, iterator)
                  yield chunk
              except StopIteration:
                  break

      async def main():
          def gen():
              yield 1
              yield 2
          async for c in _aiter_from_iter(gen()):
              print("chunk:", c)

      asyncio.run(main())
      # chunk: 1
      # chunk: 2
      # RuntimeError: coroutine raised StopIteration   <-- before the fix

  With the sentinel version above, the same script prints the two chunks and
  exits cleanly with no exception. Originally observed on a live Vertex AI Agent
  Engine deployment (google-adk==2.2.0, Python 3.11) where every `stream_query`
  call logged the RuntimeError after the final chunk.

  ## Checklist

  - [x] I have read the 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.
  - [ ] Any dependent changes have been merged and published in downstream modules.

  ## Additional context

  Original server traceback:

      ERROR:    Exception in ASGI application
      Traceback (most recent call last):
        File ".../starlette/responses.py", line 250, in stream_response
          async for chunk in self.body_iterator:
        File ".../google/adk/cli/fast_api.py", line 797, in json_generator
          async for chunk in output:
        File ".../google/adk/cli/fast_api.py", line 919, in _aiter_from_iter
          chunk = await run_in_threadpool(next, iterator)
        File ".../starlette/concurrency.py", line 32, in run_in_threadpool
          return await anyio.to_thread.run_sync(func)
        File ".../anyio/to_thread.py", line 63, in run_sync
          return await get_async_backend().run_sync_in_worker_thread(
        File ".../anyio/_backends/_asyncio.py", line 2518, in run_sync_in_worker_thread
          return await future
      RuntimeError: coroutine raised StopIteration

  Occurs 100% of the time on every sync streaming request once the generator is
  exhausted. The bug is model-agnostic (purely in the FastAPI streaming adapter).

Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/6114 from surajit-1306:fix/stream-reasoning-engine-stopiteration e5ee866074fefc56418ec03441e3706617f9d755
PiperOrigin-RevId: 962875380
2026-08-11 10:54:24 -07:00
Max Ind f4fd7d5db9 test(telemetry): add property based tests for metrics export
Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 962669975
2026-08-11 02:59:24 -07:00
nikkie 76027ddb2f feat(evaluation): add optional eval set result persistence to AgentEvaluator
Merge https://github.com/google/adk-python/pull/4414

**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):**

- Related: #4410
- Fixes: #2602

**Problem:**
`AgentEvaluator.evaluate()` did not support built-in eval set result persistence, making it harder to reuse the same workflow as CLI/Web paths that already use `EvalSetResultsManager`.
Also, introducing new parameters in the middle of method signatures would break positional-argument compatibility for existing users.

**Solution:**
This PR adds optional eval result persistence to `AgentEvaluator` while preserving backward compatibility:

- Add optional parameters to `AgentEvaluator.evaluate()` and `AgentEvaluator.evaluate_eval_set()`:
  - `app_name: Optional[str] = None`
  - `eval_set_results_manager: Optional[EvalSetResultsManager] = None`
- Persist results per eval set (a single save aggregating all `EvalCaseResult`s), aligning `AgentEvaluator` with existing CLI/Web/API (`LocalEvalService`) persistence behavior.
- Resolve `app_name` from explicit input first, then derive from `agent_module` (including `.agent` suffix handling).
- Save results before failure assertion so failed eval runs still leave artifacts for inspection.
- Keep existing positional argument behavior by appending new parameters at the end of public method signatures.
- Add/extend tests to verify:
  - explicit and derived `app_name`
  - save-on-failure behavior
  - argument propagation from `evaluate()` to `evaluate_eval_set()`
  - positional-argument backward compatibility
- Add an integration usage example for `app_name` omission with `LocalEvalSetResultsManager`.
- For multi-run evals, all runs and eval cases are aggregated into a single result file per eval set (each run contributes one `EvalCaseResult`).

### Testing Plan

**Unit Tests:**

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

```
% pytest tests/unittests/evaluation

======================== 357 passed, 169 warnings in 9.68s =========================
```

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

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

======================== 1 passed, 14 warnings in 5.24s ========================
```

Verify a result file is created under: `<tmp_path>/<derived_app_name>/.adk/eval_history/*.evalset_result.json` (e.g., 1 file containing 2 `EvalCaseResult`s when num_runs=2 on a single-case eval fixture).
This is helpful for debugging failed integration tests.

### 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 PR intentionally preserves public API positional compatibility by appending new optional parameters at the tail of method signatures.
- A generated local eval result JSON file may exist in the working tree from manual verification and is intentionally not part of the code change.

Co-authored-by: Yi Liu <yiliuly@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4414 from ftnext:agent-evaluator-save-evalset-result 873973e549c0a4b25b83e1ef81e1b4148ab4e379
PiperOrigin-RevId: 962597058
2026-08-10 23:52:52 -07:00
George Weale 74e7167d13 fix(ci): run the update-constraints hook in check mode
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 962546379
2026-08-10 21:28:52 -07:00
Rayan Dasoriya bc2c97cbdf fix: Key directory-loaded skill resources with forward slashes
PiperOrigin-RevId: 962465660
2026-08-10 17:44:35 -07:00
George Weale 3df5a6519a docs: add Session unit guide
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 962458183
2026-08-10 17:25:52 -07:00
George Weale 461205c8ba feat: accept a pre-configured client on the labs OpenAI model
Close #4180

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 962427806
2026-08-10 16:20:39 -07:00
Haiyuan Cao 04b8b72709 feat(plugins): add BigQuery Agent Analytics delivery and termination observability
Add an unconditional per-row event_id assigned before enqueue so Storage Write
API retry duplicates are identifiable, and add an opt-in exactly_once_delivery
mode that uses one loop-local committed stream with explicit offsets, sticky
ambiguous-send state, an offset_conflict drop bucket, and non-blocking stream
rotation. Expose finish_reason and sanitized error_message on final LLM
responses only, so progressive SSE does not double count. Emit NODE_OUTPUT and
NODE_ERROR for final workflow-node results while keeping model finish and block
diagnostics classified as LLM_RESPONSE. Remove the dead module-level
OpenTelemetry tracer allocation.

Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 962420983
2026-08-10 16:07:31 -07:00