use crate::agent::swarm::knowledge::SharedKnowledge;
use crate::search::tokenizer;
impl SharedKnowledge {
pub async fn build_context_summary(&self) -> String {
let inner = self.inner.read().await;
let mut parts = Vec::new();
if !inner.files_read.is_empty() {
parts.push("## Files Already Read\n".to_string());
for (path, summary) in &inner.files_read {
parts.push(format!(
"- `{path}` ({} lines): {}",
summary.line_count, summary.summary
));
}
}
if !inner.facts.is_empty() {
parts.push("\n## Shared Discoveries\n".to_string());
for fact in inner.facts.values() {
let age_secs = fact.timestamp.elapsed().as_secs();
parts.push(format!(
"- [{}] (id={}, {}s ago) {}: {}",
fact.agent_id, fact.id, age_secs, fact.topic, fact.content
));
}
}
if !inner.announcements.is_empty() {
parts.push("\n## Announcements\n".to_string());
for ann in inner.announcements.iter().rev().take(10) {
let age_secs = ann.timestamp.elapsed().as_secs();
parts.push(format!(
"- [{}] (id={}, {}s ago) {}",
ann.agent_id, ann.id, age_secs, ann.message
));
}
}
if !inner.blackboard.is_empty() {
parts.push("\n## Blackboard\n".to_string());
for entry in inner.blackboard.values() {
let votes = format!("+{} -{}", entry.votes_for.len(), entry.votes_against.len());
let age_secs = entry.timestamp.elapsed().as_secs();
parts.push(format!(
"- [{:?}] {} = {} (by {}, votes: {}, {}s ago)",
entry.kind, entry.key, entry.value, entry.author, votes, age_secs
));
}
}
parts.join("\n")
}
pub async fn build_relevant_summary(
&self,
subtask_prompt: &str,
target_files: &[String],
max_items: usize,
) -> String {
let inner = self.inner.read().await;
if inner.index.is_empty() {
return String::new();
}
let query_tokens = tokenizer::tokenize_query(subtask_prompt);
if query_tokens.is_empty() {
drop(inner);
return self.build_context_summary().await;
}
let ranked = inner.rank_entries(&query_tokens, target_files, max_items);
if ranked.is_empty() {
return String::new();
}
let mut file_entries = Vec::new();
let mut fact_entries = Vec::new();
let mut ann_entries = Vec::new();
for (idx, score) in &ranked {
let entry = &inner.index[*idx];
let line = format!("[{}] {} (relevance: {:.1})", entry.id, entry.display, score);
match entry.kind {
crate::agent::swarm::knowledge::types::EntryKind::FileRead => {
file_entries.push(line)
}
crate::agent::swarm::knowledge::types::EntryKind::Fact => fact_entries.push(line),
crate::agent::swarm::knowledge::types::EntryKind::Announcement => {
ann_entries.push(line)
}
}
}
let mut parts = Vec::new();
if !file_entries.is_empty() {
parts.push("## Relevant Files Read\n".to_string());
parts.extend(file_entries);
}
if !fact_entries.is_empty() {
parts.push("\n## Relevant Discoveries\n".to_string());
parts.extend(fact_entries);
}
if !ann_entries.is_empty() {
parts.push("\n## Recent Announcements\n".to_string());
parts.extend(ann_entries);
}
parts.join("\n")
}
pub async fn batch_relevant_summaries(
&self,
tasks: &[(String, Vec<String>)], max_items: usize,
) -> Vec<String> {
let inner = self.inner.read().await;
if inner.index.is_empty() {
return vec![String::new(); tasks.len()];
}
tasks
.iter()
.map(|(prompt, target_files)| {
let query_tokens = tokenizer::tokenize_query(prompt);
if query_tokens.is_empty() {
return String::new();
}
let ranked = inner.rank_entries(&query_tokens, target_files, max_items);
if ranked.is_empty() {
return String::new();
}
let mut file_entries = Vec::new();
let mut fact_entries = Vec::new();
let mut ann_entries = Vec::new();
for (idx, score) in &ranked {
let entry = &inner.index[*idx];
let line = format!("{} (relevance: {:.1})", entry.display, score);
match entry.kind {
crate::agent::swarm::knowledge::types::EntryKind::FileRead => {
file_entries.push(line)
}
crate::agent::swarm::knowledge::types::EntryKind::Fact => {
fact_entries.push(line)
}
crate::agent::swarm::knowledge::types::EntryKind::Announcement => {
ann_entries.push(line)
}
}
}
let mut parts = Vec::new();
if !file_entries.is_empty() {
parts.push("## Relevant Files Read\n".to_string());
parts.extend(file_entries);
}
if !fact_entries.is_empty() {
parts.push("\n## Relevant Discoveries\n".to_string());
parts.extend(fact_entries);
}
if !ann_entries.is_empty() {
parts.push("\n## Recent Announcements\n".to_string());
parts.extend(ann_entries);
}
parts.join("\n")
})
.collect()
}
}