procedure-memory-mcp-server
Version:
Procedure memory management system with MCP integration using Zep
274 lines ⢠13.7 kB
JavaScript
import { ZepClient } from "@getzep/zep-cloud";
const client = new ZepClient({
apiKey: process.env.ZEP_API_KEY || "z_1dWlkIjoiMTA5OTk0MTItOGQ0MS00ZTcwLTlhOWItMzVhNTYyNWJiMDcwIn0.Belvq0qUi40Fqg2pNDkLKYhRod5OeMnZh2SMe4TlgH_vllqqnKBdl4vCdUFX47JIK9Wluu6jnPpYL6SZEtEUIg"
});
const RELEVANCE_THRESHOLD = 0.3; // Minimum score to consider
const HIGH_QUALITY_THRESHOLD = 0.5; // Threshold for strong references
export async function findMostRelevantEpisodes(query, options = {}) {
const { n = 3, limit = 20, relevanceThreshold = RELEVANCE_THRESHOLD, includeMetrics = false } = options;
console.log(`\nš Searching for top ${n} most relevant episodes for query: "${query}"\n`);
try {
// Step 1: Search all three scopes in parallel for better coverage
console.log("š Step 1: Searching across edges, nodes, and episodes...");
const [edgeResults, nodeResults, episodeResults] = await Promise.all([
client.graph.search({
query: query,
userId: process.env.USER_ID || "syiaOpsMemory",
scope: "edges",
limit: limit
}),
client.graph.search({
query: query,
userId: process.env.USER_ID || "syiaOpsMemory",
scope: "nodes",
limit: limit
}),
client.graph.search({
query: query,
userId: process.env.USER_ID || "syiaOpsMemory",
scope: "episodes",
limit: limit
})
]);
console.log(`Found: ${edgeResults.edges?.length || 0} edges, ${nodeResults.nodes?.length || 0} nodes, ${episodeResults.episodes?.length || 0} episodes`);
// Step 2: Build episode map with improved scoring
const episodeMap = new Map();
// Process direct episode matches first (these are most relevant)
episodeResults.episodes?.forEach((episode, index) => {
const rankScore = Math.exp(-2 * index / limit); // Exponential decay
if (rankScore >= relevanceThreshold) {
episodeMap.set(episode.uuid, {
uuid: episode.uuid,
referenceCount: 1,
rank: rankScore * 1.5, // Boost direct matches
maxSingleScore: rankScore * 1.5,
avgScore: rankScore * 1.5,
content: episode.content,
source: episode.source,
createdAt: episode.createdAt,
sources: {
fromEdges: [],
fromNodes: [],
fromDirect: [{
content: episode.content?.substring(0, 100) || '',
rank: rankScore * 1.5
}]
}
});
}
});
// Process edges with threshold filtering
edgeResults.edges?.forEach((edge, index) => {
const rankScore = Math.exp(-2 * index / limit); // Exponential decay
if (rankScore < relevanceThreshold)
return; // Skip low-relevance matches
if (edge.episodes && Array.isArray(edge.episodes)) {
edge.episodes.forEach((episodeUuid) => {
if (!episodeMap.has(episodeUuid)) {
episodeMap.set(episodeUuid, {
uuid: episodeUuid,
referenceCount: 0,
rank: 0,
maxSingleScore: 0,
avgScore: 0,
sources: { fromEdges: [], fromNodes: [], fromDirect: [] }
});
}
const episode = episodeMap.get(episodeUuid);
episode.referenceCount++;
episode.rank += rankScore;
episode.maxSingleScore = Math.max(episode.maxSingleScore, rankScore);
episode.sources.fromEdges.push({
type: edge.name,
fact: edge.fact,
rank: rankScore
});
});
}
});
// Process nodes with threshold filtering
nodeResults.nodes?.forEach((node, index) => {
const rankScore = Math.exp(-2 * index / limit); // Exponential decay
if (rankScore < relevanceThreshold)
return; // Skip low-relevance matches
const nodeEpisodes = node.episodes || node.attributes?.episodes || [];
if (Array.isArray(nodeEpisodes)) {
nodeEpisodes.forEach((episodeUuid) => {
if (!episodeMap.has(episodeUuid)) {
episodeMap.set(episodeUuid, {
uuid: episodeUuid,
referenceCount: 0,
rank: 0,
maxSingleScore: 0,
avgScore: 0,
sources: { fromEdges: [], fromNodes: [], fromDirect: [] }
});
}
const episode = episodeMap.get(episodeUuid);
episode.referenceCount++;
episode.rank += rankScore;
episode.maxSingleScore = Math.max(episode.maxSingleScore, rankScore);
episode.sources.fromNodes.push({
name: node.name,
summary: node.summary || '',
rank: rankScore
});
});
}
});
// Step 3: Calculate average scores and apply improved ranking
episodeMap.forEach(episode => {
const totalSources = episode.sources.fromEdges.length +
episode.sources.fromNodes.length +
episode.sources.fromDirect.length;
episode.avgScore = totalSources > 0 ? episode.rank / totalSources : 0;
});
// Sort by quality-first scoring
const sortedEpisodes = Array.from(episodeMap.values())
.map(episode => {
// Calculate logarithmic dampening for reference count
const logCount = Math.log(episode.referenceCount + 1);
// Quality-first composite score
const qualityScore = episode.maxSingleScore * 0.5 + // Best match quality (50%)
episode.avgScore * 0.3 + // Average quality (30%)
Math.min(logCount / 3, 1) * 0.2; // Dampened quantity (20%)
return { ...episode, qualityScore };
})
.sort((a, b) => b.qualityScore - a.qualityScore);
if (sortedEpisodes.length === 0) {
console.log("\nā No episodes found for this query");
const markdownContent = {
type: "text",
text: `# Procedure Search Results\n\n**Query**: "${query}"\n\n## No Results Found\n\nNo procedures were found matching your query with a relevance score above ${(relevanceThreshold * 100).toFixed(0)}%.\n\n### Suggestions:\n- Try using different keywords\n- Use more general terms\n- Check for spelling errors`,
title: `Procedure Search: ${query}`,
format: "markdown"
};
const jsonContent = {
type: "text",
text: JSON.stringify({
query: query,
message: "No procedures found for this query",
relevanceThreshold: relevanceThreshold,
episodes: []
}, null, 2),
title: "Procedure Search Results (JSON)",
format: "json"
};
return [markdownContent, jsonContent];
}
// Get top N episodes
const topEpisodes = sortedEpisodes.slice(0, Math.min(n, sortedEpisodes.length));
// Display results with quality metrics
console.log(`\nš TOP ${topEpisodes.length} MOST RELEVANT EPISODES (Quality-First Ranking):`);
topEpisodes.forEach((episode, index) => {
console.log(`\n${index + 1}. Episode ${episode.uuid}`);
console.log(` Quality Score: ${episode.qualityScore.toFixed(3)}`);
console.log(` Best Match: ${episode.maxSingleScore.toFixed(3)}`);
console.log(` Avg Score: ${episode.avgScore.toFixed(3)}`);
console.log(` References: ${episode.referenceCount} (above threshold)`);
// Show only high-quality references
const topRefs = [...episode.sources.fromEdges, ...episode.sources.fromNodes]
.filter(ref => ref.rank >= HIGH_QUALITY_THRESHOLD)
.sort((a, b) => b.rank - a.rank)
.slice(0, 2);
if (topRefs.length > 0) {
console.log(` Strong References (ā„0.5):`);
topRefs.forEach(ref => {
const preview = 'fact' in ref ? ref.fact : ref.summary;
console.log(` ⢠${preview.substring(0, 60)}... (${ref.rank.toFixed(2)})`);
});
}
});
// Enrich episodes with any missing content
const enrichedEpisodes = [];
for (const episode of topEpisodes) {
// If we don't have content from direct search, try to fetch it
if (!episode.content && episodeResults.episodes) {
const directMatch = episodeResults.episodes.find((ep) => ep.uuid === episode.uuid);
if (directMatch) {
episode.content = directMatch.content;
episode.source = directMatch.source;
episode.createdAt = directMatch.createdAt;
}
}
enrichedEpisodes.push({
uuid: episode.uuid,
rank: episode.qualityScore,
referenceCount: episode.referenceCount,
content: episode.content,
source: episode.source,
createdAt: episode.createdAt,
topReferences: [...episode.sources.fromEdges, ...episode.sources.fromNodes]
.sort((a, b) => b.rank - a.rank)
.slice(0, 3),
metrics: {
maxSingleScore: episode.maxSingleScore,
avgScore: episode.avgScore,
directMatch: episode.sources.fromDirect.length > 0
}
});
}
// Format results based on content availability
const formattedResults = enrichedEpisodes.map((episode, index) => {
const sections = [];
// Episode header
sections.push(`## ${index + 1}. Episode ${episode.uuid}`);
sections.push(`**Relevance Score**: ${(episode.rank * 100).toFixed(1)}%`);
// Content section
if (episode.content) {
sections.push("\n### Procedure Content");
sections.push(episode.content);
}
// Metadata section
if (episode.source || episode.createdAt) {
sections.push("\n### Metadata");
if (episode.source)
sections.push(`- **Source**: ${episode.source}`);
if (episode.createdAt)
sections.push(`- **Created**: ${new Date(episode.createdAt).toLocaleString()}`);
}
// References section (only show if high-quality)
const strongRefs = episode.topReferences.filter((ref) => ref.rank >= HIGH_QUALITY_THRESHOLD);
if (strongRefs.length > 0) {
sections.push("\n### Key References");
strongRefs.forEach((ref) => {
const preview = ref.fact || ref.summary || "";
sections.push(`- ${preview.substring(0, 100)}${preview.length > 100 ? '...' : ''}`);
});
}
// Quality metrics (only if includeMetrics is true or if no content)
if (!episode.content || includeMetrics) {
sections.push("\n### Quality Metrics");
sections.push(`- Direct match: ${episode.metrics.directMatch ? 'Yes' : 'No'}`);
sections.push(`- Best match score: ${(episode.metrics.maxSingleScore * 100).toFixed(1)}%`);
sections.push(`- Average score: ${(episode.metrics.avgScore * 100).toFixed(1)}%`);
sections.push(`- Reference count: ${episode.referenceCount}`);
}
return sections.join("\n");
});
// Create both formatted text and structured JSON responses
const markdownContent = {
type: "text",
text: `# Procedure Search Results\n\n**Query**: "${query}"\n**Total Episodes Found**: ${sortedEpisodes.length}\n**Showing Top**: ${enrichedEpisodes.length}\n\n${formattedResults.join("\n\n---\n\n")}`,
title: `Procedure Search: ${query}`,
format: "markdown"
};
const jsonContent = {
type: "text",
text: JSON.stringify({
query: query,
totalEpisodesFound: sortedEpisodes.length,
relevanceThreshold: relevanceThreshold,
topEpisodes: enrichedEpisodes
}, null, 2),
title: `Procedure Search Results (JSON): ${query}`,
format: "json"
};
return [markdownContent, jsonContent];
}
catch (error) {
console.error("ā Error:", error);
throw error;
}
}
//# sourceMappingURL=procedureSearch.js.map