Docs Frameworks

Google ADK

Status: verified. The adapter ships in kiff-guard and passes the conformance suite. The seam was verified against the real google-adk package.

Shape: vote (inverted-control). ADK runs the tool itself; its before_tool_callback only votes — return None to allow, return a dict to block (the dict becomes the tool result). So the guard uses observe() / decide_only(), not a run callback. See how the guard works.

Install

pip install "git+https://github.com/kiffhq/kiff-guard.git#subdirectory=packages/python/kiff-guard"
# plus Google ADK, the framework you're guarding:
pip install google-adk

Observe — audit with zero config

from google.adk.agents import LlmAgent
from kiff_guard import Guard
from kiff_guard.adapters.google_adk import kiff_before_tool_callback

guard = Guard(mode="observe")     # no client, no tenant needed

agent = LlmAgent(
    model=...,
    tools=[refund_order, send_email],
    before_tool_callback=kiff_before_tool_callback(guard),
)

Run the agent as usual, then read the trail:

for r in guard.receipts:
    print(r.state, r.tool, r.outcome)

Activate

Derive a starter domain from what you observed, review it, and activate it in the dashboard:

from kiff_guard import export_yaml
print(export_yaml("my-domain", guard.catalog))

Enforce — govern at runtime

Once you have a tenant and an active domain, point the guard at KIFF and switch to enforce. Only the guard changes:

from kiff_guard import Guard, HTTPClient, ToolMap
from kiff_guard.adapters.google_adk import kiff_before_tool_callback

client = HTTPClient(
    api_key="kiff_live_...",                       # mint in the dashboard
    tool_map=ToolMap().bind(
        "refund_order", action="REFUND_ORDER",
        entity_type="Order", entity_arg="order_id"),
)
guard = Guard(client=client, tenant="<tenant>", agent="support", mode="enforce")

agent = LlmAgent(model=..., tools=[refund_order],
                 before_tool_callback=kiff_before_tool_callback(guard))

In enforce mode a withheld decision returns a dict to ADK, so the tool is skipped and the reason is handed back to the model as the result. An allowed decision returns None and the tool runs. On a transport error the guard fails closed (blocks) by default.

The seam (verified)

ADK calls before_tool_callback(tool, args, tool_context) before a tool’s run_async. Returning None runs the tool; returning a dict skips it and uses that dict as the result — the block path.