Input & Output Controls
Tool Budget
tool-budget blocks a request whose max_tokens is larger than the limit you set for a tool that the request declares. For example, with a 100-token limit for web_search, a request that declares web_search with max_tokens: 250 is blocked.
Despite its name, this is a check of one request field, not a spending budget. It does not count tool runs or token use. For spending limits, see Usage Costs and Budgets.
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: 100Verify the block with a pack test
Create a JSON test next to the configuration. The request goes in input.request:
{
"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"
}
}verdictan policy lint --file policy-config.yaml
verdictan policy test --jsonThe test passes, and its details.policy_results entry shows exceeded_tools: ["web_search"]. Copy the file for the boundary cases:
| Request | Result |
|---|---|
max_tokens: 100 | allow, ok |
max_tokens: 101 | block, tool-budget.exceeded |
| A tool without a budget | allow, ok |
No max_tokens | allow, ok |
Fields
| Field | Type | Default | Description |
|---|---|---|---|
budgets | object | {} | Limits by tool name. The key must equal the name that the client sends. |
budgets.<tool>.max_tokens | integer, at least 1 | Token limit for the tool. Required in each entry. |
Lint rejects the removed field max_cost_usd.
How it works
- The gateway reads the top-level
max_tokens. A missing, negative, fractional, or string value counts as0. - It reads each tool name from
tools[*].function.name, or fromtools[*].nameif that is missing. - For each tool with a budget, it compares
max_tokenswith the limit. - A value above the limit adds the tool to
exceeded_tools. A value equal to the limit is fine. - Any exceeded tool blocks the request with
tool-budget.exceeded: HTTP400before the provider call. - Otherwise, the request is allowed with
tool-budget.ok.
The tools array is what the client offers the model, not what the model called. Both name forms are read:
{
"max_tokens": 250,
"tools": [
{
"type": "function",
"function": {
"name": "web_search"
}
},
{
"name": "database_query"
}
]
}Phase and verdicts
| Item | Value |
|---|---|
| Input phase | Compares max_tokens with the limit of each declared tool |
| Output phase | Not checked |
| Verdicts | allow, block |
| No exceeded tool | tool-budget.ok |
| An exceeded tool | tool-budget.exceeded |
An allowed decision keeps the top-level reason ok. The nested tool-budget result shows tool-budget.ok.
Limits and failure behavior
- Only the top-level
max_tokensis read. A request withmax_completion_tokensormax_output_tokenscounts as0and passes. - Without a
toolsarray, nothing is checked. A tool without a budget passes. - Names must match the budget keys exactly, including case.
- Nothing is counted across requests, sessions, users, or teams.
- The control does not limit tool calls, validate arguments, or examine model tool calls. Use
tool-validation,tool-security,agent-firewall, and rate limits for that. - If an application declares many tools in every request, a low limit on one tool blocks every request. Set each limit to the largest completion size that the tool allows.
| Problem | Cause | Action |
|---|---|---|
| A large request was allowed | The client used max_completion_tokens or max_output_tokens, left out max_tokens, or sent a non-integer. | Send an integer max_tokens at the top level. |
| A tool was not checked | Its name is missing, elsewhere in the object, or different from the key. | Match function.name or name with the key. |
| Blocked, although the model called no tool | The control checks declared tools, not calls. | Remove the tool from the request, or use a control for calls. |
Lint rejects max_cost_usd | The field was removed. | Use max_tokens. |
| A request at the limit passed | The check is "greater than". | Set the limit one lower. |
Next steps
- Tool Validation: declared tools and argument schemas.
- Agent Firewall: tool access and actions.
- Rate Limit Configuration: request and token rates.