Feature Description
LangChain’s agent middleware suite can cap the number of model calls
(ModelCallLimitMiddleware) and tool calls (ToolCallLimitMiddleware), but nothing bounds
token consumption. I’d like a TokenBudgetMiddleware that accumulates token usage across
model calls (from AIMessage.usage_metadata) and halts the agent — via jump_to=“end” or
a TokenBudgetExceededError — when a configured budget is exceeded. It would support
independent input/output/total limits at both thread and run scope, with sync and async
hooks, mirroring ModelCallLimitMiddleware’s structure and exit_behavior contract.
Use Case
Production agents can consume a very large token volume within a small number of calls
(context grows with tool outputs and retrieved documents), so a call-count cap doesn’t bound
spend or context blowup. SummarizationMiddleware and ContextEditingMiddleware reduce
context reactively but don’t enforce a hard ceiling. Today users hand-roll before_model /
after_model hooks re-reading usage_metadata and managing state manually. A reusable,
provider-agnostic budget guard would cover a common production need.
Proposed Solution
from langchain.agents.middleware import TokenBudgetMiddleware
from langchain.agents import create_agent
budget = TokenBudgetMiddleware(
thread_total_limit=200_000,
run_total_limit=50_000,
exit_behavior=“end”, # or “error”
)
agent = create_agent(“openai:gpt-5.5”, middleware=[budget])
after_modelreadsusage_metadataoff the latestAIMessageand accumulates
input/output/total tokens for thread + run scope (defensive: missing usage = zero
contribution, never raises).
before_model(with@hook_config(can_jump_to=[“end”])) checks accumulated usage against
any set limit; on breach it injects an explanatory AIMessage and jumps to end, or raises
TokenBudgetExceededError, per exit_behavior.
- State extends
AgentStatewith private token-count fields, reusing the same
PrivateStateAttr / UntrackedValue channel annotations ModelCallLimitMiddleware uses
for thread- vs run-scoped persistence.
- Scope is token-only; monetary cost (which needs drifting per-model pricing tables) is left
as a possible follow-up.