You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Replace datetime.utcnow() with datetime.now(timezone.utc) throughout
(deprecated in Python 3.12+)
- Make API_BASE_URL configurable via ADK_HITL_API_URL env var
- Make poll interval configurable via ADK_HITL_POLL_INTERVAL_S env var
- Add jitter to polling loop to reduce backend traffic under
concurrent load
- Simplify _to_pydantic() using Pydantic v2 model_validate() with
from_attributes=True and a model_validator to handle JSON strings
- Update test_approved_tool_runs assertion to match enriched return
dict (action_result + supervisor_decision)
- Add README.md with architecture diagram, quick start, and
configuration reference
Adds a production-ready Human-in-the-Loop approval gateway for Google ADK agents. This addresses a documented gap where ADK's built-in Tool Confirmation feature explicitly does not support `DatabaseSessionService` or `VertexAiSessionService` — the two session backends required for production deployments — making structured human oversight unavailable in any persistent production environment.
4
+
5
+
## Problem
6
+
ADK's Tool Confirmation (v1.14.0+) is experimental and has three blockers for production use:
7
+
1. Does not support `DatabaseSessionService` or `VertexAiSessionService`
8
+
2. Does not trigger inside `AgentTool` or across A2A boundaries
9
+
3. No structured approval UI, audit trail, or persistence layer
10
+
11
+
Validated by community issues: #1797, #1851, #2645, #3276, #3567 on `google/adk-python`.
12
+
13
+
## Solution
14
+
A session-agnostic HITL approval gateway that manages approval state in its own persistence layer (SQLite, with a documented path to Postgres), independent of ADK's session service. The agent resumes via ADK's standard REST API after a human decision is submitted.
-`gateway.py` — `hitl_tool` decorator that wraps any async function before it is passed to `FunctionTool`. Adding HITL to an existing tool takes ~5 lines.
20
+
-`models.py` — `ApprovalRequest` Pydantic model, normalised data contract capturing agent context, payload, risk level, and audit metadata
-`start_servers.sh` — One-command startup for all three services
32
+
-`requirements.txt` — Sample-only dependencies
33
+
34
+
### Architecture
35
+
```
36
+
ADK Agent Pipeline
37
+
↓
38
+
@hitl_tool decorator (wraps async function → FunctionTool)
39
+
↓ POST /approvals/ — creates ApprovalRequest
40
+
FastAPI + SQLite (approval state)
41
+
↓ serves pending approvals
42
+
Streamlit Dashboard (reviewer decides)
43
+
↓ POST /approvals/{id}/decide
44
+
FastAPI updates status in SQLite
45
+
↓ decorator polls GET /approvals/{id} every 2 s
46
+
Agent resumes execution (wrapper unblocks; runs tool if approved)
47
+
```
48
+
49
+
### Forward compatibility
50
+
Built with an adapter pattern so the same approval backend and dashboard work with ADK 1.x today and ADK 2.0's `RequestInput` pattern when it reaches stable — without teams needing to rebuild their approval layer on upgrade.
51
+
52
+
## Testing
53
+
### Unit tests
54
+
All 11 tests passing:
55
+
```text
56
+
============================= test session starts =============================
57
+
platform darwin -- Python 3.11.15, pytest-9.0.2, pluggy-1.6.0
> 🎥 *Please drag-and-drop your `hitl_demo_video_1774318429041.webp` file here before publishing*
78
+
79
+
## Testing plan
80
+
For reviewers wanting to reproduce locally:
81
+
```bash
82
+
cd contributing/samples/hitl_approval
83
+
uv pip install -r requirements.txt
84
+
./start_servers.sh
85
+
```
86
+
87
+
Then open:
88
+
- ADK Dev UI: `http://localhost:8080`
89
+
- Streamlit dashboard: `http://localhost:8501`
90
+
- FastAPI docs: `http://localhost:8000/docs`
91
+
92
+
Trigger an approval by asking the credit agent to process an amount over $500.
93
+
94
+
## Notes for reviewers
95
+
- Opening as **Draft** — happy to address structural feedback before requesting full review
96
+
- ADK 2.0 adapter (`adapters/adk2.py`) is planned as a follow-up PR once 2.0 moves toward stable
97
+
- Confirmed structure placement from proposal issue: `tools/hitl` for the gateway and models, `services/hitl_approval` for the FastAPI backend — let me know if you'd prefer a different organisation
Copy file name to clipboardExpand all lines: contributing/samples/hitl_approval/README.md
+38-18Lines changed: 38 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,19 +5,20 @@ A drop-in **production-ready Human-in-the-Loop (HITL) approval middleware** for
5
5
## The Problem Solved
6
6
7
7
ADK 1.x ships with an experimental `require_confirmation=True` feature that handles pausing the LLM loop for human verification. However, it is fundamentally built for local debugging and introduces major blockers to an enterprise environment:
8
+
8
9
1.**Incompatible with Persistent Sessions:** Native confirmations intentionally do not serialize well and will completely fail to resume your agent if you use `DatabaseSessionService`, `SpannerSessionService`, or `VertexAiSessionService` (the mandatory session backends for production deployments).
9
10
2.**Single-Agent Limitations:** They silently break across `AgentTool` nested bounds and true multi-agent (A2A) topologies, causing missing events or infinitely looping models.
10
-
3.**No Resilient Audit Log:** The Native confirmation tool leaves no easily queryable paper trail linking the human supervisor to a precise LLM request.
11
+
3.**No Resilient Audit Log:** The native confirmation tool leaves no easily queryable paper trail linking the human supervisor to a precise LLM request.
11
12
12
-
*This project is the production implementation of the HITL pattern covered in the [ADK Multi-Agent Patterns Guide (Advent of Agents Day 13)](#).*
13
+
*This project is the production implementation of the HITL pattern covered in the [ADK Multi-Agent Patterns Guide (Advent of Agents Day 13)](https://medium.com/@garythomasgeorge/why-google-adks-human-in-the-loop-story-has-a-production-gap-and-one-way-it-could-be-fixed-66aabef33a32).*
13
14
14
15
## What This Library Provides
15
16
16
17
This project solves the production gaps by explicitly decoupling the human approval payload from ADK's internal session memory. It introduces a session-agnostic REST API layer using an Adapter pattern.
@@ -37,24 +38,42 @@ This project solves the production gaps by explicitly decoupling the human appro
37
38
38
39
By retaining HITL state inside an independent FastAPI engine and SQLite database, an active agent can pause safely. When a human supervisor hits "Approve" inside a centralized web portal hours later, the middleware simply posts the decision back into the agent's `/run_sse` stream seamlessly.
39
40
41
+
## Configuration
42
+
43
+
| Environment Variable | Default | Description |
44
+
|---|---|---|
45
+
|`ADK_HITL_API_URL`|`http://localhost:8000`| URL of the HITL approval FastAPI backend. Override for Cloud Run or any remote deployment. |
46
+
|`ADK_HITL_POLL_INTERVAL_S`|`2.0`| Base polling interval in seconds. Up to 1s of random jitter is added automatically to reduce backend traffic under concurrent load. |
This repository acts as the production baseline for a contact center or enterprise orchestration grid. Once deployed to staging, consider swapping out:
85
-
***Storage Layer:** Replace the local `SQLite` engine in `app/api/store.py` with `PostgreSQL` or `Cloud Spanner`.
86
-
***Proactive Notification:** Hook the FastAPI `POST /approvals/` route into Slack, PagerDuty, or Microsoft Teams to actively ping channels when a high-risk request pops up.
87
-
***Remove Streamlit:** Bypass the Streamlit frontend completely and point your existing support portal interface (like Salesforce Service Cloud) directly to `GET /approvals/pending` and `POST /approvals/{id}/decide`.
104
+
105
+
-**Storage Layer:** Replace the local `SQLite` engine in `app/api/store.py` with `PostgreSQL` or `Cloud Spanner`.
106
+
-**Proactive Notification:** Hook the FastAPI `POST /approvals/` route into Slack, PagerDuty, or Microsoft Teams to actively ping channels when a high-risk request pops up.
107
+
-**Remove Streamlit:** Bypass the Streamlit frontend completely and point your existing support portal interface (like Salesforce Service Cloud) directly to `GET /approvals/pending` and `POST /approvals/{id}/decide`.
88
108
89
109
## ADK 2.0 Compatibility
90
110
91
-
This project currently uses ADK 1.x conventions and event triggers. Because it strictly implements an `adapters` layer, all the Pydantic API schemas and Streamlit logic are completely forward-compatible with ADK 2.0 `RequestInput` workflow yielding. You'll simply need to switch the adapter layer translation once ADK 2.0 exits Alpha.
111
+
This project currently uses ADK 1.x conventions and event triggers. Because it strictly implements an `adapters` layer, all the Pydantic API schemas and Streamlit logic are completely forward-compatible with ADK 2.0 `RequestInput` workflow yielding. You'll simply need to switch the adapter layer translation once ADK 2.0 exits Alpha. The `ADK_HITL_API_URL` and `ADK_HITL_POLL_INTERVAL_S` environment variables remain valid across both adapter versions.
0 commit comments