TokenSavingMiddleware is an AgentMiddleware subclass that does three independent things, each toggleable:
- Tool-output compression — head+tail truncates
ToolMessagebodies once they exceedcompress_threshold_chars, leaving the most recent N tool messages untouched. - Early-exit nudge — once the agent has run at least
early_exit_min_call_indexmodel calls, evaluates trajectory signals and injects aHumanMessagetelling the agent to submit when it appears stuck. - Perplexity compression (opt-in) — applies word-level keep/drop classification to stale messages.
Standalone setup
Stack with ReasonBlocksMiddleware
When stacking withReasonBlocksMiddleware, place TokenSavingMiddleware last. It runs after steering injections are queued, so any injected content goes out compressed.
When you assemble the stack via
ReasonBlocksConfig + build_middleware, set enable_token_saving=True and the ordering is handled for you. See ReasonBlocksConfig.Tool-output compression
OldToolMessage bodies in the message history are head+tail truncated once they exceed compress_threshold_chars. The middleware replaces same-id messages via LangGraph’s add_messages reducer, so the history actually shrinks rather than growing.
The most recent keep_recent_tool_messages tool messages are exempt — the agent always has full visibility into its current reasoning step.
compress_tool_output is also exposed as a standalone helper:
Early-exit nudge
Once the call index reachesearly_exit_min_call_index (default 40), the middleware calls signals_fn(steps) on each before_model. If the returned signals indicate a loop, it injects a single HumanMessage with early_exit_text.
The fire condition reads three keys from the dict your signals_fn returns (any key it omits is treated as 0.0):
[0, 1]. Compute them however you like; the server-side monitor scores you already collect via telemetry are one source.
You appear to be stuck in a loop. Stop investigating and submit your current best answer now using whatever submission tool your task expects. Do not start another investigation.Override it with
early_exit_text="...".
Inspect TokenSavingStats
TokenSavingMiddleware.stats is a TokenSavingStats dataclass with running counters.
Perplexity compression (opt-in)
For long trajectories where head+tail isn’t enough, the middleware can apply LLMLingua-2-style word-level keep/drop classification to staleAIMessage and ToolMessage content. Two staleness tiers, each with its own keep ratio. Decisions are cached per (message_id, target_keep_ratio) so each message is classified only once.
- Setup
- Tuning
Provide a With
WordClassifier callable. The shipped factory uses Anthropic Haiku as the classifier:enable_perplexity_compression=True but perplexity_classifier=None, perplexity compression is silently skipped.Use ReasonBlocksConfig for full control
If you assemble the stack viaReasonBlocksConfig + build_middleware, every field above maps to a ts_* config field. See ReasonBlocksConfig.

