use super::compaction::*;
use super::config::*;
use super::strategy::*;
use super::token::{resolve_counter, TokenCounter};
use crate::session::Session;
use crate::types::*;
use std::sync::Arc;
fn resolve_scope(
session: &Session,
chain: &[String],
scope: &CompactionScope,
max_context_tokens: usize,
counter: &dyn TokenCounter,
) -> usize {
match scope {
CompactionScope::FixedCount(n) => *n,
CompactionScope::TokenBudget => {
let mut budget = max_context_tokens;
let mut count = 0usize;
for loop_id in chain.iter().rev().skip(1) {
if let Some(record) = session.get_loop(loop_id) {
let loop_tokens = counter.estimate_messages(&record.messages);
if loop_tokens > budget {
break;
}
budget -= loop_tokens;
count += 1;
}
}
count
}
}
}
pub fn compact_session_loops(
session: &mut Session,
current_loop_id: &str,
strategy: &dyn BlockCompactionStrategy,
config: &CompactionConfig,
max_context_tokens: usize,
counter: Option<&Arc<dyn TokenCounter>>,
) {
let counter = resolve_counter(counter);
let chain = session.loop_chain_to(current_loop_id);
if let Some(current) = session.get_loop_mut(current_loop_id) {
current.compaction_block = Some(strategy.compact(current, config, true));
}
let earlier_count = resolve_scope(
session,
&chain,
&config.compaction_scope,
max_context_tokens,
counter,
)
.min(chain.len().saturating_sub(1));
let earlier_start = chain.len().saturating_sub(1 + earlier_count);
for loop_id in &chain[earlier_start..chain.len().saturating_sub(1)] {
if let Some(record) = session.get_loop_mut(loop_id) {
if record.compaction_block.is_none() {
record.compaction_block = Some(strategy.compact(record, config, false));
}
}
}
}
pub fn build_context_from_session(
session: &Session,
current_loop_id: &str,
config: &CompactionConfig,
max_context_tokens: usize,
counter: Option<&Arc<dyn TokenCounter>>,
) -> Vec<AgentMessage> {
let counter = resolve_counter(counter);
let chain = session.loop_chain_to(current_loop_id);
let mut context = Vec::new();
let earlier_count = resolve_scope(
session,
&chain,
&config.compaction_scope,
max_context_tokens,
counter,
);
let load_start = chain.len().saturating_sub(earlier_count + 1);
for (i, loop_id) in chain.iter().enumerate().skip(load_start) {
let Some(record) = session.get_loop(loop_id) else {
continue;
};
let is_most_recent = i == chain.len() - 1;
match &record.compaction_block {
Some(block) => {
if is_most_recent {
if let Some(ref range) = block.keep_first {
let turn_map = TurnMap::from_messages(&record.messages);
let msgs = turn_map.messages_for_range(range, &record.messages);
context.extend_from_slice(msgs);
}
if let Some(ref section) = block.keep_compacted {
context.extend(section.messages.iter().cloned());
}
if let Some(ref section) = block.keep_recent {
context.extend(section.messages.iter().cloned());
}
} else {
if let Some(ref section) = block.keep_compacted {
context.extend(section.messages.iter().cloned());
}
}
}
None => {
context.extend(record.messages.iter().cloned());
}
}
}
context
}