[AI-303] google_adk_agents: graph workflows, dynamic workflows, and durable HITL (ADK v2) - #1675
[AI-303] google_adk_agents: graph workflows, dynamic workflows, and durable HITL (ADK v2)#1675DABH wants to merge 12 commits into
Conversation
3d4be86 to
697e407
Compare
Graph/dynamic workflow support relies on Workflow-as-Tool (added in ADK 2.4.0) and HITL resumption for standalone nodes and NodeTool plus related resumable-mode hardening (added in ADK 2.5.0). Existing contrib tests pass unchanged against 2.5.0. The exclude-newer-package exemption can be removed once 2.5.0 (published 2026-07-16) passes the two-week cooldown.
…K v2 - activity_node() wraps a Temporal activity as an ADK FunctionNode for use in Workflow graphs and dynamic ctx.run_node() calls. - HITL helpers (HitlRequest, pending_hitl_requests, hitl_input_response, hitl_confirmation_response) cover ADK's pause/resume wire format so workflows can durably wait on human input via signals/updates. - setup_deterministic_runtime() additionally installs a workflow.random()- backed provider on ADK versions exposing the platform random seam. - README sections for graph workflows, dynamic workflows, durable HITL, and determinism notes.
ADK holds its platform time/uuid/random providers in ContextVars, and the public set_*_provider helpers only affect the calling context. Temporal executes workflow code on executor threads whose contexts never see the run_context call, so the deterministic providers were previously never active inside workflows: event/function-call ids came from stdlib uuid4 and Event timestamps from wall-clock time. This went unnoticed because Temporal replay compares command sequences, not payloads — but any flow whose control depends on a generated id (HITL resume matching recorded responses by interrupt/function-call id) diverged on replay and hung. Rebind each platform module's ContextVar with the deterministic provider as its default. Context-local set_*_provider calls still override it, and the providers fall back to real primitives outside workflows.
- Graph: sequential/conditional-routing/parallel-join graphs with activity-backed nodes, LlmAgent node through the invoke_model activity, node timeout (durable timer), ADK RetryConfig retry, and a seam-gated jittered-retry replay test. All run with max_cached_workflows=0 so every workflow task fully replays. - Dynamic: ctx.run_node loops and asyncio.gather fan-out, Workflow-as-Tool, and HITL resume proving completed activity children are served from the session cache (exactly one real execution). - HITL: human-input node resume via query + update, activity_tool behind FunctionTool(require_confirmation=True) with the gated activity running exactly once on approval and never on rejection (replay-proven via forced eviction), multiple pending requests with partial responses, and a seam-gated default-interrupt-id replay test. - Replay: recorded graph_workflow.json and hitl_workflow.json histories added to the replay regression test. Tests marked seam-gated skip on google-adk releases that predate the upstream platform-seam routing (google/adk-python PR) and run against a build that includes it.
697e407 to
41276f8
Compare
…d bindings) basedpyright fails CI on warnings: replace typing.Optional/typing.Mapping with PEP 604/collections.abc forms, drop two unused query-result bindings, and mark the intentionally-unused node_input parameters (the name is load-bearing for ADK FunctionNode binding) with targeted ignores.
41276f8 to
51dc7a5
Compare
# Conflicts: # pyproject.toml # temporalio/contrib/google_adk_agents/README.md # temporalio/contrib/google_adk_agents/_plugin.py # tests/contrib/google_adk_agents/test_adk_streaming.py
google-adk 2.8.0 (released 2026-08-26) is the first release containing the platform seams this plugin relies on: the random provider and the routing of RequestInput interrupt ids, _ToolNode function-call ids, and retry jitter through google.adk.platform (google/adk-python@8f85107c), plus the deterministic ParallelWorker failure selection (google/adk-python@d31b5e7d). Resolve google-adk from PyPI again and raise the extra's floor accordingly. The repo's exclude-newer window is two weeks, so a per-package exclude-newer-package override admits 2.8.0 until it ages into the window (2026-09-10); it can be removed after that.
There was a problem hiding this comment.
🟡 Changes recommended
Positional-only activity signatures are mishandled, and the temporary dependency cutoff remains committed.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds ADK v2 graph, dynamic workflow, and durable human-in-the-loop support to the Temporal Google ADK integration.
Changes:
- Adds
activity_nodeand HITL request/response helpers. - Installs deterministic ADK time, UUID, and random providers.
- Adds documentation, dependency updates, integration tests, and replay histories.
File summaries
| File | Description |
|---|---|
CHANGELOG.md |
Documents new ADK v2 capabilities and version floor. |
pyproject.toml |
Raises Google ADK minimum and adds a temporary cutoff override. |
uv.lock |
Locks Google ADK 2.8.0 and updated dependencies. |
temporalio/contrib/google_adk_agents/__init__.py |
Exports HITL APIs. |
temporalio/contrib/google_adk_agents/_hitl.py |
Implements HITL request parsing and response construction. |
temporalio/contrib/google_adk_agents/_plugin.py |
Installs deterministic platform providers. |
temporalio/contrib/google_adk_agents/workflow.py |
Adds activity-backed graph nodes. |
temporalio/contrib/google_adk_agents/README.md |
Documents graph, dynamic, and HITL workflows. |
tests/contrib/google_adk_agents/test_adk_graph_workflows.py |
Tests graph execution and replay safety. |
tests/contrib/google_adk_agents/test_adk_dynamic_workflows.py |
Tests dynamic nodes and resume behavior. |
tests/contrib/google_adk_agents/test_adk_hitl.py |
Tests durable input and confirmation flows. |
tests/contrib/google_adk_agents/test_google_adk_agents_replay.py |
Extends recorded-history replay coverage. |
tests/contrib/google_adk_agents/histories/graph_workflow.json |
Adds graph workflow replay history. |
tests/contrib/google_adk_agents/histories/hitl_workflow.json |
Adds HITL workflow replay history. |
Review details
- Files reviewed: 13/14 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # Admit google-adk 2.8.0 (released 2026-08-26) inside the 2-week window; drop after 2026-09-10. | ||
| exclude-newer-package = { google-adk = "2026-08-27T00:00:00Z" } |
| params = [ | ||
| p | ||
| for p in sig.parameters.values() | ||
| if p.kind | ||
| in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) | ||
| ] |
| ] = { | ||
| _REQUEST_INPUT_FUNCTION_CALL_NAME: "input", | ||
| _REQUEST_CONFIRMATION_FUNCTION_CALL_NAME: "tool_confirmation", | ||
| _REQUEST_CREDENTIAL_FUNCTION_CALL_NAME: "credential", |
There was a problem hiding this comment.
This exposes adk_request_credential as a pending HitlRequest, but neither response helper can actually resume it: hitl_input_response() emits adk_request_input and hitl_confirmation_response() emits adk_request_confirmation. ADK’s auth preprocessor only consumes a response whose name is exactly adk_request_credential, so an agent-auth flow discovered through this API remains unresolved if the caller follows the documented helpers. Could we add and export a hitl_credential_response() helper with an auth-resume test, or omit the credential kind until it is supported?
What was changed
ADK v2 moved to a graph runtime. Its scheduler is plain asyncio driven by session events, so it already runs deterministically inside a workflow with
TemporalModel, the MCP toolsets, andactivity_as_toolintercepting as before. This PR adds the missing pieces (AI-303):activity_node(...): run a Temporal activity as an ADKFunctionNode, either in aWorkflow(edges=...)graph or viactx.run_node(...).HitlRequest,pending_hitl_requests,hitl_input_response,hitl_confirmation_responsefor ADK'sadk_request_input/adk_request_confirmationpause-and-resume protocol. The durable wait itself is just workflow code: query the pending requests, wait on a signal or update, callrun_asyncagain. Works withactivity_as_toolbehindFunctionTool(require_confirmation=True).set_*_providercalls. Replay only compares commands, so nobody noticed until HITL, which matches responses on generated ids. The providers (time, uuid, and now random) are installed as ContextVar defaults instead.RequestInputids, node-tool call ids, and retry jitter throughgoogle.adk.platform; without that the fix above doesn't reach those call sites. The old git pin to ADK main is gone. 2.8.0 sits inside our two-weekexclude-newerwindow until Sept 10, so there's a temporaryexclude-newer-packageline in pyproject that I'll drop before merge.Testing
16 new tests (graph, dynamic, HITL) with
max_cached_workflows=0so every task replays, plus two recorded histories in the replay test. 71 passed, 5 skipped (env-gated) on 2.8.0. Lint clean.