148 lines
5.2 KiB
Rust
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}"
|
|
);
|
|
}
|