use std::sync::Arc;
use molo::{
Budget, Memory, MemoryError, Message, TokenCounter, TrimResult, TrimStrategy, WindowMemory,
};
#[derive(Debug, Default)]
struct Summarizer;
#[async_trait::async_trait]
impl TrimStrategy for Summarizer {
async fn trim(
&self,
messages: &[Message],
_budget: &Budget,
_counter: &dyn TokenCounter,
) -> Result<TrimResult, MemoryError> {
let result = match messages.iter().rposition(|m| matches!(m, Message::User(_))) {
Some(pos) => {
let mut result = Vec::with_capacity(messages.len() - pos + 1);
result.push(Message::system("summary of the earlier context"));
result.extend_from_slice(&messages[pos..]);
result
}
None => messages.to_vec(),
};
Ok(TrimResult {
messages: result,
replace: true,
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut memory = WindowMemory::new(30);
for i in 1..=4 {
memory
.record(Message::user(format!("question for round {i}")))
.await?;
memory
.record(Message::assistant(format!("reply for round {i}")))
.await?;
}
let context = memory.context().await?;
println!(
"1. default window (budget 30): full 8 messages → {} after trimming",
context.len()
);
for message in &context {
println!(" {message:?}");
}
memory.set_max_tokens(1000);
println!(
"2. budget raised: back to the full {} messages (lossless projection; history restored)",
memory.context().await?.len()
);
memory.set_max_tokens(30);
let mut memory = WindowMemory::new(1000).with_max_rounds(2);
for i in 1..=4 {
memory
.record(Message::user(format!("question for round {i}")))
.await?;
memory
.record(Message::assistant(format!("reply for round {i}")))
.await?;
}
println!(
"3. max_rounds(2): full 8 messages → the most recent 2 rounds {}",
memory.context().await?.len()
);
let mut memory = WindowMemory::new(30).with_strategy(Arc::new(Summarizer));
for i in 1..=4 {
memory
.record(Message::user(format!("question for round {i}")))
.await?;
memory
.record(Message::assistant(format!("reply for round {i}")))
.await?;
}
let compressed = memory.context().await?;
println!(
"4. summarizer strategy injected: {} after compression (materialized; old messages are not kept)",
compressed.len()
);
for message in &compressed {
println!(" {message:?}");
}
let again = memory.context().await?;
println!(
" → fetched again: {} (under budget, zero recompute)",
again.len()
);
memory.record(Message::user("question for round 5")).await?;
memory
.record(Message::assistant("reply for round 5"))
.await?;
let context = memory.context().await?;
println!(
" → compressed again after appending round 5: {} (input includes the previous summary)",
context.len()
);
Ok(())
}