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:
Riz Ashraf committed 2026-10-10 10:58:50 +01:00
1 parent 26eaf03b6f
commit e006ef1220
4 files changed
+58 -71

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
use schemars::schema_for;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(JsonSchema, Deserialize, Serialize)]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum TasksToolEnum {
/// Create task.
Add {
/// Task title.
title: String,
/// Task description.
description: Option<String>,
},
/// Delete task.
Delete {
/// Task ID.
id: String,
}
}
fn main() {
let schema = schema_for!(TasksToolEnum);
println!("{}", serde_json::to_string_pretty(&schema).unwrap());
}
+6
View File
@@ -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());
}
+1 -30
View File
@@ -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()
+26 -41
View File
@@ -285,58 +285,45 @@ 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 Some(existing) = seen.insert(normalized, name.clone()) {
if existing != *name {
duplicates = Some((existing, name.clone()));
break;
}
}
}
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 {
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) {
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);
@@ -344,18 +331,16 @@ pub async fn memory_consolidation_worker(state: Arc<MemoryState>) {
}
for rel in g.relations.iter_mut() {
if rel.from == *e2_name {
if rel.from == e2_name {
rel.from = e1_name.clone();
}
if rel.to == *e2_name {
if rel.to == e2_name {
rel.to = e1_name.clone();
}
}
});
}
}
}
}
}
pub async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::error::Error>> {