Incremental memory scoring with per-score persistence
score_memories_incremental now takes an async callback that fires after each memory is scored. The callback: - Writes the score to the conversation entry via set_score() - Persists to memory-scores.json immediately - Notifies the UI so the context screen updates live Scoring no longer batches — each score is visible and persisted as it completes. Does not touch the memory store. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
e213644514
commit
fd58386951
2 changed files with 28 additions and 20 deletions
|
|
@ -396,26 +396,28 @@ impl Mind {
|
||||||
ag.memory_scoring_in_flight = true;
|
ag.memory_scoring_in_flight = true;
|
||||||
(ag.context.clone(), ag.client_clone())
|
(ag.context.clone(), ag.client_clone())
|
||||||
};
|
};
|
||||||
let result = learn::score_memories_incremental(
|
let _result = learn::score_memories_incremental(
|
||||||
&context, max_age as i64, response_window, &client, &agent,
|
&context, max_age as i64, response_window, &client, &agent,
|
||||||
|
|key: String, score: f64| {
|
||||||
|
let agent = agent.clone();
|
||||||
|
let path = scores_path.clone();
|
||||||
|
async move {
|
||||||
|
let mut ag = agent.lock().await;
|
||||||
|
for i in 0..ag.context.conversation.len() {
|
||||||
|
if let ConversationEntry::Memory { key: k, .. } = &ag.context.conversation.entries()[i].entry {
|
||||||
|
if *k == key {
|
||||||
|
ag.context.conversation.set_score(i, Some(score));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save_memory_scores(&ag.context.conversation, &path);
|
||||||
|
ag.changed.notify_one();
|
||||||
|
}
|
||||||
|
},
|
||||||
).await;
|
).await;
|
||||||
{
|
{
|
||||||
let mut ag = agent.lock().await;
|
let mut ag = agent.lock().await;
|
||||||
ag.memory_scoring_in_flight = false;
|
ag.memory_scoring_in_flight = false;
|
||||||
if let Ok(ref scores) = result {
|
|
||||||
// Write scores onto Memory entries
|
|
||||||
for (key, weight) in scores {
|
|
||||||
for i in 0..ag.context.conversation.len() {
|
|
||||||
if let ConversationEntry::Memory { key: k, .. } = &ag.context.conversation.entries()[i].entry {
|
|
||||||
if k == key {
|
|
||||||
ag.context.conversation.set_score(i, Some(*weight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Persist all scores to disk
|
|
||||||
save_memory_scores(&ag.context.conversation, &scores_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = bg_tx.send(BgEvent::ScoringDone);
|
let _ = bg_tx.send(BgEvent::ScoringDone);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -299,13 +299,18 @@ pub async fn score_memory(
|
||||||
/// Updates the graph weight (EWMA) and last_scored after each.
|
/// Updates the graph weight (EWMA) and last_scored after each.
|
||||||
///
|
///
|
||||||
/// Returns the number of nodes scored and their (key, score) pairs.
|
/// Returns the number of nodes scored and their (key, score) pairs.
|
||||||
pub async fn score_memories_incremental(
|
pub async fn score_memories_incremental<F, Fut>(
|
||||||
context: &ContextState,
|
context: &ContextState,
|
||||||
max_age_secs: i64,
|
max_age_secs: i64,
|
||||||
response_window: usize,
|
response_window: usize,
|
||||||
client: &ApiClient,
|
client: &ApiClient,
|
||||||
agent: &std::sync::Arc<tokio::sync::Mutex<crate::agent::Agent>>,
|
agent: &std::sync::Arc<tokio::sync::Mutex<crate::agent::Agent>>,
|
||||||
) -> anyhow::Result<Vec<(String, f64)>> {
|
mut on_score: F,
|
||||||
|
) -> anyhow::Result<usize>
|
||||||
|
where
|
||||||
|
F: FnMut(String, f64) -> Fut,
|
||||||
|
Fut: std::future::Future<Output = ()>,
|
||||||
|
{
|
||||||
let now = chrono::Utc::now().timestamp();
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
|
||||||
// Collect unique memory keys with their first position
|
// Collect unique memory keys with their first position
|
||||||
|
|
@ -330,7 +335,7 @@ pub async fn score_memories_incremental(
|
||||||
candidates.sort_by_key(|&(_, _, last)| last);
|
candidates.sort_by_key(|&(_, _, last)| last);
|
||||||
|
|
||||||
let http = http_client();
|
let http = http_client();
|
||||||
let mut results = Vec::new();
|
let mut scored = 0;
|
||||||
|
|
||||||
let total_entries = context.conversation.entries().len();
|
let total_entries = context.conversation.entries().len();
|
||||||
let first_quarter = total_entries / 4;
|
let first_quarter = total_entries / 4;
|
||||||
|
|
@ -355,7 +360,8 @@ pub async fn score_memories_incremental(
|
||||||
dbglog!(
|
dbglog!(
|
||||||
"[scoring] {} max:{:.3} ({} responses)", key, max_div, n_responses,
|
"[scoring] {} max:{:.3} ({} responses)", key, max_div, n_responses,
|
||||||
);
|
);
|
||||||
results.push((key.clone(), max_div));
|
on_score(key.clone(), max_div).await;
|
||||||
|
scored += 1;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
dbglog!(
|
dbglog!(
|
||||||
|
|
@ -365,7 +371,7 @@ pub async fn score_memories_incremental(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(results)
|
Ok(scored)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Fine-tuning scoring ─────────────────────────────────────────
|
// ── Fine-tuning scoring ─────────────────────────────────────────
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue