Skip to main content

History

Trait History 

Source
pub trait History: Send + Sync {
    // Required methods
    fn append(&self, msg: Message);
    fn snapshot(&self) -> Vec<Message>;
    fn replace(&self, messages: Vec<Message>);
    fn splice_prefix(&self, drop_count: usize, summary: Message) -> usize;
    fn len(&self) -> usize;
    fn truncate(&self, len: usize);
    fn record_input_tokens(&self, tokens: u64);
    fn token_estimate(&self) -> Option<u64>;

    // Provided method
    fn is_empty(&self) -> bool { ... }
}
Expand description

Abstraction over message history — pure storage + token accounting.

Compaction is not handled here: summarization requires calling the LLM, which the storage abstraction cannot reach. Compaction is orchestrated in the turn main loop (session/turn/compact.rs) — it reads History::snapshot, calls the LLM for a summary, then writes back the computed new message list via History::replace. This trait is only responsible for: appending, snapshotting, wholesale replacement, and providing the main loop with an estimate of “how many tokens the current history is worth.”

Token estimation strategy (see VecHistory): use the actual input token count reported by the last LLM call as a baseline, then add a character-heuristic increment for messages appended after that baseline; when no real baseline is available, fall back to a pure character-heuristic estimate for the entire history. The turn main loop compares this estimate against the compaction threshold.

Required Methods§

Source

fn append(&self, msg: Message)

Appends a message.

Source

fn snapshot(&self) -> Vec<Message>

A snapshot of the current history, to be fed into the next LLM call.

Source

fn replace(&self, messages: Vec<Message>)

Replace the entire message list after compression. The turn main loop calls this to write back the new list consisting of a summary plus the retained tail. The implementation should also reset the token estimation baseline, since the old actual token counts no longer apply to the new list.

Source

fn splice_prefix(&self, drop_count: usize, summary: Message) -> usize

Prefix splice: replaces the first drop_count messages in the current list with the single summary message, preserving everything after them. Returns the actual number of messages dropped (drop_count is clamped to the current length).

This is the primitive for background compression write-back: a background task computes drop_count (= the prefix length to summarize) and summary from a snapshot taken at some point, but while the summarization LLM call is in flight, the foreground turn may still be appending to the tail. Writing back with replace(entire list) would discard any tail messages added during that time. splice_prefix only touches the first drop_count messages of the current list, preserving everything from drop_count.. onward (including tail messages added in the meantime), so the write-back is correct.

Concurrency invariant (must be maintained): drop_count is computed from an old snapshot and remains valid for the current list provided that during the flight only tail appends (append) and in-place content replacements (micro-compression replace with same-length rebuild) occur — no insertion or deletion of middle messages. The only operation that removes middle messages is compression itself, and compression runs solo (at most one in flight at a time), so the invariant holds.

Like Self::replace, resets the token estimation baseline after write-back (the true token count of the new prefix is unknown).

Source

fn len(&self) -> usize

Number of messages currently held. Used to record a rollback boundary before a turn appends its prompt, so Self::truncate can undo it if the turn fails permanently.

Source

fn truncate(&self, len: usize)

Truncates the message list to at most len messages, dropping any tail beyond it. A no-op when len >= current length.

Used to roll back a permanently-failed turn: the user prompt (and any hook feedback) appended at the start of the turn must not linger in history once the turn errors out, otherwise it would be replayed on reload and re-sent to the model on the next request. Like Self::replace, resets the token estimation baseline since the dropped messages may have contributed to the delta estimate.

Source

fn record_input_tokens(&self, tokens: u64)

Records the actual input token count from the last LLM call (input + cache_read + cache_creation). Serves as the precise baseline for Self::token_estimate; subsequent Self::append messages are accumulated incrementally using a character heuristic.

Source

fn token_estimate(&self) -> Option<u64>

Estimates the token count for the current history. None indicates the history is empty or no estimate is available.

Provided Methods§

Source

fn is_empty(&self) -> bool

Returns whether the history holds no messages.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§