Skip to main content

Tool Budget

Use tool-budget to block a request when its top-level max_tokens value is greater than the configured ceiling for any tool definition listed in that request. The current policy is a declaration-time guard, not a usage meter.

It does not observe whether the model actually calls a tool, how many tokens a tool invocation consumes, or how much the request costs.

Outcome

ConditionVerdictReason code
At least one listed tool exceeds its token ceilingblocktool-budget.exceeded
No listed tool exceedsallowtool-budget.ok

The nested policy-result details contain:

{
"exceeded_tools": [
"web_search"
]
}

An allowed chain normally retains final reason ok; inspect the nested tool-budget.ok result for policy-specific evidence. A block happens before the upstream call and returns an HTTP 400 policy-violation response.

Prerequisites

  • Add tool-budget to the effective request chain.
  • Use exactly the same tool names as the client sends in request.tools.
  • Ensure clients send a non-negative integer max_tokens field. Other token limit names are not read by this policy.
  • Use separate Usage, key-budget, rate-limit, or billing controls for actual or cumulative consumption.

Configuration

pack:
name: tool-budget-example-1
version: "1.0.0"
enabled: true

policies:
chain:
- tool-budget

policy:
tool-budget:
budgets:
web_search:
max_tokens: 5000
database_query:
max_tokens: 2000

Supported fields

FieldTypeDefaultNotes
budgetsobject{}Map of tool names to limit objects.
budgets.<tool>.max_tokensinteger ≥ 1Enforced against the request's top-level max_tokens.
budgets.<tool>.max_cost_usdnumber ≥ 0Schema-valid and parsed, but not enforced by this evaluator.

The linter warns when a budget entry has neither max_tokens nor max_cost_usd. A max_cost_usd-only entry satisfies that lint check but still has no runtime effect in this policy.

Exact evaluation

The evaluator:

  1. Reads top-level request.max_tokens as an unsigned integer.
  2. Reads every item in top-level request.tools.
  3. Extracts tools[*].function.name, falling back to tools[*].name.
  4. Looks up each name in policy.tool-budget.budgets.
  5. Adds the name to exceeded_tools when request.max_tokens > budgets.<name>.max_tokens.

The comparison is strict: a request equal to the configured ceiling is allowed.

Request shapes

Both tool-name forms are recognized:

{
"max_tokens": 250,
"tools": [
{
"type": "function",
"function": {
"name": "web_search"
}
},
{
"name": "database_query"
}
]
}

The list is the set of tools made available in the request. It is not a list of tool calls already executed by the model.

Missing and alternate fields

These cases currently evaluate as a zero token request and do not exceed a positive limit:

  • missing max_tokens;
  • negative, fractional, or string max_tokens; and
  • request families that send only max_completion_tokens or max_output_tokens.

An absent or non-array tools field also yields no checked tools. Unconfigured tool names are allowed by this policy.

Do not depend on those allow paths as safe defaults. Normalize the client request to the exact supported fields or use another owning control.

Test the blocking contract

Create tests/blocks-search-over-budget.json:

{
"name": "blocks-search-over-budget",
"input": {
"messages": [
{
"role": "user",
"content": "Search the documentation."
}
],
"request": {
"model": "replace-with-model-id",
"max_tokens": 250,
"tools": [
{
"type": "function",
"function": {
"name": "web_search"
}
}
]
}
},
"expected": {
"verdict": "block",
"reason_code": "tool-budget.exceeded"
}
}

With web_search.max_tokens: 100, run:

kt policy lint --file policy-config.yaml
kt policy test --json

Inspect details.policy_results[] and confirm exceeded_tools: ["web_search"].

Add boundary fixtures for:

  • max_tokens: 100 -> allow / ok;
  • max_tokens: 101 -> block / tool-budget.exceeded;
  • an unconfigured tool -> allow / ok;
  • missing max_tokens -> allow / ok; and
  • multiple tools where only one exceeds.

What this policy does not provide

  • Cumulative session, user, team, agent, or organization counters.
  • Actual prompt, completion, or tool-execution token metering.
  • Cost estimation or enforcement for max_cost_usd.
  • Limits on the number of tool calls.
  • Validation of tool arguments, schemas, safety, or authorization.
  • Enforcement against tool calls returned later in a model response.

Use tool-validation, tool-security, agent-firewall, token rate limits, and the product's Usage/budget surfaces for those separate concerns.

Rollout guidance

  1. Capture the exact tools and token-limit shape sent by each client library.
  2. Normalize tool names and casing; budget lookup is an exact map-key match.
  3. Set limits from the maximum declared completion size you are willing to permit when each tool is available.
  4. Add equality, one-over-limit, missing-field, and multi-tool fixtures.
  5. Run a live request and verify the HTTP response plus nested exceeded_tools event details.
  6. Monitor false blocks when applications expose a large tool catalog on every request, even when most tools will not be called.

Troubleshooting

SymptomLikely causeResolution
Large request was allowedThe client used max_completion_tokens/max_output_tokens, omitted max_tokens, or sent a non-integer value.Send supported top-level max_tokens or use a control that owns the alternate request family.
Tool was not checkedIts name was absent, nested differently, or did not exactly match a budget key.Inspect request.tools and align function.name or name.
Request blocked although no tool was calledThe policy checks tools declared as available, not model-emitted invocations.Reduce the request tool list or choose a different control for actual calls.
Cost-only budget never blocksmax_cost_usd is not enforced here.Use the product's supported cost-budget/Usage workflow.
Equality was expected to blockThe evaluator uses > rather than >=.Set the ceiling one unit lower if that is the intended contract and retest.
Allowed event has final reason okAllow-only nested reasons do not replace the final decision reason.Inspect the nested result for tool-budget.ok.

Next steps