Restore full N×M memory scoring matrix (/score command)

The full matrix scorer was deleted during the AST conversion. Restore
it: /score runs score_memories() which computes divergence for every
memory × response pair, stores the MemoryScore on MindState, and
displays per-memory weights with bar charts on the F2 screen.

Both scoring paths now use ActivityGuard::update() for live progress
in the status bar instead of creating a new activity per iteration.

Also bumps score API timeout from 120s to 300s and adds progress
logging throughout.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-04-09 22:19:02 -04:00 committed by Kent Overstreet
parent f6a6c37435
commit 58cec97e57
6 changed files with 187 additions and 98 deletions

View file

@ -137,8 +137,10 @@ impl Clone for MindState {
pub enum MindCommand {
/// Run compaction check
Compact,
/// Run memory scoring
/// Run incremental memory scoring (auto, after turns)
Score,
/// Run full N×M memory scoring matrix (/score command)
ScoreFull,
/// Abort current turn, kill processes
Interrupt,
/// Reset session
@ -362,6 +364,18 @@ impl Mind {
s.scoring_in_flight = true;
drop(s);
self.start_memory_scoring();
} else {
dbglog!("[scoring] skipped: scoring_in_flight=true");
}
}
MindCommand::ScoreFull => {
let mut s = self.shared.lock().unwrap();
if !s.scoring_in_flight {
s.scoring_in_flight = true;
drop(s);
self.start_full_scoring();
} else {
dbglog!("[scoring-full] skipped: scoring_in_flight=true");
}
}
MindCommand::Interrupt => {
@ -406,7 +420,10 @@ impl Mind {
tokio::spawn(async move {
let (context, client) = {
let mut st = agent.state.lock().await;
if st.memory_scoring_in_flight { return; }
if st.memory_scoring_in_flight {
dbglog!("[scoring] skipped: memory_scoring_in_flight=true");
return;
}
st.memory_scoring_in_flight = true;
drop(st);
let ctx = agent.context.lock().await.clone();
@ -445,6 +462,28 @@ impl Mind {
});
}
/// Run full N×M scoring matrix — scores every memory against every response.
pub fn start_full_scoring(&self) {
let agent = self.agent.clone();
let bg_tx = self.bg_tx.clone();
tokio::spawn(async move {
{
let mut st = agent.state.lock().await;
if st.memory_scoring_in_flight {
dbglog!("[scoring-full] skipped: memory_scoring_in_flight=true");
return;
}
st.memory_scoring_in_flight = true;
}
let client = agent.client.clone();
match learn::score_memories(&client, &agent).await {
Ok(()) => { let _ = bg_tx.send(BgEvent::ScoringDone); }
Err(e) => { dbglog!("[scoring-full] FAILED: {:#}", e); }
}
agent.state.lock().await.memory_scoring_in_flight = false;
});
}
async fn start_turn(&self, text: &str, target: StreamTarget) {
{
match target {