Skip to content

feat: add HITL approval gateway for production ADK agent workflows#110

Open
garythomasgeorge wants to merge 3 commits intogoogle:mainfrom
garythomasgeorge:feat/hitl-approval-gateway
Open

feat: add HITL approval gateway for production ADK agent workflows#110
garythomasgeorge wants to merge 3 commits intogoogle:mainfrom
garythomasgeorge:feat/hitl-approval-gateway

Conversation

@garythomasgeorge
Copy link
Copy Markdown

@garythomasgeorge garythomasgeorge commented Mar 24, 2026

Summary

Closes #111
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.

Problem

ADK's Tool Confirmation (v1.14.0+) is experimental and has three blockers for production use:

  1. Does not support DatabaseSessionService or VertexAiSessionService
  2. Does not trigger inside AgentTool or across A2A boundaries
  3. No structured approval UI, audit trail, or persistence layer

Validated by community issues: #1797, #1851, #2645, #3276, #3567 on google/adk-python.

Solution

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.

What's included

Core module (src/google/adk_community/tools/hitl/)

  • gateway.pyhitl_tool decorator that wraps any async function before it is passed to FunctionTool. Adding HITL to an existing tool takes ~5 lines.
  • models.pyApprovalRequest Pydantic model, normalised data contract capturing agent context, payload, risk level, and audit metadata
  • adapters/adk1.py — ADK 1.x adapter translating request_confirmation() events into ApprovalRequest objects

Service (src/google/adk_community/services/hitl_approval/)

  • api.py — FastAPI application
  • routes.py — REST endpoints for approval queue management
  • store.py — SQLite persistence with full audit log

Sample (contributing/samples/hitl_approval/)

  • credit_agent/agent.py — Credit approval agent demonstrating end-to-end integration
  • dashboard/app.py — Reference Streamlit approval inbox UI
  • start_servers.sh — One-command startup for all three services
  • requirements.txt — Sample-only dependencies

Architecture

ADK Agent Pipeline
      ↓
@hitl_tool decorator (wraps async function → FunctionTool)
      ↓ POST /approvals/ — creates ApprovalRequest
FastAPI + SQLite (approval state)
      ↓ serves pending approvals
Streamlit Dashboard (reviewer decides)
      ↓ POST /approvals/{id}/decide
FastAPI updates status in SQLite
      ↓ decorator polls GET /approvals/{id} every 2 s
Agent resumes execution (wrapper unblocks; runs tool if approved)

Forward compatibility

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.

Testing

Unit tests

All 11 tests passing:

============================= test session starts =============================
platform darwin -- Python 3.11.15, pytest-9.0.2, pluggy-1.6.0
rootdir: /Users/garythomasgeorge/Desktop/Work/AI Dev/adk-python-community
configfile: pyproject.toml
plugins: anyio-4.12.1, asyncio-1.3.0
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collected 11 items                                                                  

tests/unittests/tools/test_hitl_gateway.py ......                             [ 54%]
tests/unittests/services/test_hitl_approval_api.py .....                      [100%]

================================ 11 passed in 1.76s =================================
Exit code: 0

Manual E2E

Full end-to-end flow verified:

  • Agent triggers approval request → appears in Streamlit dashboard ✓
  • Reviewer approves in dashboard → agent resumes correctly ✓
  • Uvicorn restart → SQLite persists previous approvals ✓
ADK_HITL.Demo.webm

Testing plan

For reviewers wanting to reproduce locally:

cd contributing/samples/hitl_approval
uv pip install -r requirements.txt
./start_servers.sh

Then open:

  • ADK Dev UI: http://localhost:8080
  • Streamlit dashboard: http://localhost:8501
  • FastAPI docs: http://localhost:8000/docs

Trigger an approval by asking the credit agent to process an amount over $500.

Notes for reviewers

  • Opening as Draft — happy to address structural feedback before requesting full review
  • ADK 2.0 adapter (adapters/adk2.py) is planned as a follow-up PR once 2.0 moves toward stable
  • 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

Related

- Add ADK 1.x adapter and data models
- Add FastAPI backend and SQLite state management routes
- Add Streamlit dashboard UI for human reviewers
- Add sample credit_agent showcasing the full @hitl_tool integration
- Fully formatted under pyink and isort conventions
@garythomasgeorge garythomasgeorge marked this pull request as ready for review April 30, 2026 01:57
@DeanChensj
Copy link
Copy Markdown
Collaborator

@gemini-cli /review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

🤖 Hi @DeanChensj, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Overall, this is a very solid and well-architected contribution. The decoupling of the HITL state from the agent session is a smart move for production resilience.

I've left a few minor comments regarding:

  1. Python 3.12+ deprecations (utcnow).
  2. Configurable API endpoints for production.
  3. Potential simplifications using Pydantic's built-in ORM support.
  4. Considerations for polling traffic.

Great work on the sample agent and the Streamlit dashboard as well!

Comment thread src/google/adk_community/tools/hitl/models.py
Comment thread src/google/adk_community/tools/hitl/gateway.py Outdated
Comment thread src/google/adk_community/services/hitl_approval/routes.py Outdated
Comment thread src/google/adk_community/tools/hitl/gateway.py Outdated
- 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
@garythomasgeorge
Copy link
Copy Markdown
Author

@DeanChensj — addressed all four points from the automated review in the latest commit. 38/39 tests passing — the one remaining failure (test_session_state_management) is pre-existing in the Redis session service and fails on main as well [attach screenshot]. Ready for human review when you have a chance.
Screenshot 2026-05-05 at 6 37 29 PM

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.

Proposal: Production-Ready Human-In-The-Loop (HITL) Gateway

2 participants