defs.rs: remove store/graph params, use typed memory API

resolve_placeholders() and run_agent() no longer take &Store.
All placeholders now use async memory_render/memory_links/memory_query
directly. The "siblings" placeholder uses Vec<LinkInfo> for ranking
neighbors by link_strength * node_weight.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-13 15:18:05 -04:00
parent 598f0112a4
commit 419bb222b5
4 changed files with 19 additions and 26 deletions

View file

@ -405,7 +405,7 @@ pub async fn run_one_agent(
let mut all_keys: Vec<String> = keys.to_vec();
for step in &def.steps {
let (prompt, extra_keys) = defs::resolve_placeholders(
&step.prompt, store, keys, count,
&step.prompt, keys, count,
).await;
all_keys.extend(extra_keys);
resolved_steps.push(prompts::ResolvedStep {
@ -420,7 +420,7 @@ pub async fn run_one_agent(
batch
} else {
let effective_count = def.count.unwrap_or(count);
defs::run_agent(store, &def, effective_count, &Default::default()).await?
defs::run_agent(&def, effective_count, &Default::default()).await?
};
// Base memory tools + extras from agent def (matching unconscious.rs pattern)

View file

@ -264,7 +264,7 @@ pub async fn prepare_spawn(name: &str, mut auto: AutoAgent) -> Result<SpawnResul
let exclude: std::collections::HashSet<String> = std::collections::HashSet::new();
let batch = match defs::run_agent(
&store, &def, def.count.unwrap_or(5), &exclude,
&def, def.count.unwrap_or(5), &exclude,
).await {
Ok(b) => b,
Err(e) => {

View file

@ -15,8 +15,6 @@
// The query selects what to operate on; placeholders pull in context.
use crate::agent::tools::memory::memory_render;
use crate::graph::Graph;
use crate::store::Store;
use serde::Deserialize;
@ -201,8 +199,6 @@ struct Resolved {
/// Returns the replacement text and any node keys it produced (for visit tracking).
async fn resolve(
name: &str,
store: &Store,
graph: &Graph,
keys: &[String],
_count: usize,
) -> Option<Resolved> {
@ -245,6 +241,7 @@ async fn resolve(
}
"siblings" | "neighborhood" => {
use crate::agent::tools::memory::{memory_render, memory_links};
const MAX_NEIGHBORS: usize = 20;
const BUDGET: usize = 400_000; // ~100K tokens
@ -255,19 +252,18 @@ async fn resolve(
for key in keys {
if included.contains(key) { continue; }
included.insert(key.clone());
let Some(node) = store.nodes.get(key.as_str()) else { continue };
// Seed node with full content
out.push_str(&format!("## {} (seed)\n\n{}\n\n", key, node.content));
let Ok(content) = memory_render(None, key, Some(true)).await else { continue };
out.push_str(&format!("## {} (seed)\n\n{}\n\n", key, content));
all_keys.push(key.clone());
// Rank neighbors by link_strength * node_weight, take top 20
let mut ranked: Vec<_> = graph.neighbors(key).iter()
.filter_map(|(nbr, strength)| {
store.nodes.get(nbr.as_str()).map(|n| {
let score = strength * n.weight.max(0.01);
(nbr.to_string(), *strength, score)
})
// Get neighbors with link_strength and node_weight, rank and take top 20
let Ok(links) = memory_links(None, key).await else { continue };
let mut ranked: Vec<_> = links.into_iter()
.map(|l| {
let score = l.link_strength * l.node_weight.max(0.01);
(l.key, l.link_strength, score)
})
.collect();
ranked.sort_by(|a, b| b.2.total_cmp(&a.2));
@ -279,15 +275,15 @@ async fn resolve(
for (nbr, strength, _) in &ranked {
if included.contains(nbr) { continue; }
included.insert(nbr.clone());
if let Some(n) = store.nodes.get(nbr.as_str()) {
if let Ok(content) = memory_render(None, nbr, Some(true)).await {
if out.len() > BUDGET {
// Header-only past budget
let first = n.content.lines()
let first = content.lines()
.find(|l| !l.trim().is_empty())
.unwrap_or("(empty)");
out.push_str(&format!("#### {} ({:.2}) — {}\n", nbr, strength, first));
} else {
out.push_str(&format!("#### {} ({:.2})\n\n{}\n\n", nbr, strength, n.content));
out.push_str(&format!("#### {} ({:.2})\n\n{}\n\n", nbr, strength, content));
}
all_keys.push(nbr.to_string());
}
@ -558,11 +554,9 @@ async fn resolve_tool(spec: &str) -> Option<Resolved> {
/// Returns the resolved text and all node keys collected from placeholders.
pub async fn resolve_placeholders(
template: &str,
store: &Store,
keys: &[String],
count: usize,
) -> (String, Vec<String>) {
let graph = store.build_graph();
let mut result = template.to_string();
let mut extra_keys = Vec::new();
let mut pos = 0;
@ -572,7 +566,7 @@ pub async fn resolve_placeholders(
let Some(rel_end) = result[start + 2..].find("}}") else { break };
let end = start + 2 + rel_end;
let name = result[start + 2..end].trim().to_lowercase();
match resolve(&name, store, &graph, keys, count).await {
match resolve(&name, keys, count).await {
Some(resolved) => {
let len = resolved.text.len();
extra_keys.extend(resolved.keys);
@ -594,7 +588,6 @@ pub async fn resolve_placeholders(
/// `exclude` filters out nodes (and their neighborhoods) already being
/// worked on by other agents, preventing concurrent collisions.
pub async fn run_agent(
store: &Store,
def: &AgentDef,
count: usize,
exclude: &std::collections::HashSet<String>,
@ -635,7 +628,7 @@ pub async fn run_agent(
.replace("{agent_name}", &def.agent)
.replace("{user_name}", &cfg.user_name)
.replace("{assistant_name}", &cfg.assistant_name);
let (prompt, extra_keys) = resolve_placeholders(&template, store, &all_keys, count).await;
let (prompt, extra_keys) = resolve_placeholders(&template, &all_keys, count).await;
all_keys.extend(extra_keys);
resolved_steps.push(super::prompts::ResolvedStep {
prompt,

View file

@ -212,8 +212,8 @@ pub fn format_health_section(store: &Store, graph: &Graph) -> String {
}
/// Generate a specific agent prompt with filled-in data.
pub async fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<AgentBatch, String> {
pub async fn agent_prompt(agent: &str, count: usize) -> Result<AgentBatch, String> {
let def = super::defs::get_def(agent)
.ok_or_else(|| format!("Unknown agent: {}", agent))?;
super::defs::run_agent(store, &def, count, &Default::default()).await
super::defs::run_agent(&def, count, &Default::default()).await
}