loopd: compact summary on the background slot
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -1,8 +1,34 @@
|
||||
//! Compaction: when a session starts a new epoch, the summary call, and writing the next files
|
||||
//! (M5d spec, section 3). Later tasks fill the call and the write.
|
||||
//! (M5d spec, section 3). Task 07 writes the next files; task 08 calls this after a turn.
|
||||
|
||||
use bxcore::sanitize::neutralise;
|
||||
use bxcore::{LogRecord, Usage};
|
||||
|
||||
use crate::baseline::messages_with_carry;
|
||||
use crate::config::Config;
|
||||
use crate::llama::{ChatMessage, ChatRequest, Client};
|
||||
use crate::session::Session;
|
||||
use crate::turn::is_context_full;
|
||||
|
||||
/// Why a summary call or a next-epoch write failed. Every variant's `Display` ends with the
|
||||
/// runbook pointer `session-compact-failed`.
|
||||
#[derive(Debug)]
|
||||
pub enum CompactError {
|
||||
Failed { detail: String },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompactError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CompactError::Failed { detail } => {
|
||||
write!(f, "{detail}; see docs/runbook.md#session-compact-failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CompactError {}
|
||||
|
||||
/// True when `cache_n + prompt_n` is at or above `threshold * n_ctx`.
|
||||
pub fn due(usage: &Usage, threshold: f64, n_ctx: u64) -> bool {
|
||||
let total = usage.cache_n.saturating_add(usage.prompt_n) as f64;
|
||||
@@ -30,3 +56,37 @@ pub fn fallback_summary(records: &[LogRecord], summary_chars: usize) -> String {
|
||||
pub(crate) fn cap_chars(s: &str, n: usize) -> String {
|
||||
s.chars().take(n).collect()
|
||||
}
|
||||
|
||||
/// One completion on the background slot: no tools, thinking on, last user `compact.md`.
|
||||
/// Context-full uses `fallback_summary`. A missing prompt, or any other infer error, is
|
||||
/// `CompactError::Failed` with the runbook pointer. No fallback for a missing prompt.
|
||||
pub fn summarise(cfg: &Config, client: &Client, session: &Session) -> Result<String, CompactError> {
|
||||
let path = cfg.compact_prompt();
|
||||
let prompt = std::fs::read_to_string(&path).map_err(|e| CompactError::Failed {
|
||||
detail: format!("{}: {e}", path.display()),
|
||||
})?;
|
||||
let mut messages = messages_with_carry(session.baseline(), session.records(), session.carry());
|
||||
messages.push(ChatMessage::User {
|
||||
content: prompt.trim_end().to_string(),
|
||||
});
|
||||
let request = ChatRequest {
|
||||
slot: cfg.slots.background,
|
||||
messages,
|
||||
tools: Vec::new(),
|
||||
thinking: true,
|
||||
};
|
||||
match client.chat_with_retry(&request, &mut |_| {}) {
|
||||
Ok(completion) => {
|
||||
let text = completion.content.unwrap_or_default();
|
||||
let text = neutralise(&text);
|
||||
Ok(cap_chars(text.as_ref(), cfg.compact.summary_chars))
|
||||
}
|
||||
Err(e) if is_context_full(&e) => Ok(fallback_summary(
|
||||
session.records(),
|
||||
cfg.compact.summary_chars,
|
||||
)),
|
||||
Err(e) => Err(CompactError::Failed {
|
||||
detail: e.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user