update_artifact
Modify existing artifact details in MemoryMesh's knowledge graph, including metadata, attributes, and relationships with characters, quests, and locations.
Instructions
Update an existing artifact in the knowledge graph
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| update_artifact | Yes |
Implementation Reference
- Main handler function that executes the update_artifact tool logic. Finds the node, processes updates to metadata and edges, performs transactional updates via ApplicationManager.
export async function handleSchemaUpdate( updates: NodeData, schema: SchemaConfig, nodeType: string, applicationManager: ApplicationManager ): Promise<ToolResponse> { try { // Start a transaction to ensure atomic updates await applicationManager.beginTransaction(); // Get the complete current state const fullGraph = await applicationManager.readGraph(); const node = fullGraph.nodes.find((n: Node) => n.nodeType === nodeType && n.name === updates.name); if (!node) { await applicationManager.rollback(); return formatToolError({ operation: 'updateSchema', error: `${nodeType} "${updates.name}" not found`, context: {updates, nodeType}, suggestions: ["Verify the node exists", "Check node type matches"] }); } try { // Process updates const {metadata, edgeChanges} = await updateSchemaNode( updates, node, schema, fullGraph ); // Update the node first const updatedNode: Node = { ...node, metadata }; await applicationManager.updateNodes([updatedNode]); // Then handle edges if there are any changes if (edgeChanges.remove.length > 0) { await applicationManager.deleteEdges(edgeChanges.remove); } if (edgeChanges.add.length > 0) { await applicationManager.addEdges(edgeChanges.add); } // If everything succeeded, commit the transaction await applicationManager.commit(); return formatToolResponse({ data: { updatedNode, edgeChanges }, actionTaken: `Updated ${nodeType}: ${updatedNode.name}` }); } catch (error) { // If anything fails, rollback all changes await applicationManager.rollback(); throw error; } } catch (error) { if (applicationManager.isInTransaction()) { await applicationManager.rollback(); } return formatToolError({ operation: 'updateSchema', error: error instanceof Error ? error.message : 'Unknown error occurred', context: {updates, schema, nodeType}, suggestions: [ "Check all required fields are provided", "Verify relationship targets exist" ], recoverySteps: [ "Review schema requirements", "Ensure node exists before updating" ] }); } } - Helper function that processes the update data: parses current metadata, applies changes to metadata and computes edge additions/removals for relationships.
export async function updateSchemaNode( updates: NodeData, currentNode: Node, schema: SchemaConfig, currentGraph: Graph ): Promise<SchemaUpdateResult> { const {metadataConfig, relationships} = schema; const metadata = new Map<string, string>(); const edgeChanges = { remove: [] as Edge[], add: [] as Edge[] }; // Create a set of all schema-defined fields const schemaFields = new Set<string>([ ...metadataConfig.requiredFields, ...metadataConfig.optionalFields, ...(metadataConfig.excludeFields || []), 'name', 'metadata' ]); // Add relationship fields to schema fields if (relationships) { Object.keys(relationships).forEach(field => schemaFields.add(field)); } // Process existing metadata into the Map currentNode.metadata.forEach(meta => { const colonIndex = meta.indexOf(':'); if (colonIndex !== -1) { const key = meta.substring(0, colonIndex).trim().toLowerCase(); const value = meta.substring(colonIndex + 1).trim(); metadata.set(key, value); } }); const updateMetadataEntry = (key: string, value: unknown) => { const formattedValue = Array.isArray(value) ? value.join(', ') : String(value); metadata.set(key.toLowerCase(), formattedValue); }; // Process standard metadata fields const allSchemaFields = [...metadataConfig.requiredFields, ...metadataConfig.optionalFields]; for (const field of allSchemaFields) { if (updates[field] !== undefined && (!relationships || !relationships[field])) { updateMetadataEntry(field, updates[field]); } } // Process relationships if they exist in the schema if (relationships) { for (const [field, config] of Object.entries(relationships)) { // Only process relationship if it's being updated if (updates[field] !== undefined) { // Get all existing edges for this relationship type from this node const existingEdges = currentGraph.edges.filter(edge => edge.from === currentNode.name && edge.edgeType === config.edgeType ); // Only mark edges for removal if they're part of this relationship type edgeChanges.remove.push(...existingEdges); // Add new edges const value = updates[field]; if (Array.isArray(value)) { value.forEach((target: string) => { edgeChanges.add.push({ type: 'edge', from: currentNode.name, to: target, edgeType: config.edgeType }); }); } else if (value) { edgeChanges.add.push({ type: 'edge', from: currentNode.name, to: value as string, edgeType: config.edgeType }); } updateMetadataEntry(field, value); } } } // Process additional fields not defined in schema for (const [key, value] of Object.entries(updates)) { if (!schemaFields.has(key) && value !== undefined) { updateMetadataEntry(key, value); } } const updatedMetadata = Array.from(metadata).map(([key, value]) => { const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1); return `${capitalizedKey}: ${value}`; }); return { metadata: updatedMetadata, edgeChanges }; } - Generates the input schema specifically for update tools like update_artifact by copying properties from the base add schema, making them optional, and adding support for metadata and relationships.
createUpdateSchema(excludeFields: Set<string> = new Set()): SchemaConfig { const schemaName = this.schema.name!.replace('add_', 'update_'); const updateSchemaBuilder = new SchemaBuilder( schemaName, `Update an existing ${schemaName.replace('update_', '')} in the knowledge graph` ); const baseProperties = this.schema.inputSchema!.properties[this.schema.name!.replace('add_', '')].properties; // Copy properties except excluded ones Object.entries(baseProperties).forEach(([propName, propValue]) => { if (!excludeFields.has(propName)) { if (propValue.type === 'array') { updateSchemaBuilder.addArrayProperty( propName, propValue.description, false, propValue.items?.enum ); } else { updateSchemaBuilder.addStringProperty( propName, propValue.description, false, propValue.enum ); } } }); // Copy relationships this.relationships.forEach((config, propName) => { if (!excludeFields.has(propName)) { updateSchemaBuilder.addRelationship( propName, config.edgeType, config.description || 'Relationship property', config.nodeType || null ); } }); // Add metadata array updateSchemaBuilder.addArrayProperty( 'metadata', 'An array of metadata contents to replace the existing metadata' ); return updateSchemaBuilder.build(); } - src/integration/tools/DynamicSchemaToolRegistry.ts:89-131 (registration)Dynamically generates Tool objects including the update_<schemaName> tool from loaded schemas and adds them to the registry cache.
private async generateToolsForSchema(schemaName: string, schema: SchemaBuilder): Promise<Tool[]> { const tools: Tool[] = []; const baseSchema = schema.build(); // Add tool tools.push(baseSchema as unknown as Tool); // Update tool const updateSchema = schema.createUpdateSchema(); tools.push(updateSchema as unknown as Tool); // Delete tool const deleteSchema: Tool = { name: `delete_${schemaName}`, description: `Delete an existing ${schemaName} from the knowledge graph`, inputSchema: { type: "object", properties: { [`delete_${schemaName}`]: { type: "object", description: `Delete parameters for ${schemaName}`, properties: { name: { type: "string", description: `The name of the ${schemaName} to delete` } }, required: ["name"] } }, required: [`delete_${schemaName}`] } }; tools.push(deleteSchema); return tools; } - Tool call dispatcher that matches 'update_<schema>' tool names and routes to the specific update handler.
case 'update': { return handleSchemaUpdate( args[`update_${schemaName}`], schema, schemaName, knowledgeGraphManager ); }