Skip to content

feat(AIC-3412): add the Google ADK agents handler - #106

Open
andrewklatzke wants to merge 8 commits into
mainfrom
aklatzke/AIC-3412/add-google-adk
Open

andrewklatzke wants to merge 8 commits into
mainfrom
aklatzke/AIC-3412/add-google-adk

Conversation

@andrewklatzke

Copy link
Copy Markdown
Contributor

Summary

  • Adds launchdarkly-ai-google-adk-agents, a wildcard agent handler for Google ADK. Gemini Developer API is the default. Vertex is an explicit opt-in and never shares an API key with that path.
  • Non-Gemini models go through ADK's own LiteLLM adapter (provider/model). google-adk is not a declared dependency: 2.9.2 caps OpenTelemetry below this workspace's exporter pin, so production installs add google-adk[extensions] themselves.
  • Includes google_adk_graph and to_adk_agents. The native adapter runs the root, follows transfer_to_<target> when the model calls it, and emits ld.ai.graph plus $ld:ai:graph:* when a context is present.

Test plan

  • uv run mypy packages/google-adk-agents/src
  • uv run pytest packages/google-adk-agents/tests tests/test_cross_handler_parity.py
  • Live integration against a Google-backed AI Config (the monorepo scenario key is not confirmed Google-backed)
  • Confirm a Vertex opt-in call does not send an API key, and a failed Gemini call is not retried on Vertex

Made with Cursor

andrewklatzke and others added 6 commits September 23, 2026 13:52
ADK is a wildcard agent runtime, so the handler serves any provider. Gemini is the default transport and Vertex is an explicit opt-in.

Co-authored-by: Cursor <cursoragent@cursor.com>
…-google-adk

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	.release-please-manifest.json
#	.sdk_metadata.json
#	AGENTS.md
#	tests/test_cross_handler_parity.py
#	uv.lock
…nd emit graph:node in the Google ADK adapter

Co-authored-by: Cursor <cursoragent@cursor.com>
…span

Co-authored-by: Cursor <cursoragent@cursor.com>
@andrewklatzke
andrewklatzke marked this pull request as ready for review October 21, 2026 22:03

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

)
],
**options,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graph wrapper drops API key

Medium Severity

google_adk_graph forwards Vertex and model options to the handler factory but never takes api_key. A caller who passes api_key sends it to _graph, which only accepts handlers, tool_handlers, registry, and graph_judge, so the call raises TypeError instead of authenticating Gemini.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f347aa. Configure here.

)
spanlib.finish_model_span(
span, self.config, spanlib.span_usage_from_counts(prompt, candidates)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Model spans start after the call

Medium Severity

LaunchDarklyTelemetryPlugin opens and closes the chat span only in after_model_callback. The span never wraps the model call, so durations are near zero and a failed inference produces no chat span at all.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f347aa. Configure here.

def _message(text: str) -> Any:
return type(
"Content", (), {"role": "user", "parts": [type("Part", (), {"text": text})()]}
)()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Native graph sends dummy messages

High Severity

_run_agent builds new_message with anonymous type(...) objects instead of the handler’s genai Content/Part helpers. The real ADK runner and Gemini client expect google.genai.types.Content, so to_adk_agents can fail when ADK is actually installed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f347aa. Configure here.

model=str((config.get("model") or {}).get("name") or ""),
instruction=parse_template(str(config.get("instructions") or ""), variables),
tools=tools,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Native graph skips model transport

Medium Severity

to_adk_agents passes the raw model name string into LlmAgent and never uses the handler’s Gemini / Vertex / LiteLLM resolution. Vertex project and location are ignored, and non-Gemini models are not wrapped as provider/model LiteLLM ids.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f347aa. Configure here.

andrewklatzke and others added 2 commits October 21, 2026 14:13
…-google-adk

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	.release-please-manifest.json
#	.sdk_metadata.json
#	AGENTS.md
#	tests/test_cross_handler_parity.py
Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 5 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e58baa5. Configure here.

def to_adk_agents(graph_def: Any, **options: Any) -> _AdkAgents:
"""Compiles a LaunchDarkly agent graph into an ADK workflow."""
report_usage("google-adk-agents.toAdkAgents", PACKAGE_NAME, __version__)
return _AdkAgents(graph_def, options)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Native adapter ignores graph coroutine

High Severity

to_adk_agents stores its first argument and never awaits it, and it only accepts keyword options. Sibling adapters take (def_coro, opts) from resolve_graph(). That call is a TypeError, and a bare coroutine is treated as a graph with no root.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e58baa5. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants