feat: Add Brain Monitor dashboard and live API stats

This commit is contained in:
Riz Ashraf committed 2026-09-08 15:09:44 +01:00
1 parent 3239e32866
commit 18fd05bb11
10 files changed
+672 -80

No files matched your search

+63 -71
View File
@@ -120,6 +120,9 @@ impl ServerHandler for MemoryHandler {
} {
let entity: Entity = e;
if !entity.name.is_empty() {
if let Ok(idx) = self.state.search_index.read() {
let _ = idx.index_entity(&entity);
}
g.entities.insert(entity.name.clone(), entity);
}
}
@@ -267,29 +270,19 @@ impl ServerHandler for MemoryHandler {
}
"search_nodes" => {
let req: SearchNodesTool = parse_args(args)?;
let query = req.query.to_lowercase();
let tokens: Vec<&str> = query.split_whitespace().collect();
let matches = if let Ok(idx) = self.state.search_index.read() {
idx.search(&req.query, req.namespace.as_deref()).unwrap_or_default()
} else {
vec![]
};
let mut result = KnowledgeGraph::default();
for (name, e) in self.state.get_full_graph().entities {
if let Some(ns) = &req.namespace
&& e.namespace != *ns
{
continue;
}
let name_low = name.to_lowercase();
let type_low = e.entity_type.to_lowercase();
let full_txt = format!(
"{} {} {}",
name_low,
type_low,
e.observations.join(" ").to_lowercase()
);
let matched = full_txt.contains(&query)
|| (!tokens.is_empty() && tokens.iter().all(|&t| full_txt.contains(t)))
|| strsim::jaro_winkler(&name_low, &query) > 0.85
|| strsim::jaro_winkler(&type_low, &query) > 0.85;
if matched {
result.entities.insert(name, e);
let full = self.state.get_full_graph();
for (id, doc_type) in matches {
if doc_type == "entity" {
if let Some(e) = full.entities.get(&id) {
result.entities.insert(id, e.clone());
}
}
}
let data = serde_json::to_string(&result).unwrap_or_default();
@@ -456,16 +449,20 @@ impl ServerHandler for MemoryHandler {
.unwrap()
.as_secs();
let id = uuid::Uuid::new_v4().to_string();
let task = Task {
id: id.clone(),
title: req.title,
status: "pending".to_string(),
description: req.description,
created_at: now,
updated_at: now,
git_branch: req.git_branch,
};
if let Ok(idx) = self.state.search_index.read() {
let _ = idx.index_task(&task);
}
self.state.tasks.modify(|tasks| {
tasks.push(Task {
id: id.clone(),
title: req.title,
status: "pending".to_string(),
description: req.description,
created_at: now,
updated_at: now,
git_branch: req.git_branch,
});
tasks.push(task);
});
Ok(ServerResult::from(CallToolResult::text_content(vec![
format!("Task added with ID: {}", id).into(),
@@ -1098,47 +1095,42 @@ impl ServerHandler for MemoryHandler {
}
"omni_search" => {
let req: OmniSearchTool = parse_args(args)?;
let matches = if let Ok(idx) = self.state.search_index.read() {
idx.search(&req.query, req.namespace.as_deref()).unwrap_or_default()
} else {
vec![]
};
let mut kg = KnowledgeGraph::default();
let mut tasks = Vec::new();
let mut snippets = Vec::new();
let mut adrs = Vec::new();
let full = self.state.get_full_graph();
for (id, doc_type) in &matches {
if doc_type == "entity" {
if let Some(e) = full.entities.get(id) {
kg.entities.insert(id.clone(), e.clone());
}
}
}
for t in self.state.tasks.read() {
if matches.iter().any(|(id, typ)| id == &t.id && typ == "task") {
tasks.push(t);
}
}
for s in self.state.snippets.read() {
if matches.iter().any(|(id, typ)| id == &s.name && typ == "snippet") {
snippets.push(s);
}
}
for a in self.state.adrs.read() {
if matches.iter().any(|(id, typ)| id == &a.id && typ == "adr") {
adrs.push(a);
}
}
let q = req.query.to_lowercase();
let mut kg = self.state.get_full_graph();
kg.entities.retain(|name, e| {
(req.namespace.is_none() || e.namespace == *req.namespace.as_ref().unwrap())
&& (name.to_lowercase().contains(&q)
|| e.entity_type.to_lowercase().contains(&q)
|| e.observations.join(" ").to_lowercase().contains(&q))
});
let tasks: Vec<_> = self
.state
.tasks
.read()
.into_iter()
.filter(|t| {
t.title.to_lowercase().contains(&q)
|| t.description.to_lowercase().contains(&q)
})
.collect();
let snippets: Vec<_> = self
.state
.snippets
.read()
.into_iter()
.filter(|s| {
s.name.to_lowercase().contains(&q)
|| s.description.to_lowercase().contains(&q)
})
.collect();
let adrs: Vec<_> = self
.state
.adrs
.read()
.into_iter()
.filter(|a| {
a.title.to_lowercase().contains(&q)
|| a.context.to_lowercase().contains(&q)
|| a.decision.to_lowercase().contains(&q)
})
.collect();
let tech_debts: Vec<_> = self
.state
.tech_debts