Skip to main content
Glama
entity-handlers.ts9.77 kB
import { Entity, EntityStatus, Relation } from "../../memory-types.js"; import { logger } from "../logger.js"; import { ModernSimilarityEngine } from "../similarity/similarity-engine.js"; /** * Entity Management Handlers * Handles entity creation, updates, and deletion with automatic similarity detection */ export class EntityHandlers { private memoryManager: any; private modernSimilarity: ModernSimilarityEngine; private relationshipIndexer?: any; constructor( memoryManager: any, modernSimilarity: ModernSimilarityEngine, relationshipIndexer?: any ) { this.memoryManager = memoryManager; this.modernSimilarity = modernSimilarity; this.relationshipIndexer = relationshipIndexer; } async handleCreateEntities(args: any): Promise<any> { let createBranch = args.branch_name as string; if (!createBranch && args.entities && (args.entities as any[]).length > 0) { // Auto-suggest branch based on first entity const firstEntity = (args.entities as any[])[0]; createBranch = await this.memoryManager.suggestBranch( firstEntity.entityType, firstEntity.observations?.join(" ") ); } const createdEntities = await this.memoryManager.createEntities( args.entities as Entity[], createBranch ); let autoRelationsResults: any[] = []; // Handle auto_create_relations if enabled (defaults to true in SMART_MEMORY_TOOLS) if (args.auto_create_relations !== false) { logger.info("Starting automatic relationship detection..."); try { // Get existing entities from the branch to compare against const branchGraph = await this.memoryManager.readGraph( createBranch, ["active", "draft"], // Include both active and draft entities for comparison false // Don't include cross-context for similarity detection ); const existingEntities = branchGraph.entities; let totalRelationsCreated = 0; // Process each created entity for similarity detection for (const newEntity of createdEntities) { logger.debug(`Analyzing "${newEntity.name}" for similar entities...`); // Use statistical similarity engine to detect similar entities const similarEntities = await this.modernSimilarity.detectSimilarEntities( newEntity, existingEntities.filter((e: Entity) => e.name !== newEntity.name) // Exclude self ); if (similarEntities.length > 0) { logger.info( `Found ${similarEntities.length} similar entities for "${newEntity.name}"` ); // Create relationships with high-confidence matches const relationsToCreate: Relation[] = []; for (const match of similarEntities) { // Only auto-create relationships for high confidence matches logger.debug( `Match: "${ match.entity.name }" similarity=${match.similarity.toFixed(3)} confidence=${ match.confidence } type=${match.suggestedRelationType}` ); if (match.confidence === "high" || match.similarity > 0.5) { relationsToCreate.push({ from: newEntity.name, to: match.entity.name, relationType: match.suggestedRelationType, }); autoRelationsResults.push({ from: newEntity.name, to: match.entity.name, relationType: match.suggestedRelationType, similarity_score: match.similarity, confidence: match.confidence, reasoning: match.reasoning, auto_created: true, }); } else { // Log medium/low confidence matches for reference autoRelationsResults.push({ from: newEntity.name, to: match.entity.name, relationType: match.suggestedRelationType, similarity_score: match.similarity, confidence: match.confidence, reasoning: match.reasoning, auto_created: false, note: "Low confidence - relation suggested but not auto-created", }); } } // Create the high-confidence relations if (relationsToCreate.length > 0) { const createdRelations = await this.memoryManager.createRelations( relationsToCreate, createBranch ); totalRelationsCreated += createdRelations.length; logger.info( `Auto-created ${createdRelations.length} high-confidence relations for "${newEntity.name}"` ); } } else { logger.info(`No similar entities found for "${newEntity.name}"`); logger.debug( `Similarity analysis for "${newEntity.name}": found ${similarEntities.length} candidates, threshold: 0.78` ); autoRelationsResults.push({ entity: newEntity.name, message: "No similar entities found above similarity threshold", similarity_threshold: 0.5, candidates_analyzed: existingEntities.length, similarity_results: similarEntities.length, }); } } logger.info( `Auto-relationship detection complete: ${totalRelationsCreated} relations created` ); // Add summary to results autoRelationsResults.unshift({ summary: `Auto-relationship detection complete`, total_relations_created: totalRelationsCreated, entities_processed: createdEntities.length, similarity_engine: "ModernSimilarityEngine", similarity_threshold: 0.65, high_confidence_threshold: 0.85, }); // Notify background indexer about new entities if (this.relationshipIndexer) { for (const entity of createdEntities) { this.relationshipIndexer.onEntityCreated(entity.name, createBranch); } } } catch (error) { logger.error("Error during automatic relationship detection:", error); autoRelationsResults.push({ error: "Auto-relationship detection failed", message: error instanceof Error ? error.message : String(error), fallback: "Relations can still be created manually", }); } } return { content: [ { type: "text", text: JSON.stringify( { created_entities: createdEntities, branch: createBranch || "main", auto_relations_enabled: args.auto_create_relations !== false, auto_relations_results: autoRelationsResults, message: `Created ${createdEntities.length} entities in branch "${ createBranch || "main" }"${ args.auto_create_relations !== false ? " with auto-relationship detection enabled" : "" }`, }, null, 2 ), }, ], }; } async handleAddObservations(args: any): Promise<any> { if (!args.observations) { throw new Error("observations array is required"); } const results = await this.memoryManager.addObservations( args.observations, args.branch_name as string ); return { content: [ { type: "text", text: JSON.stringify( { results, branch: args.branch_name || "main", message: `Added observations to ${ results.length } entities in branch "${args.branch_name || "main"}"`, }, null, 2 ), }, ], }; } async handleUpdateEntityStatus(args: any): Promise<any> { if (!args.entity_name || !args.status) { throw new Error("entity_name and status are required"); } await this.memoryManager.updateEntityStatus( args.entity_name as string, args.status as EntityStatus, args.status_reason as string, args.branch_name as string ); return { content: [ { type: "text", text: JSON.stringify( { message: `Updated entity "${args.entity_name}" status to "${ args.status }" in branch "${args.branch_name || "main"}"`, entity_name: args.entity_name, new_status: args.status, status_reason: args.status_reason, branch: args.branch_name || "main", }, null, 2 ), }, ], }; } async handleDeleteEntities(args: any): Promise<any> { if (!args.entity_names) { throw new Error("entity_names array is required"); } await this.memoryManager.deleteEntities( args.entity_names as string[], args.branch_name as string ); return { content: [ { type: "text", text: JSON.stringify( { message: `Deleted ${ (args.entity_names as string[]).length } entities from branch "${args.branch_name || "main"}"`, deleted_entities: args.entity_names, branch: args.branch_name || "main", }, null, 2 ), }, ], }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PrismAero/agentic-memory-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server