StiggAdkPlugin governs a Google ADK graph. Add it to a Runner’s plugins, and ADK runs it for every agent the Runner reaches, including sub-agents. Each model call and paid tool is then checked before it runs, and its usage is reported as the agent that made it. For how Stigg governs a team of agents and what it’s used for, see Agent frameworks.
Quick start
You need a Google ADK app, a Stigg server API key, and a credit type in Stigg, herecredits, that your customers have.
1
Install the SDK
@google/adk 2.x is an optional peer dependency. The plugin uses the copy your app already has.2
Create the Stigg client
Create it once, when your server starts:
3
Create the plugin
Tell the plugin what to check each step against, and how to find the customer a step is for:ADK has no per-run metadata, so
stepContext names the customer. It’s called for every step, and customerOf stands for your own lookup from the logged-in user. See Naming the customer safely.4
Add the plugin to the Runner
Your agents need no changes. Put the plugin first in ADK stops at the first plugin that answers a step, so a plugin before Stigg’s can hide that step from Stigg.
plugins:5
Check that it works
Run the agent once. Then confirm in Stigg that the customer’s
credits balance went down. If it didn’t, the token counts the plugin reports aren’t converted into credits yet. See The credit balance does not go down.To see each decision while you test, add a hook to the plugin:Naming the customer safely
The customer ID decides who pays for a run. If it comes from the user’s request, a user can send another customer’s ID and charge their usage to that customer. Always resolve it on your server, from the logged-in user.adkContext.userIdis safe only when your server sets it from the login. ADK’s own API server lets the client chooseuserId.- Session state is never safe, because the client can send a
stateDeltawith every run. - A default customer in the plugin’s
stiggContext: { customerId }is charged for any step whosestepContextreturns nothing. Set one only in a process that serves one customer.
Configurations
The plugin takes the samestiggContext, agents, tools and hooks options as the LangChain handler. Each configuration below adds to the one before it.
Metering only
entitlement, nothing is checked or denied. Every model call is reported, and the agents run as they would without Stigg. By default each call reports ADK’s token counts under Gemini’s names, such as promptTokenCount and candidatesTokenCount. See the Gemini page for what each count includes, then create meters for them in Stigg or convert them with a usageMapper.
One budget for the whole team
A budget per agent
Register a parent agent and one agent per team member in Stigg, as in Register the agents. Then keyagents by the name given to each LlmAgent:
stepContext returns is merged over the plugin’s and the agent’s settings, and a tool’s price from tools still applies over it.
Tools
Paid tools are listed undertools, as in the LangChain handler. A tool counts against the agent whose model asked for it. Three kinds of tools work differently in ADK:
- ADK’s control tools,
transfer_to_agent,exit_loop,set_model_responseandfinish_task, are never checked, not even by'*'. They do no work, and denying one would break the agent. - Gemini’s built-in tools, such as
GOOGLE_SEARCH, run inside the model call, so they’re counted in the call’s tokens. - An agent wrapped in an
AgentToolruns in a Runner of its own, without the plugin, so its model calls aren’t governed. The plugin warns once. Add the agent as a sub-agent instead.
When access is denied
ADK doesn’t let a plugin throw, so the plugin answers a denied step instead:
Use
onRefusal to hand a denied agent’s turn to another agent. In this example, a researcher that runs out of budget hands its turn to a cheaper generalist agent, which answers without search:
transferTomust name an agent ADK lets the denied one transfer to. Any other name throws, as a configuration error.- An agent hands off at most once per run, so two denied agents can’t pass the turn back and forth.
- Any other
LlmResponseyou return is used as the agent’s answer.
stiggPlugin.denialOf(event). It returns the decision behind a denied step, with a message you can show, or undefined for any other event:
customMetadata, which the model never sees.
If Stigg can’t be reached to check, the SDK follows checkErrorAction, which lets the step run by default. See Fallback strategy. Reporting usage never throws into the run.
How usage is reported
- A model call is reported when its last response arrives, before ADK hands that event to you. A caller that stops reading after the answer still has the call reported.
- A streamed call is reported once, with its final total.
- A call with no finish reason is reported at the agent’s next step, or when the agent or the run ends.
- A failed model call or tool reports nothing.
Observability
Hooks, tracing spans and flushing work as they do in the LangChain handler. See Observability.Best practices
- Set the customer on your server. The customer ID decides who pays for a run. Take it from the logged-in user, never from the request, or a user can charge their usage to another customer. Return it from
stepContext. See Naming the customer safely. - Keep parallel spend within budget. Each check reads the balance at that moment, so steps that start together can each pass. Keep limits a little below the true cap, or reserve credits for the whole task. See Caching and accuracy.
- Choose small credit units. Agent budgets count whole units and round each call up, so define your credit type in small units, such as thousandths of a dollar, to keep rounding negligible. See Usage governance.
- Roll out without denials. Start with metering only, or register agents with no limit, then add limits once usage looks right. See Observe-only rollout.