perf(mcp): Replace redundant LLM usages with deterministic logic
- Refactored memory_consolidation_worker to use purely deterministic string normalization ((N)$ regex stripping & lowercase) instead of passing graph entities to Ollama for duplication detection. - Removed redundant Ollama architectural summarization from log_code_change tool since the agent already provides a descriptive summary. - Removed redundant Ollama fix summarization from log_error_fix tool since the agent already provides a solution string.
This commit is contained in:
1 parent
26eaf03b6f
commit
e006ef1220
4 files changed
+77
-90
No files matched your search
@@ -0,0 +1,6 @@
|
||||
use mcp_memory_server::tools::{DecisionsTool, TechDebtTool, ManageCheckpointTool, HypothesesTool, AgentSignalsTool};
|
||||
use schemars::schema_for;
|
||||
|
||||
fn main() {
|
||||
println!("{}", serde_json::to_string_pretty(&schema_for!(AgentSignalsTool)).unwrap());
|
||||
}
|
||||
@@ -24,24 +24,7 @@ impl McpTool for LogErrorFixHandler {
|
||||
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
let req: LogErrorFixTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
||||
let text_to_embed = format!("Signature: {}\nSolution: {}", req.signature, req.solution);
|
||||
let mut solution = req.solution;
|
||||
|
||||
if state.ollama.is_available().await {
|
||||
let prompt = format!(
|
||||
"Analyze this error signature and solution. Output 1 sentence summarizing the root cause and fix:\nSignature: {}\nSolution: {}",
|
||||
req.signature, solution
|
||||
);
|
||||
if let Ok(summary) = state
|
||||
.ollama
|
||||
.generate(&prompt, Some(&state.ollama.reasoning_model), None, None)
|
||||
.await
|
||||
{
|
||||
let clean = summary.trim();
|
||||
if !clean.is_empty() {
|
||||
solution = format!("{} (AI Analysis: {})", solution, clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
let solution = req.solution;
|
||||
|
||||
let embedding = crate::embedding::generate_embedding_async(text_to_embed)
|
||||
.await
|
||||
@@ -204,18 +187,6 @@ impl McpTool for LogCodeChangeHandler {
|
||||
description = format!("{} [Symbols: {}]", description, symbols.join(", "));
|
||||
}
|
||||
|
||||
if state.ollama.is_available().await {
|
||||
let prompt = format!(
|
||||
"Summarize in 1 concise sentence the architectural impact of changing file '{}': {}",
|
||||
req.file_path, description
|
||||
);
|
||||
if let Ok(summary) = state.ollama.generate(&prompt, None, None, None).await {
|
||||
let clean = summary.trim();
|
||||
if !clean.is_empty() {
|
||||
description = format!("{} (AI Summary: {})", description, clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
let change_kind = match req
|
||||
.change_kind
|
||||
.as_deref()
|
||||
|
||||
+45
-60
@@ -285,76 +285,61 @@ pub async fn memory_consolidation_worker(state: Arc<MemoryState>) {
|
||||
_ = interval.tick() => {},
|
||||
}
|
||||
|
||||
let entities: Vec<_> = state.graph.read_with(|g| {
|
||||
g.entities
|
||||
.values()
|
||||
.map(|e| (e.name.clone(), e.entity_type.clone()))
|
||||
.collect()
|
||||
let entities: Vec<String> = state.graph.read_with(|g| {
|
||||
g.entities.keys().cloned().collect()
|
||||
});
|
||||
|
||||
if entities.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut entity_summaries = String::new();
|
||||
for (name, e_type) in entities.iter().take(50) {
|
||||
entity_summaries.push_str(&format!("- [{}] {}\n", e_type, name));
|
||||
}
|
||||
let mut duplicates = None;
|
||||
let mut seen = std::collections::HashMap::new();
|
||||
for name in &entities {
|
||||
let normalized: String = name
|
||||
.chars()
|
||||
.filter(|c| c.is_alphanumeric())
|
||||
.flat_map(|c| c.to_lowercase())
|
||||
.collect();
|
||||
|
||||
// Skip empty normalized names
|
||||
if normalized.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let prompt = format!(
|
||||
"Analyze the following list of entities and identify exactly TWO that represent the exact same concept or item but have slightly different names (e.g. 'auth_service' and 'AuthService'). Return ONLY a valid JSON array containing exactly two strings: the two names to merge. If no obvious duplicates exist, return an empty array []. Do not output any markdown formatting or extra text.\n\nEntities:\n{}",
|
||||
entity_summaries
|
||||
);
|
||||
|
||||
if let Ok(response) = state
|
||||
.ollama
|
||||
.generate(
|
||||
&prompt,
|
||||
None,
|
||||
Some("You are a helpful JSON-only data deduplication assistant. Output only JSON."),
|
||||
Some("json"),
|
||||
)
|
||||
.await
|
||||
{
|
||||
let cleaned = response
|
||||
.trim()
|
||||
.trim_start_matches("```json")
|
||||
.trim_start_matches("```")
|
||||
.trim_end_matches("```")
|
||||
.trim();
|
||||
if let Ok(duplicates) = serde_json::from_str::<Vec<String>>(cleaned)
|
||||
&& duplicates.len() == 2
|
||||
{
|
||||
let e1_name = &duplicates[0];
|
||||
let e2_name = &duplicates[1];
|
||||
|
||||
if e1_name != e2_name {
|
||||
tracing::info!(
|
||||
"Memory Consolidation Daemon: Merging '{}' into '{}'",
|
||||
e2_name,
|
||||
e1_name
|
||||
);
|
||||
state.modify_graph(|g| {
|
||||
if let Some(mut e2) = g.entities.remove(e2_name) {
|
||||
if let Some(e1) = g.entities.get_mut(e1_name) {
|
||||
e1.observations.append(&mut e2.observations);
|
||||
} else {
|
||||
g.entities.insert(e2_name.clone(), e2);
|
||||
}
|
||||
}
|
||||
|
||||
for rel in g.relations.iter_mut() {
|
||||
if rel.from == *e2_name {
|
||||
rel.from = e1_name.clone();
|
||||
}
|
||||
if rel.to == *e2_name {
|
||||
rel.to = e1_name.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Some(existing) = seen.insert(normalized, name.clone()) {
|
||||
if existing != *name {
|
||||
duplicates = Some((existing, name.clone()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((e1_name, e2_name)) = duplicates {
|
||||
tracing::info!(
|
||||
"Memory Consolidation Daemon: Merging '{}' into '{}'",
|
||||
e2_name,
|
||||
e1_name
|
||||
);
|
||||
state.modify_graph(|g| {
|
||||
if let Some(mut e2) = g.entities.remove(&e2_name) {
|
||||
if let Some(e1) = g.entities.get_mut(&e1_name) {
|
||||
e1.observations.append(&mut e2.observations);
|
||||
} else {
|
||||
g.entities.insert(e2_name.clone(), e2);
|
||||
}
|
||||
}
|
||||
|
||||
for rel in g.relations.iter_mut() {
|
||||
if rel.from == e2_name {
|
||||
rel.from = e1_name.clone();
|
||||
}
|
||||
if rel.to == e2_name {
|
||||
rel.to = e1_name.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user