Files
boxmaker/crates/loopd/tests/compact_summary.rs
T
2026-10-03 00:14:03 -07:00

148 lines
5.2 KiB
Rust

//! Tests for the compaction summary call. Do not edit.
mod support;
use bxcore::SessionId;
use loopd::baseline::Baseline;
use loopd::compact::{self, CompactError};
use loopd::llama::Client;
use loopd::session::Session;
use loopd::tools::Registry;
use support::{FakeServer, Home, Reply};
const CHAT: &str = "/v1/chat/completions";
const PROMPT: &str = "Summarise this conversation for the next epoch.\n";
fn sse(content: &str, cache_n: u64, prompt_n: u64) -> Reply {
let content_json = serde_json::to_string(content).unwrap();
let data1 = format!(
r#"data: {{"choices":[{{"finish_reason":null,"index":0,"delta":{{"content":{content_json}}}}}],"id":"sum","timings":{{"cache_n":{cache_n},"prompt_n":{prompt_n},"predicted_n":1}}}}"#
);
let data2 = format!(
r#"data: {{"choices":[{{"finish_reason":"stop","index":0,"delta":{{}}}}],"id":"sum","timings":{{"cache_n":{cache_n},"prompt_n":{prompt_n},"predicted_n":1}}}}"#
);
let body = format!("{data1}\n\n{data2}\n\ndata: [DONE]\n\n");
Reply::raw(format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
body.len()
))
}
fn session(home: &Home, cfg: &loopd::config::Config) -> Session {
let baseline = Baseline::assemble(cfg, &Registry::m2b()).unwrap();
Session::create(
&home.dir,
SessionId::new("a").unwrap(),
baseline,
cfg.slots.main,
)
.unwrap()
}
#[test]
fn the_summary_call_is_on_the_background_slot_with_no_tools_and_thinking() {
let home = Home::new();
home.write("compact.md", PROMPT);
let server = FakeServer::start();
let cfg = home.config(&server.socket);
server.route(CHAT, vec![sse("the sum", 0, 10)]);
let client = Client::new(cfg.clone());
let s = session(&home, &cfg);
let text = compact::summarise(&cfg, &client, &s).unwrap();
assert_eq!(text, "the sum");
let sent = server.requests_to(CHAT);
assert_eq!(sent.len(), 1);
let body = sent[0].json();
assert_eq!(body["id_slot"], 1);
assert!(body.get("tools").is_none(), "{body}");
assert_eq!(body["chat_template_kwargs"]["enable_thinking"], true);
let messages = body["messages"].as_array().unwrap();
let last = messages.last().unwrap();
assert_eq!(last["role"], "user");
assert_eq!(last["content"], PROMPT.trim_end());
}
#[test]
fn the_summary_is_neutralised_and_capped() {
let home = Home::new();
home.write("compact.md", PROMPT);
let server = FakeServer::start();
let mut cfg = home.config(&server.socket);
cfg.compact.summary_chars = 4;
server.route(CHAT, vec![sse("abcdefghij", 0, 10)]);
let client = Client::new(cfg.clone());
let s = session(&home, &cfg);
let text = compact::summarise(&cfg, &client, &s).unwrap();
assert_eq!(text, "abcd");
}
#[test]
fn a_context_full_summary_uses_the_fallback() {
let home = Home::new();
home.write("compact.md", PROMPT);
let server = FakeServer::start();
let cfg = home.config(&server.socket);
server.route(CHAT, vec![Reply::fixture("context_full")]);
let client = Client::new(cfg.clone());
let mut s = session(&home, &cfg);
s.append(bxcore::LogRecord::User {
time: bxcore::Timestamp::parse("2026-09-18T08:00:00.000Z").unwrap(),
content: "remember the tea".to_string(),
speaker: None,
})
.unwrap();
let text = compact::summarise(&cfg, &client, &s).unwrap();
assert_eq!(text, "remember the tea");
}
#[test]
fn tags_in_the_completion_are_neutralised() {
let home = Home::new();
home.write("compact.md", PROMPT);
let server = FakeServer::start();
let cfg = home.config(&server.socket);
let tag = concat!("<", "think", ">");
server.route(CHAT, vec![sse(&format!("hello {tag} world"), 0, 10)]);
let client = Client::new(cfg.clone());
let s = session(&home, &cfg);
let text = compact::summarise(&cfg, &client, &s).unwrap();
assert!(!text.contains(tag), "{text}");
assert!(text.contains("hello"), "{text}");
}
#[test]
fn a_missing_prompt_file_names_the_runbook() {
let home = Home::new();
let server = FakeServer::start();
let cfg = home.config(&server.socket);
let client = Client::new(cfg.clone());
let s = session(&home, &cfg);
let err = compact::summarise(&cfg, &client, &s).unwrap_err();
let e: Box<dyn std::error::Error> = Box::new(err);
assert!(e.to_string().contains("compact.md"), "{e}");
assert!(
e.to_string()
.ends_with("see docs/runbook.md#session-compact-failed"),
"{e}"
);
}
#[test]
fn an_inference_error_that_is_not_context_full_is_a_compact_failure() {
let home = Home::new();
home.write("compact.md", PROMPT);
let server = FakeServer::start();
let cfg = home.config(&server.socket);
server.route(CHAT, vec![Reply::fixture("bad_request")]);
let client = Client::new(cfg.clone());
let s = session(&home, &cfg);
let err = compact::summarise(&cfg, &client, &s).unwrap_err();
assert!(matches!(err, CompactError::Failed { .. }), "{err:?}");
let e: Box<dyn std::error::Error> = Box::new(err);
assert!(
e.to_string()
.ends_with("see docs/runbook.md#session-compact-failed"),
"{e}"
);
}