Raxol.Agent.ContextCompactor (Raxol Agent v2.6.0)

Copy Markdown View Source

Token-aware message history compaction for agent sessions.

When an agent's conversation history exceeds a token budget, older messages are summarized into a single system message while preserving recent messages verbatim.

Usage

config = %{max_tokens: 8_000, preserve_recent: 4}

case ContextCompactor.compact(messages, config) do
  %{compacted: true, messages: compacted_messages} ->
    # Use compacted_messages for next LLM call
  %{compacted: false} ->
    # History is within budget, no changes needed
end

Token estimation uses a simple byte_size/4 heuristic (no tokenizer dependency). Summaries are generated purely from message metadata -- no LLM call required.

Summary

Functions

Build a structured summary from a list of messages.

Estimate token count for a string or message list.

Format a summary struct into a human-readable continuation message.

Types

compaction_result()

@type compaction_result() :: %{
  messages: [message()],
  compacted: boolean(),
  original_tokens: non_neg_integer(),
  compacted_tokens: non_neg_integer(),
  summary: String.t() | nil
}

config()

@type config() :: %{
  max_tokens: pos_integer(),
  preserve_recent: pos_integer(),
  summary_max_tokens: pos_integer()
}

message()

@type message() :: %{role: atom(), content: String.t()}

summary()

@type summary() :: %{
  message_count: non_neg_integer(),
  role_counts: %{required(atom()) => non_neg_integer()},
  recent_user_requests: [String.t()],
  key_topics: [String.t()],
  file_paths: [String.t()],
  pending_work: [String.t()]
}

Functions

build_summary(messages)

@spec build_summary([message()]) :: summary()

Build a structured summary from a list of messages.

Extracts metadata (role counts, file paths, recent requests, pending work) without requiring an LLM call.

compact(messages, config \\ %{max_tokens: 8000, preserve_recent: 4, summary_max_tokens: 1000})

@spec compact([message()], config()) :: compaction_result()

Compact messages if over token budget.

Splits system messages from conversation, preserves the last N non-system messages verbatim, and summarizes the rest into a continuation system message.

Returns a compaction_result map. If no compaction was needed, compacted is false and messages are returned unchanged.

estimate_tokens(text)

@spec estimate_tokens(String.t()) :: non_neg_integer()
@spec estimate_tokens([message()]) :: non_neg_integer()

Estimate token count for a string or message list.

Uses a byte_size/4 heuristic. Not exact, but sufficient for budget decisions.

format_continuation(summary)

@spec format_continuation(summary()) :: String.t()

Format a summary struct into a human-readable continuation message.

needs_compaction?(messages, config \\ %{max_tokens: 8000, preserve_recent: 4, summary_max_tokens: 1000})

@spec needs_compaction?([message()], config()) :: boolean()

Check if compaction is needed for the given messages.