//! 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 = 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 = Box::new(err); assert!( e.to_string() .ends_with("see docs/runbook.md#session-compact-failed"), "{e}" ); }