Kill log callback — use ConversationEntry::Log for debug traces

Add Log variant to ConversationEntry that serializes to the
conversation log but is filtered out on read-back and API calls.
AutoAgent writes debug/status info (turns, tokens, tool calls)
through the conversation log instead of a callback parameter.

Removes the log callback from run_one_agent, call_api_with_tools,
and all callers.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-07 01:23:22 -04:00
parent 7c0d8b79d9
commit b37b6d7495
8 changed files with 74 additions and 73 deletions

View file

@ -87,7 +87,7 @@ pub fn trim_entries(
+ count_msg(&Message::user(render_journal(&context.journal)));
let msg_costs: Vec<usize> = deduped.iter()
.map(|e| count_msg(e.api_message())).collect();
.map(|e| if e.is_log() { 0 } else { count_msg(e.api_message()) }).collect();
let entry_total: usize = msg_costs.iter().sum();
let total: usize = fixed_cost + entry_total;
@ -199,6 +199,9 @@ pub enum ConversationEntry {
Memory { key: String, message: Message },
/// DMN heartbeat/autonomous prompt — evicted aggressively during compaction.
Dmn(Message),
/// Debug/status log line — written to conversation log for tracing,
/// skipped on read-back.
Log(String),
}
// Custom serde: serialize Memory with a "memory_key" field added to the message,
@ -219,6 +222,12 @@ impl Serialize for ConversationEntry {
map.serialize_entry("memory_key", key)?;
map.end()
}
Self::Log(text) => {
use serde::ser::SerializeMap;
let mut map = s.serialize_map(Some(1))?;
map.serialize_entry("log", text)?;
map.end()
}
}
}
}
@ -226,6 +235,11 @@ impl Serialize for ConversationEntry {
impl<'de> Deserialize<'de> for ConversationEntry {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let mut json: serde_json::Value = serde_json::Value::deserialize(d)?;
// Log entries — skip on read-back
if json.get("log").is_some() {
let text = json["log"].as_str().unwrap_or("").to_string();
return Ok(Self::Log(text));
}
if let Some(key) = json.as_object_mut().and_then(|o| o.remove("memory_key")) {
let key = key.as_str().unwrap_or("").to_string();
let message: Message = serde_json::from_value(json).map_err(serde::de::Error::custom)?;
@ -239,10 +253,12 @@ impl<'de> Deserialize<'de> for ConversationEntry {
impl ConversationEntry {
/// Get the API message for sending to the model.
/// Panics on Log entries (which should be filtered before API calls).
pub fn api_message(&self) -> &Message {
match self {
Self::Message(m) | Self::Dmn(m) => m,
Self::Memory { message, .. } => message,
Self::Log(_) => panic!("Log entries have no API message"),
}
}
@ -254,19 +270,27 @@ impl ConversationEntry {
matches!(self, Self::Dmn(_))
}
pub fn is_log(&self) -> bool {
matches!(self, Self::Log(_))
}
/// Get a reference to the inner message.
/// Panics on Log entries.
pub fn message(&self) -> &Message {
match self {
Self::Message(m) | Self::Dmn(m) => m,
Self::Memory { message, .. } => message,
Self::Log(_) => panic!("Log entries have no message"),
}
}
/// Get a mutable reference to the inner message.
/// Panics on Log entries.
pub fn message_mut(&mut self) -> &mut Message {
match self {
Self::Message(m) | Self::Dmn(m) => m,
Self::Memory { message, .. } => message,
Self::Log(_) => panic!("Log entries have no message"),
}
}
}