refactor(memory_selector): Phase B language-advantage pass — dichotomy wave
Unify the four self-scored decision kernels' shared floor/tag/cap loop into one generic floored_rows iterator consumer — neurons/graphs/ abstraction/consolidated each keep ONLY their scoring (and the neuron trigger-scan prefilter), dropping four hand-rolled copies of the same below-floor-drop, tag, budget-cap shape. Behavior unchanged — battery 9/9, clippy --all-targets -D warnings 0, live parity 536/536 against the frozen oracle.
This commit is contained in:
1 parent
6bdd28521a
commit
f2ff903e65
1 file changed
+59
-70
@@ -157,6 +157,35 @@ pub fn select_core(candidates: &[Value], budget: i64, min_score: f64, types: &[S
|
||||
})
|
||||
}
|
||||
|
||||
/// Shared per-source row loop (Phase B): the floor/tag/cap shape every
|
||||
/// self-scored decision kernel shares — below-floor rows drop, survivors
|
||||
/// are tagged {text, score, kind, source}, the cap stops the scan. Callers
|
||||
/// own only the scoring (and any prefilter, e.g. the neuron trigger scan).
|
||||
fn floored_rows(
|
||||
pairs: impl Iterator<Item = (String, f64)>,
|
||||
kind: &str,
|
||||
source: &str,
|
||||
budget: i64,
|
||||
min_score: f64,
|
||||
) -> Vec<Value> {
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for (text, score) in pairs {
|
||||
if score < min_score {
|
||||
continue;
|
||||
}
|
||||
out.push(json!({
|
||||
"text": text,
|
||||
"score": score,
|
||||
"kind": kind,
|
||||
"source": source,
|
||||
}));
|
||||
if out.len() >= budget.max(0) as usize {
|
||||
break;
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// _neurons_recall's post-IO decision math: trigger scan (a neuron that does
|
||||
/// not match the task is not injected merely because it is important), lexical
|
||||
/// scoring, the ONE floor, budget cap.
|
||||
@@ -169,8 +198,7 @@ pub fn neurons_decision(neurons: &[Value], query: &str, budget: i64, min_score:
|
||||
.to_string()
|
||||
})
|
||||
.collect();
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for neuron in neurons {
|
||||
let pairs = neurons.iter().filter_map(|neuron| {
|
||||
let triggers = match neuron.get("triggers") {
|
||||
Some(Value::Array(t)) => t.clone(),
|
||||
_ => Vec::new(),
|
||||
@@ -180,30 +208,21 @@ pub fn neurons_decision(neurons: &[Value], query: &str, budget: i64, min_score:
|
||||
.map(|s| task_words.contains(&s.to_string()))
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
continue;
|
||||
return None;
|
||||
}
|
||||
let text = py_str(neuron.get("text"));
|
||||
let score = lex_overlap_score(&text, query);
|
||||
if score < min_score {
|
||||
continue;
|
||||
}
|
||||
let slug = py_str(neuron.get("slug"));
|
||||
let slug = if slug.is_empty() {
|
||||
"warm".to_string()
|
||||
} else {
|
||||
slug
|
||||
};
|
||||
out.push(json!({
|
||||
"text": format!("[{slug}] {text}"),
|
||||
"score": score,
|
||||
"kind": "neuron",
|
||||
"source": "warm_neuron_store.list_neurons",
|
||||
}));
|
||||
if out.len() >= budget.max(0) as usize {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Value::Array(out)
|
||||
let slug = if slug.is_empty() { "warm".to_string() } else { slug };
|
||||
Some((format!("[{slug}] {text}"), score))
|
||||
});
|
||||
Value::Array(floored_rows(
|
||||
pairs,
|
||||
"neuron",
|
||||
"warm_neuron_store.list_neurons",
|
||||
budget,
|
||||
min_score,
|
||||
))
|
||||
}
|
||||
|
||||
/// _graphs_recall's post-IO decision math: lexical score with the
|
||||
@@ -215,50 +234,26 @@ pub fn graphs_decision(
|
||||
budget: i64,
|
||||
min_score: f64,
|
||||
) -> Value {
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for tv in texts {
|
||||
let pairs = texts.iter().map(|tv| {
|
||||
let text = py_str(Some(tv));
|
||||
let mut score = lex_overlap_score(&text, query);
|
||||
if score == 0.0 {
|
||||
score = 1.0 / std::cmp::max(n_task_words, 1) as f64;
|
||||
}
|
||||
if score < min_score {
|
||||
continue;
|
||||
}
|
||||
out.push(json!({
|
||||
"text": text,
|
||||
"score": score,
|
||||
"kind": "graph",
|
||||
"source": "graph_memory.query",
|
||||
}));
|
||||
if out.len() >= budget.max(0) as usize {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Value::Array(out)
|
||||
(text, score)
|
||||
});
|
||||
Value::Array(floored_rows(pairs, "graph", "graph_memory.query", budget, min_score))
|
||||
}
|
||||
|
||||
/// _abstraction_recall's post-IO decision math: LIKE matches are only
|
||||
/// candidates — each must clear the floor.
|
||||
pub fn abstraction_decision(whats: &[Value], query: &str, budget: i64, min_score: f64) -> Value {
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for wv in whats {
|
||||
let pairs = whats.iter().map(|wv| {
|
||||
let text = py_str(Some(wv));
|
||||
let score = lex_overlap_score(&text, query);
|
||||
if score < min_score {
|
||||
continue;
|
||||
}
|
||||
out.push(json!({
|
||||
"text": text,
|
||||
"score": score,
|
||||
"kind": "abstraction",
|
||||
"source": "abstraction facts",
|
||||
}));
|
||||
if out.len() >= budget.max(0) as usize {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Value::Array(out)
|
||||
(text, score)
|
||||
});
|
||||
Value::Array(floored_rows(pairs, "abstraction", "abstraction facts", budget, min_score))
|
||||
}
|
||||
|
||||
/// _consolidated_recall's post-IO decision math: knowledge competes under the
|
||||
@@ -269,24 +264,18 @@ pub fn consolidated_decision(
|
||||
budget: i64,
|
||||
min_score: f64,
|
||||
) -> Value {
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for cv in contents {
|
||||
let pairs = contents.iter().map(|cv| {
|
||||
let text = py_str(Some(cv));
|
||||
let score = lex_overlap_score(&text, query);
|
||||
if score < min_score {
|
||||
continue;
|
||||
}
|
||||
out.push(json!({
|
||||
"text": text,
|
||||
"score": score,
|
||||
"kind": "consolidated",
|
||||
"source": "memory_consolidation",
|
||||
}));
|
||||
if out.len() >= budget.max(0) as usize {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Value::Array(out)
|
||||
(text, score)
|
||||
});
|
||||
Value::Array(floored_rows(
|
||||
pairs,
|
||||
"consolidated",
|
||||
"memory_consolidation",
|
||||
budget,
|
||||
min_score,
|
||||
))
|
||||
}
|
||||
|
||||
/// _persona_recall's pre-IO gate: only self/personality types run the persona
|
||||
|
||||
Reference in new issue
Block a user