mod assembly;
mod summarization;
use zeph_memory::TokenCounter;
use super::{Agent, Channel, Message};
pub(super) fn chunk_messages(
messages: &[Message],
budget: usize,
oversized: usize,
tc: &TokenCounter,
) -> Vec<Vec<Message>> {
let mut chunks: Vec<Vec<Message>> = Vec::new();
let mut current: Vec<Message> = Vec::new();
let mut current_tokens = 0usize;
for msg in messages {
let msg_tokens = tc.count_message_tokens(msg);
if msg_tokens >= oversized {
if !current.is_empty() {
chunks.push(std::mem::take(&mut current));
current_tokens = 0;
}
chunks.push(vec![msg.clone()]);
} else if current_tokens + msg_tokens > budget && !current.is_empty() {
chunks.push(std::mem::take(&mut current));
current_tokens = 0;
current.push(msg.clone());
current_tokens += msg_tokens;
} else {
current.push(msg.clone());
current_tokens += msg_tokens;
}
}
if !current.is_empty() {
chunks.push(current);
}
if chunks.is_empty() {
chunks.push(Vec::new());
}
chunks
}
pub(super) use crate::text::truncate_to_chars as truncate_chars;
pub(super) fn cap_summary(s: String, max_chars: usize) -> String {
match s.char_indices().nth(max_chars) {
Some((byte_idx, _)) => {
tracing::warn!(
original_chars = s.chars().count(),
cap = max_chars,
"LLM summary exceeded cap, truncating"
);
format!("{}…", &s[..byte_idx])
}
None => s,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum CompactionOutcome {
Compacted,
ProbeRejected,
NoChange,
}
pub(super) enum ContextSlot {
Summaries(Option<Message>),
CrossSession(Option<Message>),
SemanticRecall(Option<Message>),
DocumentRag(Option<Message>),
Corrections(Option<Message>),
CodeContext(Option<String>),
GraphFacts(Option<Message>),
}
impl<C: Channel> Agent<C> {
pub(super) fn compaction_tier(&self) -> super::context_manager::CompactionTier {
self.context_manager
.compaction_tier(self.providers.cached_prompt_tokens)
}
}
#[cfg(test)]
mod tests;