Message-Count Pruning¶
The default mode. Pruning triggers when the number of messages exceeds max_messages, and removes the oldest eligible messages until min_messages remain.
Basic usage¶
from agentstate_reducer import MessageReducer
reducer = MessageReducer(min_messages=10, max_messages=20)
result = reducer.reduce(existing=history, new=[new_message])
Or via ReducerConfig for the full set of options:
from agentstate_reducer import MessageReducer, ReducerConfig
config = ReducerConfig(
min_messages=10,
max_messages=20,
preserve_first=True,
cascade_tool_messages=True,
)
reducer = MessageReducer(config=config)
How the window works¶
When len(existing + new) > max_messages:
- The pruning window is
[start, len - min_messages), wherestartis1ifpreserve_firstelse0. - Within that window, only
human/aimessages (and their aliases) are removed. system,tool, andfunctionmessages in the window are kept.- Tool-call cascade is applied (see below).
preserve_first adds one
With preserve_first=True, index 0 is always retained in addition to min_messages of the recent tail. So the floor is effectively min_messages + 1.
What is never pruned¶
- Index 0 when
preserve_first=True— typically the system prompt. system,tool,functionmessages — they're skipped as pruning candidates.
Tool-call cascade¶
When cascade_tool_messages=True (default), pruning an ai message that issued tool calls also prunes the tool messages that answered those calls (matched by tool_call_id). This prevents orphaned tool results that would confuse the model.
messages = [
{"role": "system", "content": "sys"},
{"role": "ai", "content": "", "tool_calls": [{"id": "tc1"}]},
{"role": "tool", "content": "result", "tool_call_id": "tc1"},
# ... many more messages ...
]
# If the ai message is pruned, the linked tool message goes with it.
Role aliases¶
Role names are normalised before pruning rules apply:
| Input role | Treated as | Common source |
|---|---|---|
human |
human |
LangChain canonical |
user |
human |
OpenAI API format |
ai |
ai |
LangChain canonical |
assistant |
ai |
OpenAI API format |
agent |
ai |
LangGraph task outputs, agent frameworks |
system |
system |
preserved, never pruned |
tool |
tool |
preserved unless cascade-pruned |
So OpenAI-format lists (role: "user" / role: "assistant") work with no conversion.
When to prefer this over token mode¶
- Message sizes are roughly uniform.
- You want simple, predictable limits (“keep the last 20 turns”).
- You don't want any token-counting dependency or overhead.
For context-window or cost control with variable message sizes, use Token-Budget Pruning.