93 lines
3.3 KiB
Rust
93 lines
3.3 KiB
Rust
//! Opted-in tool-output summaries (M5d spec, section 4.3).
|
|
|
|
use crate::baseline::messages_with_carry;
|
|
use crate::compact::cap_chars;
|
|
use crate::config::Config;
|
|
use crate::llama::{ChatMessage, ChatRequest, Client};
|
|
use crate::session::Session;
|
|
use bxcore::sanitize::neutralise;
|
|
use bxcore::{CallId, DataClass, LogRecord, Timestamp, ToolCall};
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
/// Append a `ToolSummary` for each opted-in `ToolResult` in this epoch that does not yet have
|
|
/// one. Inference uses the background slot and no tools.
|
|
pub fn missing(cfg: &Config, client: &Client, session: &mut Session) -> Result<(), String> {
|
|
let path = cfg.baseline.system.with_file_name("summarise.md");
|
|
let prompt = std::fs::read_to_string(&path).map_err(|e| format!("{}: {e}", path.display()))?;
|
|
let mut names: HashMap<String, String> = HashMap::new();
|
|
let mut done: HashSet<u64> = HashSet::new();
|
|
let mut pending: Vec<(CallId, String, String, DataClass, bool)> = Vec::new();
|
|
for record in session.records() {
|
|
match record {
|
|
LogRecord::Assistant { tool_calls, .. } => {
|
|
for ToolCall { id, name, .. } in tool_calls {
|
|
names.insert(id.clone(), name.clone());
|
|
}
|
|
}
|
|
LogRecord::ToolSummary { call, .. } => {
|
|
done.insert(call.0);
|
|
}
|
|
LogRecord::ToolResult {
|
|
call,
|
|
tool_call_id,
|
|
content,
|
|
class,
|
|
untrusted,
|
|
..
|
|
} => {
|
|
let Some(name) = names.get(tool_call_id) else {
|
|
continue;
|
|
};
|
|
if !cfg.compact.summarise.iter().any(|n| n == name) {
|
|
continue;
|
|
}
|
|
pending.push((
|
|
*call,
|
|
tool_call_id.clone(),
|
|
content.clone(),
|
|
*class,
|
|
*untrusted,
|
|
));
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
pending.retain(|(call, ..)| !done.contains(&call.0));
|
|
for (call, tool_call_id, content, class, untrusted) in pending {
|
|
let mut messages =
|
|
messages_with_carry(session.baseline(), session.records(), session.carry());
|
|
messages.push(ChatMessage::User {
|
|
content: format!(
|
|
"{}\n\n{}",
|
|
prompt.trim_end(),
|
|
cap_chars(&content, cfg.r#loop.tool_result_cap)
|
|
),
|
|
});
|
|
let request = ChatRequest {
|
|
slot: cfg.slots.background,
|
|
messages,
|
|
tools: Vec::new(),
|
|
thinking: true,
|
|
};
|
|
let text = match client.chat_with_retry(&request, &mut |_| {}) {
|
|
Ok(completion) => completion.content.unwrap_or_default(),
|
|
Err(e) => {
|
|
eprintln!("loopd: tool summary: {e}");
|
|
continue;
|
|
}
|
|
};
|
|
let text = cap_chars(neutralise(&text).as_ref(), cfg.compact.summary_chars);
|
|
session
|
|
.append(LogRecord::ToolSummary {
|
|
time: Timestamp::now(),
|
|
call,
|
|
tool_call_id,
|
|
content: text,
|
|
class,
|
|
untrusted,
|
|
})
|
|
.map_err(|e| e.to_string())?;
|
|
}
|
|
Ok(())
|
|
}
|