entity-handlers.js•10.2 kB
import { logger } from "../logger.js";
/**
* Entity Management Handlers
* Handles entity creation, updates, and deletion with automatic similarity detection
*/
export class EntityHandlers {
memoryManager;
modernSimilarity;
relationshipIndexer;
constructor(memoryManager, modernSimilarity, relationshipIndexer) {
this.memoryManager = memoryManager;
this.modernSimilarity = modernSimilarity;
this.relationshipIndexer = relationshipIndexer;
}
async handleCreateEntities(args) {
let createBranch = args.branch_name;
if (!createBranch && args.entities && args.entities.length > 0) {
// Auto-suggest branch based on first entity
const firstEntity = args.entities[0];
createBranch = await this.memoryManager.suggestBranch(firstEntity.entityType, firstEntity.observations?.join(" "));
}
const createdEntities = await this.memoryManager.createEntities(args.entities, createBranch);
let autoRelationsResults = [];
// 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) => 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 = [];
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) {
if (!args.observations) {
throw new Error("observations array is required");
}
const results = await this.memoryManager.addObservations(args.observations, args.branch_name);
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) {
if (!args.entity_name || !args.status) {
throw new Error("entity_name and status are required");
}
await this.memoryManager.updateEntityStatus(args.entity_name, args.status, args.status_reason, args.branch_name);
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) {
if (!args.entity_names) {
throw new Error("entity_names array is required");
}
await this.memoryManager.deleteEntities(args.entity_names, args.branch_name);
return {
content: [
{
type: "text",
text: JSON.stringify({
message: `Deleted ${args.entity_names.length} entities from branch "${args.branch_name || "main"}"`,
deleted_entities: args.entity_names,
branch: args.branch_name || "main",
}, null, 2),
},
],
};
}
}