Replace push() with explicit push_log() and push_no_log()

No implicit auto-logging. Call sites choose:
- push_log: new conversation entries (user messages, tool results,
  surfaced memories, assistant responses)
- push_no_log: system prompt, identity, journal, restore from log,
  compact reload, tests

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
ProofOfConcept 2026-04-09 01:10:40 -04:00
parent 6529aba069
commit c53c4f9071
3 changed files with 21 additions and 22 deletions

View file

@ -765,18 +765,17 @@ impl ContextState {
} }
} }
pub fn push(&mut self, section: Section, node: AstNode) { /// Push and log to conversation log.
if section == Section::Conversation { pub fn push_log(&mut self, section: Section, node: AstNode) {
if let Some(ref log) = self.conversation_log { if let Some(ref log) = self.conversation_log {
if let Err(e) = log.append_node(&node) { if let Err(e) = log.append_node(&node) {
eprintln!("warning: log: {:#}", e); eprintln!("warning: log: {:#}", e);
} }
} }
}
self.section_mut(section).push(node); self.section_mut(section).push(node);
} }
/// Push without logging — for restoring from an existing log. /// Push without logging.
pub fn push_no_log(&mut self, section: Section, node: AstNode) { pub fn push_no_log(&mut self, section: Section, node: AstNode) {
self.section_mut(section).push(node); self.section_mut(section).push(node);
} }
@ -1028,7 +1027,7 @@ mod tests {
/// return the children that were pushed into the branch. /// return the children that were pushed into the branch.
fn parse_into_ctx(chunks: &[&str]) -> (ContextState, Vec<PendingToolCall>) { fn parse_into_ctx(chunks: &[&str]) -> (ContextState, Vec<PendingToolCall>) {
let mut ctx = ContextState::new(); let mut ctx = ContextState::new();
ctx.push(Section::Conversation, AstNode::branch(Role::Assistant, vec![])); ctx.push_no_log(Section::Conversation, AstNode::branch(Role::Assistant, vec![]));
let mut p = ResponseParser::new(0); let mut p = ResponseParser::new(0);
let mut calls = Vec::new(); let mut calls = Vec::new();
for chunk in chunks { for chunk in chunks {
@ -1092,7 +1091,7 @@ mod tests {
fn test_parser_incremental_feed() { fn test_parser_incremental_feed() {
let text = "<think>thought</think>response"; let text = "<think>thought</think>response";
let mut ctx = ContextState::new(); let mut ctx = ContextState::new();
ctx.push(Section::Conversation, AstNode::branch(Role::Assistant, vec![])); ctx.push_no_log(Section::Conversation, AstNode::branch(Role::Assistant, vec![]));
let mut p = ResponseParser::new(0); let mut p = ResponseParser::new(0);
for ch in text.chars() { for ch in text.chars() {
p.feed_token(&ch.to_string(), 0, &mut ctx); p.feed_token(&ch.to_string(), 0, &mut ctx);
@ -1108,7 +1107,7 @@ mod tests {
fn test_parser_incremental_tool_call() { fn test_parser_incremental_tool_call() {
let text = "text<tool_call>\n<function=bash>\n<parameter=command>ls</parameter>\n</function>\n</tool_call>more"; let text = "text<tool_call>\n<function=bash>\n<parameter=command>ls</parameter>\n</function>\n</tool_call>more";
let mut ctx = ContextState::new(); let mut ctx = ContextState::new();
ctx.push(Section::Conversation, AstNode::branch(Role::Assistant, vec![])); ctx.push_no_log(Section::Conversation, AstNode::branch(Role::Assistant, vec![]));
let mut p = ResponseParser::new(0); let mut p = ResponseParser::new(0);
let mut tool_calls = 0; let mut tool_calls = 0;
for ch in text.chars() { for ch in text.chars() {
@ -1257,9 +1256,9 @@ mod tests {
if !init_tokenizer() { return; } if !init_tokenizer() { return; }
let mut ctx = ContextState::new(); let mut ctx = ContextState::new();
ctx.push(Section::System, AstNode::system_msg("you are helpful")); ctx.push_no_log(Section::System, AstNode::system_msg("you are helpful"));
ctx.push(Section::Identity, AstNode::memory("name", "Proof of Concept")); ctx.push_no_log(Section::Identity, AstNode::memory("name", "Proof of Concept"));
ctx.push(Section::Conversation, AstNode::user_msg("hi")); ctx.push_no_log(Section::Conversation, AstNode::user_msg("hi"));
assert_eq!(ctx.tokens(), ctx.token_ids().len()); assert_eq!(ctx.tokens(), ctx.token_ids().len());
} }

View file

@ -172,7 +172,7 @@ impl Agent {
) -> Arc<Self> { ) -> Arc<Self> {
let mut context = ContextState::new(); let mut context = ContextState::new();
context.conversation_log = conversation_log; context.conversation_log = conversation_log;
context.push(Section::System, AstNode::system_msg(&system_prompt)); context.push_no_log(Section::System, AstNode::system_msg(&system_prompt));
let tool_defs: Vec<String> = tools::tools().iter() let tool_defs: Vec<String> = tools::tools().iter()
.map(|t| t.to_json()).collect(); .map(|t| t.to_json()).collect();
@ -186,11 +186,11 @@ impl Agent {
IMPORTANT: Function calls MUST follow the specified format.", IMPORTANT: Function calls MUST follow the specified format.",
tool_defs.join("\n"), tool_defs.join("\n"),
); );
context.push(Section::System, AstNode::system_msg(&tools_text)); context.push_no_log(Section::System, AstNode::system_msg(&tools_text));
} }
for (name, content) in &personality { for (name, content) in &personality {
context.push(Section::Identity, AstNode::memory(name, content)); context.push_no_log(Section::Identity, AstNode::memory(name, content));
} }
let session_id = format!("consciousness-{}", chrono::Utc::now().format("%Y%m%d-%H%M%S")); let session_id = format!("consciousness-{}", chrono::Utc::now().format("%Y%m%d-%H%M%S"));
@ -267,7 +267,7 @@ impl Agent {
pub async fn push_node(&self, node: AstNode) { pub async fn push_node(&self, node: AstNode) {
let node = node.with_timestamp(chrono::Utc::now()); let node = node.with_timestamp(chrono::Utc::now());
self.context.lock().await.push(Section::Conversation, node); self.context.lock().await.push_log(Section::Conversation, node);
self.state.lock().await.changed.notify_one(); self.state.lock().await.changed.notify_one();
} }
@ -315,7 +315,7 @@ impl Agent {
let branch_idx = { let branch_idx = {
let mut ctx = agent.context.lock().await; let mut ctx = agent.context.lock().await;
let idx = ctx.len(Section::Conversation); let idx = ctx.len(Section::Conversation);
ctx.push(Section::Conversation, ctx.push_log(Section::Conversation,
AstNode::branch(Role::Assistant, vec![]) AstNode::branch(Role::Assistant, vec![])
.with_timestamp(chrono::Utc::now())); .with_timestamp(chrono::Utc::now()));
idx idx
@ -471,7 +471,7 @@ impl Agent {
{ {
let mut ctx = agent.context.lock().await; let mut ctx = agent.context.lock().await;
for node in nodes { for node in nodes {
ctx.push(Section::Conversation, node); ctx.push_log(Section::Conversation, node);
} }
} }
agent.state.lock().await.changed.notify_one(); agent.state.lock().await.changed.notify_one();
@ -529,7 +529,7 @@ impl Agent {
let mut ctx = self.context.lock().await; let mut ctx = self.context.lock().await;
ctx.clear(Section::Journal); ctx.clear(Section::Journal);
for entry in entries { for entry in entries {
ctx.push(Section::Journal, entry); ctx.push_no_log(Section::Journal, entry);
} }
} }
@ -540,7 +540,7 @@ impl Agent {
// System section (prompt + tools) set by new(), don't touch it // System section (prompt + tools) set by new(), don't touch it
ctx.clear(Section::Identity); ctx.clear(Section::Identity);
for (name, content) in &personality { for (name, content) in &personality {
ctx.push(Section::Identity, AstNode::memory(name, content)); ctx.push_no_log(Section::Identity, AstNode::memory(name, content));
} }
} }
Err(e) => { Err(e) => {

View file

@ -541,7 +541,7 @@ impl Subconscious {
if !nodes.is_empty() { if !nodes.is_empty() {
let mut ctx = agent.context.lock().await; let mut ctx = agent.context.lock().await;
for node in nodes { for node in nodes {
ctx.push(Section::Conversation, node); ctx.push_log(Section::Conversation, node);
} }
drop(ctx); drop(ctx);
agent.state.lock().await.changed.notify_one(); agent.state.lock().await.changed.notify_one();