forked from kent/consciousness
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:
parent
598f0112a4
commit
419bb222b5
4 changed files with 19 additions and 26 deletions
|
|
@ -405,7 +405,7 @@ pub async fn run_one_agent(
|
||||||
let mut all_keys: Vec<String> = keys.to_vec();
|
let mut all_keys: Vec<String> = keys.to_vec();
|
||||||
for step in &def.steps {
|
for step in &def.steps {
|
||||||
let (prompt, extra_keys) = defs::resolve_placeholders(
|
let (prompt, extra_keys) = defs::resolve_placeholders(
|
||||||
&step.prompt, store, keys, count,
|
&step.prompt, keys, count,
|
||||||
).await;
|
).await;
|
||||||
all_keys.extend(extra_keys);
|
all_keys.extend(extra_keys);
|
||||||
resolved_steps.push(prompts::ResolvedStep {
|
resolved_steps.push(prompts::ResolvedStep {
|
||||||
|
|
@ -420,7 +420,7 @@ pub async fn run_one_agent(
|
||||||
batch
|
batch
|
||||||
} else {
|
} else {
|
||||||
let effective_count = def.count.unwrap_or(count);
|
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)
|
// Base memory tools + extras from agent def (matching unconscious.rs pattern)
|
||||||
|
|
|
||||||
|
|
@ -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 exclude: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||||
let batch = match defs::run_agent(
|
let batch = match defs::run_agent(
|
||||||
&store, &def, def.count.unwrap_or(5), &exclude,
|
&def, def.count.unwrap_or(5), &exclude,
|
||||||
).await {
|
).await {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@
|
||||||
// The query selects what to operate on; placeholders pull in context.
|
// The query selects what to operate on; placeholders pull in context.
|
||||||
|
|
||||||
use crate::agent::tools::memory::memory_render;
|
use crate::agent::tools::memory::memory_render;
|
||||||
use crate::graph::Graph;
|
|
||||||
use crate::store::Store;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
@ -201,8 +199,6 @@ struct Resolved {
|
||||||
/// Returns the replacement text and any node keys it produced (for visit tracking).
|
/// Returns the replacement text and any node keys it produced (for visit tracking).
|
||||||
async fn resolve(
|
async fn resolve(
|
||||||
name: &str,
|
name: &str,
|
||||||
store: &Store,
|
|
||||||
graph: &Graph,
|
|
||||||
keys: &[String],
|
keys: &[String],
|
||||||
_count: usize,
|
_count: usize,
|
||||||
) -> Option<Resolved> {
|
) -> Option<Resolved> {
|
||||||
|
|
@ -245,6 +241,7 @@ async fn resolve(
|
||||||
}
|
}
|
||||||
|
|
||||||
"siblings" | "neighborhood" => {
|
"siblings" | "neighborhood" => {
|
||||||
|
use crate::agent::tools::memory::{memory_render, memory_links};
|
||||||
const MAX_NEIGHBORS: usize = 20;
|
const MAX_NEIGHBORS: usize = 20;
|
||||||
const BUDGET: usize = 400_000; // ~100K tokens
|
const BUDGET: usize = 400_000; // ~100K tokens
|
||||||
|
|
||||||
|
|
@ -255,19 +252,18 @@ async fn resolve(
|
||||||
for key in keys {
|
for key in keys {
|
||||||
if included.contains(key) { continue; }
|
if included.contains(key) { continue; }
|
||||||
included.insert(key.clone());
|
included.insert(key.clone());
|
||||||
let Some(node) = store.nodes.get(key.as_str()) else { continue };
|
|
||||||
|
|
||||||
// Seed node with full content
|
// 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());
|
all_keys.push(key.clone());
|
||||||
|
|
||||||
// Rank neighbors by link_strength * node_weight, take top 20
|
// Get neighbors with link_strength and node_weight, rank and take top 20
|
||||||
let mut ranked: Vec<_> = graph.neighbors(key).iter()
|
let Ok(links) = memory_links(None, key).await else { continue };
|
||||||
.filter_map(|(nbr, strength)| {
|
let mut ranked: Vec<_> = links.into_iter()
|
||||||
store.nodes.get(nbr.as_str()).map(|n| {
|
.map(|l| {
|
||||||
let score = strength * n.weight.max(0.01);
|
let score = l.link_strength * l.node_weight.max(0.01);
|
||||||
(nbr.to_string(), *strength, score)
|
(l.key, l.link_strength, score)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
ranked.sort_by(|a, b| b.2.total_cmp(&a.2));
|
ranked.sort_by(|a, b| b.2.total_cmp(&a.2));
|
||||||
|
|
@ -279,15 +275,15 @@ async fn resolve(
|
||||||
for (nbr, strength, _) in &ranked {
|
for (nbr, strength, _) in &ranked {
|
||||||
if included.contains(nbr) { continue; }
|
if included.contains(nbr) { continue; }
|
||||||
included.insert(nbr.clone());
|
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 {
|
if out.len() > BUDGET {
|
||||||
// Header-only past budget
|
// Header-only past budget
|
||||||
let first = n.content.lines()
|
let first = content.lines()
|
||||||
.find(|l| !l.trim().is_empty())
|
.find(|l| !l.trim().is_empty())
|
||||||
.unwrap_or("(empty)");
|
.unwrap_or("(empty)");
|
||||||
out.push_str(&format!("#### {} ({:.2}) — {}\n", nbr, strength, first));
|
out.push_str(&format!("#### {} ({:.2}) — {}\n", nbr, strength, first));
|
||||||
} else {
|
} 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());
|
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.
|
/// Returns the resolved text and all node keys collected from placeholders.
|
||||||
pub async fn resolve_placeholders(
|
pub async fn resolve_placeholders(
|
||||||
template: &str,
|
template: &str,
|
||||||
store: &Store,
|
|
||||||
keys: &[String],
|
keys: &[String],
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> (String, Vec<String>) {
|
) -> (String, Vec<String>) {
|
||||||
let graph = store.build_graph();
|
|
||||||
let mut result = template.to_string();
|
let mut result = template.to_string();
|
||||||
let mut extra_keys = Vec::new();
|
let mut extra_keys = Vec::new();
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
|
|
@ -572,7 +566,7 @@ pub async fn resolve_placeholders(
|
||||||
let Some(rel_end) = result[start + 2..].find("}}") else { break };
|
let Some(rel_end) = result[start + 2..].find("}}") else { break };
|
||||||
let end = start + 2 + rel_end;
|
let end = start + 2 + rel_end;
|
||||||
let name = result[start + 2..end].trim().to_lowercase();
|
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) => {
|
Some(resolved) => {
|
||||||
let len = resolved.text.len();
|
let len = resolved.text.len();
|
||||||
extra_keys.extend(resolved.keys);
|
extra_keys.extend(resolved.keys);
|
||||||
|
|
@ -594,7 +588,6 @@ pub async fn resolve_placeholders(
|
||||||
/// `exclude` filters out nodes (and their neighborhoods) already being
|
/// `exclude` filters out nodes (and their neighborhoods) already being
|
||||||
/// worked on by other agents, preventing concurrent collisions.
|
/// worked on by other agents, preventing concurrent collisions.
|
||||||
pub async fn run_agent(
|
pub async fn run_agent(
|
||||||
store: &Store,
|
|
||||||
def: &AgentDef,
|
def: &AgentDef,
|
||||||
count: usize,
|
count: usize,
|
||||||
exclude: &std::collections::HashSet<String>,
|
exclude: &std::collections::HashSet<String>,
|
||||||
|
|
@ -635,7 +628,7 @@ pub async fn run_agent(
|
||||||
.replace("{agent_name}", &def.agent)
|
.replace("{agent_name}", &def.agent)
|
||||||
.replace("{user_name}", &cfg.user_name)
|
.replace("{user_name}", &cfg.user_name)
|
||||||
.replace("{assistant_name}", &cfg.assistant_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);
|
all_keys.extend(extra_keys);
|
||||||
resolved_steps.push(super::prompts::ResolvedStep {
|
resolved_steps.push(super::prompts::ResolvedStep {
|
||||||
prompt,
|
prompt,
|
||||||
|
|
|
||||||
|
|
@ -212,8 +212,8 @@ pub fn format_health_section(store: &Store, graph: &Graph) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a specific agent prompt with filled-in data.
|
/// 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)
|
let def = super::defs::get_def(agent)
|
||||||
.ok_or_else(|| format!("Unknown agent: {}", 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue