Compare commits

..

No commits in common. "460394750641cc6a6b6d696062a5b787720b3292" and "ba4e01b6f37ce78d64d5f8c50590c68c55771631" have entirely different histories.

4 changed files with 38 additions and 82 deletions

View file

@ -33,36 +33,6 @@ pub use unconscious::{UnconsciousSnapshot, Unconscious};
use crate::agent::context::{AstNode, NodeBody, Section, Ast, ContextState}; use crate::agent::context::{AstNode, NodeBody, Section, Ast, ContextState};
fn match_scores(
nodes: &[AstNode],
scores: &std::collections::BTreeMap<String, f64>,
) -> Vec<(usize, f64)> {
nodes.iter().enumerate()
.filter_map(|(i, node)| {
if let AstNode::Leaf(leaf) = node {
if let NodeBody::Memory { key, .. } = leaf.body() {
return scores.get(key.as_str()).map(|&s| (i, s));
}
}
None
}).collect()
}
fn find_memory_by_key(ctx: &ContextState, key: &str) -> Option<(Section, usize)> {
[(Section::Identity, ctx.identity()), (Section::Conversation, ctx.conversation())]
.into_iter()
.find_map(|(section, nodes)| {
nodes.iter().enumerate().find_map(|(i, node)| {
if let AstNode::Leaf(leaf) = node {
if let NodeBody::Memory { key: k, .. } = leaf.body() {
if k == key { return Some((section, i)); }
}
}
None
})
})
}
fn load_memory_scores(ctx: &mut ContextState, path: &std::path::Path) { fn load_memory_scores(ctx: &mut ContextState, path: &std::path::Path) {
let data = match std::fs::read_to_string(path) { let data = match std::fs::read_to_string(path) {
Ok(d) => d, Ok(d) => d,
@ -72,24 +42,25 @@ fn load_memory_scores(ctx: &mut ContextState, path: &std::path::Path) {
Ok(s) => s, Ok(s) => s,
Err(_) => return, Err(_) => return,
}; };
let identity_scores = match_scores(ctx.identity(), &scores); let mut applied = 0;
let conv_scores = match_scores(ctx.conversation(), &scores); for i in 0..ctx.conversation().len() {
let applied = identity_scores.len() + conv_scores.len(); if let AstNode::Leaf(leaf) = &ctx.conversation()[i] {
for (i, s) in identity_scores { if let NodeBody::Memory { key, .. } = leaf.body() {
ctx.set_score(Section::Identity, i, Some(s)); if let Some(&s) = scores.get(key.as_str()) {
} ctx.set_score(Section::Conversation, i, Some(s));
for (i, s) in conv_scores { applied += 1;
ctx.set_score(Section::Conversation, i, Some(s)); }
}
}
} }
if applied > 0 { if applied > 0 {
dbglog!("[scoring] loaded {} scores from {}", applied, path.display()); dbglog!("[scoring] loaded {} scores from {}", applied, path.display());
} }
} }
/// Collect scored memory keys from identity and conversation entries. /// Collect scored memory keys from conversation entries.
fn collect_memory_scores(ctx: &ContextState) -> std::collections::BTreeMap<String, f64> { fn collect_memory_scores(ctx: &ContextState) -> std::collections::BTreeMap<String, f64> {
ctx.identity().iter() ctx.conversation().iter()
.chain(ctx.conversation().iter())
.filter_map(|node| { .filter_map(|node| {
if let AstNode::Leaf(leaf) = node { if let AstNode::Leaf(leaf) = node {
if let NodeBody::Memory { key, score: Some(s), .. } = leaf.body() { if let NodeBody::Memory { key, score: Some(s), .. } = leaf.body() {
@ -560,10 +531,14 @@ impl Mind {
async move { async move {
let scores_snapshot = { let scores_snapshot = {
let mut ctx = agent.context.lock().await; let mut ctx = agent.context.lock().await;
// Find memory by key in identity or conversation for i in 0..ctx.conversation().len() {
let found = find_memory_by_key(&ctx, &key); if let AstNode::Leaf(leaf) = &ctx.conversation()[i] {
if let Some((section, i)) = found { if let NodeBody::Memory { key: k, .. } = leaf.body() {
ctx.set_score(section, i, Some(score)); if *k == key {
ctx.set_score(Section::Conversation, i, Some(score));
}
}
}
} }
let snapshot = collect_memory_scores(&ctx); let snapshot = collect_memory_scores(&ctx);
drop(ctx); drop(ctx);

View file

@ -62,16 +62,8 @@ fn build_token_ids(
for node in context.system() { for node in context.system() {
ids.extend(node.token_ids()); ids.extend(node.token_ids());
} }
// Identity nodes can be filtered by key for scoring
for node in context.identity() { for node in context.identity() {
let skip = match &filter { ids.extend(node.token_ids());
Filter::SkipKey(key) => memory_key(node) == Some(*key),
Filter::SkipAllMemories => is_memory(node),
_ => false,
};
if !skip {
ids.extend(node.token_ids());
}
} }
for node in context.journal() { for node in context.journal() {
ids.extend(node.token_ids()); ids.extend(node.token_ids());
@ -183,9 +175,7 @@ pub async fn score_memories(
// Collect memory keys and response indices under a brief lock // Collect memory keys and response indices under a brief lock
let (memory_keys, response_indices) = { let (memory_keys, response_indices) = {
let ctx = agent.context.lock().await; let ctx = agent.context.lock().await;
// Include identity nodes and conversation memories let mut keys: Vec<String> = ctx.conversation().iter()
let mut keys: Vec<String> = ctx.identity().iter()
.chain(ctx.conversation().iter())
.filter_map(|node| memory_key(node).map(String::from)) .filter_map(|node| memory_key(node).map(String::from))
.collect(); .collect();
keys.dedup(); keys.dedup();
@ -341,10 +331,7 @@ where
{ {
let store = &*store_arc; let store = &*store_arc;
// Identity nodes always score at position 0; conversation nodes at their index for (i, node) in context.conversation().iter().enumerate() {
let identity_nodes = context.identity().iter().map(|n| (0, n));
let conv_nodes = context.conversation().iter().enumerate();
for (pos, node) in identity_nodes.chain(conv_nodes) {
if let Some(key) = memory_key(node) { if let Some(key) = memory_key(node) {
if !seen.insert(key.to_owned()) { continue; } if !seen.insert(key.to_owned()) { continue; }
let last_scored = store.get_node(key) let last_scored = store.get_node(key)
@ -353,7 +340,7 @@ where
.map(|n| n.last_scored) .map(|n| n.last_scored)
.unwrap_or(0); .unwrap_or(0);
if now - last_scored >= max_age_secs { if now - last_scored >= max_age_secs {
candidates.push((pos, key.to_owned(), last_scored)); candidates.push((i, key.to_owned(), last_scored));
} }
} }
} }

View file

@ -38,13 +38,16 @@ impl ConsciousScreen {
for node in ctx.conversation() { for node in ctx.conversation() {
if let AstNode::Leaf(leaf) = node { if let AstNode::Leaf(leaf) = node {
if let NodeBody::Memory { key, score, text } = leaf.body() { if let NodeBody::Memory { key, score, text } = leaf.body() {
if score.is_some() { scored += 1; } else { unscored += 1; } let status = match score {
Some(s) => { scored += 1; format!("{:.2}", s) }
None => { unscored += 1; String::new() }
};
mem_children.push(SectionView { mem_children.push(SectionView {
name: format!("mem: {}", key), name: key.clone(),
tokens: node.tokens(), tokens: node.tokens(),
content: text.clone(), content: text.clone(),
children: Vec::new(), children: Vec::new(),
status: score.map(|s| format!("{:.2}", s)).unwrap_or_default(), status,
}); });
} }
} }

View file

@ -6,7 +6,7 @@ use ratatui::{
widgets::{Block, Borders}, widgets::{Block, Borders},
crossterm::event::KeyCode, crossterm::event::KeyCode,
}; };
use crate::agent::context::{AstNode, Ast, NodeBody}; use crate::agent::context::{AstNode, Ast};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SectionView { pub struct SectionView {
@ -20,22 +20,13 @@ pub struct SectionView {
fn node_to_view(node: &AstNode) -> SectionView { fn node_to_view(node: &AstNode) -> SectionView {
match node { match node {
AstNode::Leaf(leaf) => { AstNode::Leaf(leaf) => SectionView {
let (name, status) = match leaf.body() { name: node.label(),
NodeBody::Memory { key, score, .. } => { tokens: node.tokens(),
let s = score.map(|v| format!("{:.2}", v)).unwrap_or_default(); content: leaf.body().text().to_string(),
(format!("mem: {}", key), s) children: Vec::new(),
} status: String::new(),
_ => (node.label(), String::new()), },
};
SectionView {
name,
tokens: node.tokens(),
content: leaf.body().text().to_string(),
children: Vec::new(),
status,
}
}
AstNode::Branch { children, .. } => { AstNode::Branch { children, .. } => {
let child_views: Vec<SectionView> = children.iter() let child_views: Vec<SectionView> = children.iter()
.map(|c| node_to_view(c)) .map(|c| node_to_view(c))