@polybiouslabs/polybious
Version:
Polybius is a next-generation intelligent agent framework built for adaptability across diverse domains. It merges contextual awareness, multi-agent collaboration, and predictive reasoning to deliver dynamic, self-optimizing performance.
267 lines (221 loc) • 9.63 kB
JavaScript
import { logger } from '../config/logger.js';
import * as fs from 'fs/promises';
import * as path from 'path';
export class ContextualMemoryMatrix {
memories = new Map();
relationships = new Map();
contexts = new Map();
memoryPath;
maxMemories;
constructor(maxMemories = 2000) {
this.memoryPath = path.join('data', 'contextual-memory', 'matrix.json');
this.maxMemories = maxMemories;
this.loadMemories();
}
async store(type, data, importance = 0.5, tags = []) {
const id = `ctx_${Date.now()}_${Math.random().toString(36).substring(7)}`;
const entry = {
id,
timestamp: new Date(),
type,
data,
importance,
tags,
context: this.extractContext(data),
relationships: []
};
this.memories.set(id, entry);
await this.buildRelationships(id, entry);
await this.consolidateMemories();
await this.saveMemories();
logger.debug('Contextual memory stored', { id, type, importance, tags });
return id;
}
async recall(query, limit = 10) {
const queryTerms = query.toLowerCase().split(' ');
const scored = [];
for (const entry of this.memories.values()) {
let score = 0;
const entryText = JSON.stringify(entry.data).toLowerCase();
// Keyword matching
for (const term of queryTerms) {
if (entryText.includes(term)) score += 1;
if (entry.tags.some(tag => tag.toLowerCase().includes(term))) score += 2;
if (entry.context && entry.context.toLowerCase().includes(term)) score += 1.5;
}
// Relationship bonus
const relatedMemories = this.getRelatedMemories(entry.id);
score += relatedMemories.length * 0.1;
// Recency and importance weighting
const hoursSince = (Date.now() - entry.timestamp.getTime()) / (1000 * 60 * 60);
score += Math.max(0, 1 - hoursSince / 168) * entry.importance;
if (score > 0) {
scored.push({ entry, score });
}
}
return scored
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => item.entry);
}
async generateInsights() {
const insights = [];
const allMemories = Array.from(this.memories.values());
// Pattern analysis
const tagFrequency = new Map();
const contextPatterns = new Map();
allMemories.forEach(memory => {
memory.tags.forEach(tag => {
tagFrequency.set(tag, (tagFrequency.get(tag) || 0) + 1);
});
if (memory.context) {
contextPatterns.set(memory.context, (contextPatterns.get(memory.context) || 0) + 1);
}
});
// Most frequent tags
const topTags = Array.from(tagFrequency.entries())
.sort(([, a], [, b]) => b - a)
.slice(0, 5);
if (topTags.length > 0) {
insights.push(`Most frequent topics: ${topTags.map(([tag]) => tag).join(', ')}`);
}
// Memory relationship density
const totalRelationships = Array.from(this.relationships.values()).reduce((sum, rels) => sum + rels.length, 0);
const avgRelationships = totalRelationships / this.memories.size;
insights.push(`Average memory connections: ${avgRelationships.toFixed(2)}`);
// High importance memories
const highImportanceMemories = allMemories.filter(m => m.importance > 0.8);
if (highImportanceMemories.length > 0) {
insights.push(`${highImportanceMemories.length} high-importance memories stored`);
}
return insights;
}
extractContext(data) {
if (typeof data === 'string') {
return data.slice(0, 100);
}
if (typeof data === 'object' && data !== null) {
if (data.context) return data.context;
if (data.topic) return data.topic;
if (data.task) return data.task;
return JSON.stringify(data).slice(0, 100);
}
return 'unknown_context';
}
async buildRelationships(memoryId, memory) {
const relationships = [];
for (const [otherId, otherMemory] of this.memories) {
if (otherId === memoryId) continue;
const similarity = this.calculateSimilarity(memory, otherMemory);
if (similarity > 0.3) {
relationships.push({
targetId: otherId,
strength: similarity,
type: this.classifyRelationship(memory, otherMemory)
});
}
}
this.relationships.set(memoryId, relationships);
memory.relationships = relationships.map(r => r.targetId);
}
calculateSimilarity(memory1, memory2) {
let similarity = 0;
// Tag overlap
const tags1 = new Set(memory1.tags);
const tags2 = new Set(memory2.tags);
const tagOverlap = [...tags1].filter(tag => tags2.has(tag)).length;
const tagUnion = new Set([...tags1, ...tags2]).size;
similarity += tagOverlap / tagUnion * 0.4;
// Context similarity
if (memory1.context && memory2.context) {
const contextWords1 = memory1.context.toLowerCase().split(' ');
const contextWords2 = memory2.context.toLowerCase().split(' ');
const wordOverlap = contextWords1.filter(word => contextWords2.includes(word)).length;
similarity += wordOverlap / Math.max(contextWords1.length, contextWords2.length) * 0.3;
}
// Type similarity
if (memory1.type === memory2.type) {
similarity += 0.2;
}
// Time proximity
const timeDiff = Math.abs(memory1.timestamp.getTime() - memory2.timestamp.getTime());
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
similarity += Math.max(0, (7 - daysDiff) / 7) * 0.1;
return Math.min(1, similarity);
}
classifyRelationship(memory1, memory2) {
if (memory1.type === memory2.type) return 'same_type';
if (memory1.tags.some(tag => memory2.tags.includes(tag))) return 'shared_topic';
if (memory1.context === memory2.context) return 'same_context';
return 'general';
}
getRelatedMemories(memoryId) {
const relationships = this.relationships.get(memoryId) || [];
return relationships
.sort((a, b) => b.strength - a.strength)
.map(rel => this.memories.get(rel.targetId))
.filter(Boolean);
}
async consolidateMemories() {
if (this.memories.size <= this.maxMemories) return;
const sorted = Array.from(this.memories.entries())
.sort(([, a], [, b]) => {
const scoreA = a.importance +
(this.relationships.get(a.id)?.length || 0) * 0.1 +
(Date.now() - a.timestamp.getTime()) / (1000 * 60 * 60 * 24 * 30);
const scoreB = b.importance +
(this.relationships.get(b.id)?.length || 0) * 0.1 +
(Date.now() - b.timestamp.getTime()) / (1000 * 60 * 60 * 24 * 30);
return scoreB - scoreA;
});
this.memories.clear();
this.relationships.clear();
sorted.slice(0, this.maxMemories).forEach(([id, entry]) => {
this.memories.set(id, entry);
});
// Rebuild relationships for remaining memories
for (const [id, memory] of this.memories) {
await this.buildRelationships(id, memory);
}
logger.info(`Consolidated contextual memories: kept ${this.memories.size} out of ${sorted.length}`);
}
async loadMemories() {
try {
await fs.mkdir(path.dirname(this.memoryPath), { recursive: true });
const data = await fs.readFile(this.memoryPath, 'utf8');
const parsed = JSON.parse(data);
if (parsed.memories) {
parsed.memories.forEach(entry => {
entry.timestamp = new Date(entry.timestamp);
this.memories.set(entry.id, entry);
});
}
if (parsed.relationships) {
parsed.relationships.forEach(([id, rels]) => {
this.relationships.set(id, rels);
});
}
if (parsed.contexts) {
parsed.contexts.forEach(([context, data]) => {
this.contexts.set(context, data);
});
}
logger.info(`Loaded ${this.memories.size} contextual memories with ${this.relationships.size} relationship maps`);
} catch (error) {
logger.debug('No existing contextual memory file found, starting fresh');
}
}
async saveMemories() {
try {
const data = {
memories: Array.from(this.memories.values()),
relationships: Array.from(this.relationships.entries()),
contexts: Array.from(this.contexts.entries()),
savedAt: new Date().toISOString()
};
await fs.writeFile(this.memoryPath, JSON.stringify(data, null, 2));
} catch (error) {
logger.error('Failed to save contextual memories', { error });
}
}
}