Normandy.Context.WindowManager
(normandy v2.2.0)
View Source
Manages context window limits for conversations with automatic truncation strategies.
Provides utilities for tracking token usage, managing context window limits, and automatically truncating conversation history when approaching limits.
Features
- Token counting for messages and conversations
- Context window passed in by the caller (no built-in model table)
- Multiple truncation strategies (oldest-first, sliding window, summarization)
- Automatic context management with AgentMemory integration
Example
# Create a window manager with the model's context window
manager = WindowManager.new(max_tokens: 200_000)
# Check if we're within limits
{:ok, within_limit?} = WindowManager.within_limit?(manager, agent)
# Automatically truncate if needed
case WindowManager.ensure_within_limit(agent, manager) do
{:ok, updated_agent} -> updated_agent
{:error, reason} -> handle_summary_failure(agent, reason)
endTruncation Strategies
:oldest_first- Remove oldest messages first (default):summarize- Summarize old messages before removing; needs the agent's:client(a%BaseAgentConfig{}always has one) and raisesArgumentErrorwithout it
The kept messages always start at a user message, so the conversation stays valid for the API (no leading assistant turn, no tool result without its tool use). If the newest messages alone exceed the limit, their whole turn is kept rather than an empty history.
Summary
Functions
Ensures agent's conversation stays within context window limit.
Estimates total tokens in conversation history.
Estimates token count for message content.
Estimates token count for a message.
Creates a new WindowManager.
Truncates conversation history using the configured strategy.
Checks if current conversation is within token limit.
Types
@type strategy() :: :oldest_first | :summarize
@type t() :: %Normandy.Context.WindowManager{ max_tokens: pos_integer(), reserved_tokens: non_neg_integer(), strategy: strategy(), summary_max_tokens: pos_integer() | nil }
Functions
Ensures agent's conversation stays within context window limit.
Automatically truncates history if needed using the configured strategy.
Returns {:error, reason} when the :summarize strategy's summarization
fails, leaving the agent unchanged.
Example
{:ok, updated_agent} = WindowManager.ensure_within_limit(agent, manager)
@spec estimate_conversation_tokens(Normandy.Components.AgentMemory.t()) :: non_neg_integer()
Estimates total tokens in conversation history.
Example
tokens = WindowManager.estimate_conversation_tokens(agent.memory)
@spec estimate_message_content_tokens(term()) :: non_neg_integer()
Estimates token count for message content.
Example
tokens = WindowManager.estimate_message_content_tokens("Hello")
#=> ~2
@spec estimate_tokens(String.t()) :: non_neg_integer()
Estimates token count for a message.
This is a rough estimate based on character count.
For accurate counts, use Normandy.Context.TokenCounter.count_message/3.
Example
tokens = WindowManager.estimate_tokens("Hello, world!")
#=> ~4 tokens
Creates a new WindowManager.
Options
:max_tokens(pos_integer/0) - Required. The model's context window in tokens. Inside an agent the compactor passes the window resolved through the configuredNormandy.Behaviours.ModelCatalog; standalone, take it fromClaudio.Models.get/2("max_input_tokens").:reserved_tokens(non_neg_integer/0) - Tokens kept free for the response. The default value is4096.:strategy- The truncation strategy. The default value is:oldest_first.:summary_max_tokens-max_tokensof the:summarizestrategy's summary call;nilisNormandy.Context.Summarizer's default (the agent'smax_tokens, else 16_000). The default value isnil.
Example
manager = WindowManager.new(max_tokens: 200_000, strategy: :oldest_first)
Truncates conversation history using the configured strategy.
Returns {:error, reason} when the :summarize strategy's summarization
fails, leaving the agent unchanged.
Example
case WindowManager.truncate_conversation(agent, manager) do
{:ok, updated_agent} -> updated_agent
{:error, reason} -> handle_summary_failure(agent, reason)
end
Checks if current conversation is within token limit.
Example
case WindowManager.within_limit?(manager, agent) do
{:ok, true} -> :continue
{:ok, false} -> :truncate_needed
end