Skip to content

fix(adk): carry donkey.last_call into after_model_callback on google-adk 2.13 - #1214

Merged
tommasobolis merged 1 commit into
developfrom
fix/1207-adk-last-call-bridge
Oct 25, 2026
Merged

tommasobolis merged 1 commit into
developfrom
fix/1207-adk-last-call-bridge

Conversation

@tommasobolis

@tommasobolis tommasobolis commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #1207

This PR intentionally leaves the framework-release issue open: framework-release-watch.yml shuts it once the lock moves to google-adk 2.13.0. This PR does not change constraints/adapter-contract-adk-py3.12.txt; the lock refresh does that.

Summary

  • Cause. google-adk 2.13.0 iterates the model's response generator in a copy of the flow's context (google.adk.utils._runner_utils._stream_in_caller_scope). The record the transport sets stays in that copy, so an after_model_callback read UNOBSERVED. That is why test_adk_runner_is_unobserved_but_after_model_callback_sees_it failed on 2.13.0.
  • Fix.
    • donkey.adk.gemini() and donkey.adk.model() now return a lazily defined subclass of Gemini / LiteLlm (GovernedGemini / GovernedLiteLlm, built by _last_call_bridged_class). The class is defined on first use, so google.adk is still imported lazily (§1.1). Strands' _governed_model_class is the precedent.
    • The subclass overrides only the public BaseLlm.generate_content_async, as a plain def. It opens a LastCallBridge in the flow's context before ADK copies that context, the same pattern as the langgraph bridge.
    • Hazard Epic: Pillar 1 — governed model access through the Omni Gateway LLM proxy #2. The bridge closes before the first complete (non-partial) response. ADK runs that response's function calls, in tasks of their own on 2.13, before it resumes the generator, so a tool's call never writes through the model call's bridge. Partial responses keep the bridge open.
    • isinstance against the ADK classes still holds, so verify_frameworks.py's expected-class check passes. The actual class it prints is donkey_kit.integrations.adk.GovernedGemini / GovernedLiteLlm.
  • Compatibility. Works on 2.4.0 (floor), 2.12.0 (current lock) and 2.13.0. No version ceiling (§8.4).
  • Docs (§0.3). Offline re-check entries for 2.13.0 are added to both §8 rows in docs/verified-apis.md (Google ADK; Google ADK (native Gemini)).
    • The native Gemini row's stale "client_kwargs replaces ... wholesale" wording is fixed. On 2.13, Gemini._with_adk_http_options fills only the api_version / base_url / retry_options the caller left unset, and merges ADK's tracking values into the caller's headers.
    • An offline MockTransport capture shows the wire is unchanged between 2.12.0 and 2.13.0 apart from the google-adk/<version> token.
    • website/content/frameworks/adk.mdx is updated to match, and the llms artifacts are regenerated.

Test plan

New tests in tests/unit/test_adapter_async_last_call.py:

  • test_adk_model_runner_is_unobserved_but_after_model_callback_sees_it: the Gemini Runner test, for model().
  • test_adk_streamed_run_after_model_callback_sees_every_response: an SSE Runner run, where every partial and the final callback read OBSERVED.
  • test_adk_bridge_closes_before_a_complete_response: a task started on a complete response records in its own context.
  • test_adk_factories_return_the_adk_classes: the isinstance check.

Fail before the fix, pass after:

Gate, run from python/:

  • nox -s typecheck-and-lint test-3.11: both succeed (ruff, ruff format --check, mypy --strict, lint-imports, vulture; test-3.11 green).
  • nox -s "adk-stacks(google-adk>=2.4)" "adk-stacks(google-adk==2.4.0)" "adapter-contract(adk)": all succeed.
    • The >=2.4 leg resolved google-adk 2.13.0: 87 passed, 73 skipped.
    • The 2.4.0 floor: 87 passed, 73 skipped.
    • adapter-contract(adk) on the 2.12.0 lock: 3468 passed, 228 skipped, and smoke_example.py adk passed.
  • The whole pytest -q on the PR lock with only google-adk moved to 2.13.0: 3468 passed, 228 skipped. scripts/smoke_example.py adk passed.
  • python scripts/verify_frameworks.py --only adk --emit-verified on 2.13.0 and 2.4.0, plus --only adk on 2.12.0: signature ok and expected ok for adk and adk.gemini. Live was not run.
  • nox -s docs-llms-drift docs-site: both succeed.
  • scripts/check_commit_identities.py origin/develop..HEAD: rc 0. The new-dependencies session needs a bare python on PATH and couldn't run locally; this diff doesn't touch pyproject.toml.

Not run: --live (no live round-trip on 2.13.0).

Pre-existing, not caused by this PR: offline, with the recorded responses.stream.* fixture, a streamed ADK Gemini call leaves donkey.last_call.input_tokens as None on 2.12.0 and 2.13.0, with or without the bridge. This is recorded in the §8 row. The adk page's "usage lands on the last one" for streaming is not confirmed by this offline check and may deserve a follow-up issue.

ADR needed?

  • No: this PR changes none of those.

Post-deploy steps

None. When the lock refresh moves adapter-contract-adk-py3.12.txt to google-adk 2.13.0, framework-release-watch.yml shuts the framework-release issue.

🤖 Generated with Claude Code

…adk 2.13 (#1207)

google-adk 2.13.0 iterates the model's response generator in a copy of
the flow's context (_stream_in_caller_scope), so the record the transport
set stayed in that copy and an after_model_callback read UNOBSERVED.

donkey.adk.gemini() and donkey.adk.model() now return a lazily defined
subclass of Gemini / LiteLlm (GovernedGemini / GovernedLiteLlm) whose
generate_content_async, a plain def, opens a LastCallBridge in the flow's
context before ADK copies it. The bridge closes before the first complete
(non-partial) response, because ADK runs that response's function calls,
in tasks of their own, before resuming the generator (hazard #2).
isinstance against the ADK classes still holds, so verify_frameworks.py's
expected-class check passes. Works on 2.4.0 (floor), 2.12.0 (lock) and
2.13.0; no version ceiling (§8.4).

Tests: model() Runner + after_model_callback, a streamed Runner run, a
task started on a complete response, and isinstance. The Runner tests
fail on 2.13.0 without the fix; the task test fails without the
close-before-complete step.

Docs (§0.3): verified-apis.md §8 Google ADK rows record the 2.13.0
offline re-check, and fix the native Gemini row's "replaces wholesale"
wording: 2.13's Gemini._with_adk_http_options merges ADK's tracking
headers into the caller's http_options and fills only unset fields.
adk.mdx updated to match; llms artifacts regenerated.

Refs #1207

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Oct 24, 2026
@tommasobolis
tommasobolis merged commit fb4f94e into develop Oct 25, 2026
49 checks passed
@tommasobolis
tommasobolis deleted the fix/1207-adk-last-call-bridge branch October 25, 2026 15:24
tommasobolis added a commit that referenced this pull request Oct 25, 2026
Picks up the google-adk 2.13 last_call fix (#1214) and the lock refreshes
(#1206, #1213); the test locks are recompiled on develop's with [a2a].

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant