UNPKG

thoughtmcp

Version:

AI that thinks more like humans do - MCP server with human-like cognitive architecture for enhanced reasoning, memory, and self-monitoring

373 lines 13.9 kB
/** * Semantic Memory System * * Implements embedding-based similarity search for semantic memories. * Handles storage, retrieval, and relationship management of concepts. */ export class SemanticMemory { concepts = new Map(); relations = new Map(); embeddingIndex = new Map(); activationIndex = new Map(); config; initialized = false; lastActivity = 0; constructor(config) { this.config = { capacity: 50000, embedding_dim: 768, similarity_threshold: 0.7, activation_decay: 0.05, relation_strength_threshold: 0.3, max_relations_per_concept: 20, ...config, }; } async initialize(config) { if (config) { this.config = { ...this.config, ...config }; } this.concepts.clear(); this.relations.clear(); this.embeddingIndex.clear(); this.activationIndex.clear(); this.initialized = true; this.lastActivity = Date.now(); } async process(input) { // Generic process method for CognitiveComponent interface const inputObj = input; if (typeof input === "object" && input !== null && inputObj?.concept) { return this.store(inputObj.concept); } throw new Error("Invalid input for SemanticMemory.process()"); } reset() { this.concepts.clear(); this.relations.clear(); this.embeddingIndex.clear(); this.activationIndex.clear(); this.lastActivity = Date.now(); } getStatus() { return { name: "SemanticMemory", initialized: this.initialized, active: this.concepts.size > 0, last_activity: this.lastActivity, }; } /** * Store a concept in semantic memory */ store(concept) { this.lastActivity = Date.now(); // Generate ID if not provided const conceptId = concept.id || this.generateConceptId(concept.content); // Generate embedding if not provided const embedding = concept.embedding || this.generateEmbedding(concept.content); // Store the concept const storedConcept = { ...concept, id: conceptId, embedding, activation: concept.activation || 1.0, last_accessed: Date.now(), relations: concept.relations || [], }; this.concepts.set(conceptId, storedConcept); this.embeddingIndex.set(conceptId, embedding); this.activationIndex.set(conceptId, storedConcept.activation); // Apply capacity management after storing if (this.concepts.size > this.config.capacity) { this.pruneLeastActiveConcepts(); } return conceptId; } /** * Retrieve concepts based on similarity to cue */ retrieve(cue, threshold = this.config.similarity_threshold) { this.lastActivity = Date.now(); const cueEmbedding = this.generateEmbedding(cue); const matches = []; // Compute similarity with all concepts for (const [id, concept] of this.concepts) { const embedding = this.embeddingIndex.get(id); if (!embedding) continue; const similarity = this.computeCosineSimilarity(cueEmbedding, embedding); if (similarity >= threshold) { matches.push({ concept, similarity }); // Update activation for retrieved concepts this.updateActivation(id, 0.1); } } // Sort by similarity and return concepts return matches .sort((a, b) => b.similarity - a.similarity) .map((match) => match.concept); } /** * Add a relation between two concepts */ addRelation(from, to, type, strength) { this.lastActivity = Date.now(); // Validate concepts exist if (!this.concepts.has(from) || !this.concepts.has(to)) { throw new Error(`Cannot create relation: concept(s) not found`); } // Check strength threshold if (strength < this.config.relation_strength_threshold) { return; } const relationId = `${from}-${type}-${to}`; const relation = { from, to, type, strength }; this.relations.set(relationId, relation); // Update concept relations this.addRelationToConcept(from, relationId); this.addRelationToConcept(to, relationId); } /** * Get concepts related to a given concept */ getRelated(conceptId) { const concept = this.concepts.get(conceptId); if (!concept) return []; const relatedConcepts = []; for (const relationId of concept.relations) { const relation = this.relations.get(relationId); if (!relation) continue; // Get the other concept in the relation const otherConceptId = relation.from === conceptId ? relation.to : relation.from; const otherConcept = this.concepts.get(otherConceptId); if (otherConcept) { relatedConcepts.push(otherConcept); } } // Sort by activation level return relatedConcepts.sort((a, b) => b.activation - a.activation); } /** * Update activation level of a concept */ updateActivation(conceptId, delta) { const concept = this.concepts.get(conceptId); if (!concept) return; const newActivation = Math.max(0, Math.min(1, concept.activation + delta)); const updatedConcept = { ...concept, activation: newActivation, last_accessed: Date.now(), }; this.concepts.set(conceptId, updatedConcept); this.activationIndex.set(conceptId, newActivation); } /** * Apply activation decay to all concepts */ applyDecay() { const currentTime = Date.now(); for (const [id, concept] of this.concepts) { const timeSinceAccess = (currentTime - concept.last_accessed) / (1000 * 60 * 60); // hours const decayAmount = this.config.activation_decay * timeSinceAccess; const newActivation = Math.max(0, concept.activation - decayAmount); // Only update if there's actual decay if (newActivation !== concept.activation) { this.updateActivation(id, newActivation - concept.activation); } } } /** * Find concepts by content similarity */ findSimilarConcepts(conceptId, maxResults = 10) { const concept = this.concepts.get(conceptId); if (!concept) return []; const embedding = this.embeddingIndex.get(conceptId); if (!embedding) return []; const similarities = []; for (const [id, otherConcept] of this.concepts) { if (id === conceptId) continue; const otherEmbedding = this.embeddingIndex.get(id); if (!otherEmbedding) continue; const similarity = this.computeCosineSimilarity(embedding, otherEmbedding); similarities.push({ concept: otherConcept, similarity }); } return similarities .sort((a, b) => b.similarity - a.similarity) .slice(0, maxResults) .map((item) => item.concept); } /** * Get concept by ID */ getConcept(conceptId) { return this.concepts.get(conceptId); } /** * Get all concepts with activation above threshold */ getActiveConcepts(threshold = 0.1) { return Array.from(this.concepts.values()) .filter((concept) => concept.activation >= threshold) .sort((a, b) => b.activation - a.activation); } /** * Get relations of a specific type */ getRelationsByType(type) { return Array.from(this.relations.values()).filter((relation) => relation.type === type); } // Private helper methods generateConceptId(content) { const contentStr = JSON.stringify(content); let hash = 0; for (let i = 0; i < contentStr.length; i++) { const char = contentStr.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return `concept_${Math.abs(hash).toString(16)}`; } generateEmbedding(content) { // Simple embedding generation (in production, use a proper embedding model) const contentStr = JSON.stringify(content).toLowerCase(); const embedding = new Array(this.config.embedding_dim).fill(0); // Hash-based embedding generation for (let i = 0; i < contentStr.length; i++) { const char = contentStr.charCodeAt(i); const index = char % this.config.embedding_dim; embedding[index] += Math.sin(char * 0.1) * 0.1; } // Normalize the embedding const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0)); if (magnitude > 0) { for (let i = 0; i < embedding.length; i++) { embedding[i] /= magnitude; } } return embedding; } computeCosineSimilarity(a, b) { if (a.length !== b.length) return 0; let dotProduct = 0; let magnitudeA = 0; let magnitudeB = 0; for (let i = 0; i < a.length; i++) { dotProduct += a[i] * b[i]; magnitudeA += a[i] * a[i]; magnitudeB += b[i] * b[i]; } magnitudeA = Math.sqrt(magnitudeA); magnitudeB = Math.sqrt(magnitudeB); if (magnitudeA === 0 || magnitudeB === 0) return 0; return dotProduct / (magnitudeA * magnitudeB); } addRelationToConcept(conceptId, relationId) { const concept = this.concepts.get(conceptId); if (!concept) return; // Check if we've reached the maximum relations limit if (concept.relations.length >= this.config.max_relations_per_concept) { // Remove the weakest relation this.removeWeakestRelation(conceptId); } const updatedConcept = { ...concept, relations: [...concept.relations, relationId], }; this.concepts.set(conceptId, updatedConcept); } removeWeakestRelation(conceptId) { const concept = this.concepts.get(conceptId); if (!concept || concept.relations.length === 0) return; let weakestRelationId = ""; let weakestStrength = Infinity; for (const relationId of concept.relations) { const relation = this.relations.get(relationId); if (relation && relation.strength < weakestStrength) { weakestStrength = relation.strength; weakestRelationId = relationId; } } if (weakestRelationId) { // Remove from concept const updatedRelations = concept.relations.filter((id) => id !== weakestRelationId); const updatedConcept = { ...concept, relations: updatedRelations, }; this.concepts.set(conceptId, updatedConcept); // Remove the relation entirely this.relations.delete(weakestRelationId); } } pruneLeastActiveConcepts() { // Remove concepts until we're at capacity const conceptEntries = Array.from(this.concepts.entries()); const sortedByActivation = conceptEntries.sort((a, b) => a[1].activation - b[1].activation); const toRemove = this.concepts.size - this.config.capacity; for (let i = 0; i < toRemove && i < sortedByActivation.length; i++) { const [conceptId] = sortedByActivation[i]; this.removeConcept(conceptId); } } removeConcept(conceptId) { const concept = this.concepts.get(conceptId); if (!concept) return; // Remove all relations involving this concept for (const relationId of concept.relations) { this.relations.delete(relationId); } // Remove from all indexes this.concepts.delete(conceptId); this.embeddingIndex.delete(conceptId); this.activationIndex.delete(conceptId); // Remove references from other concepts for (const [id, otherConcept] of this.concepts) { const updatedRelations = otherConcept.relations.filter((relationId) => { const relation = this.relations.get(relationId); return (relation && relation.from !== conceptId && relation.to !== conceptId); }); if (updatedRelations.length !== otherConcept.relations.length) { this.concepts.set(id, { ...otherConcept, relations: updatedRelations, }); } } } /** * Simulate memory decay for testing purposes */ async simulateDecay(milliseconds) { const decayFactor = Math.exp(-0.001 * (milliseconds / 1000)); // Small decay rate for semantic memory for (const [id, concept] of this.concepts) { // Apply decay to concept activation const updatedConcept = { ...concept, activation: concept.activation * decayFactor, }; // Remove concepts that have decayed below threshold if (updatedConcept.activation < 0.1) { this.concepts.delete(id); } else { this.concepts.set(id, updatedConcept); } } } } //# sourceMappingURL=SemanticMemory.js.map