@codai/memorai-core
Version:
Simplified advanced memory engine - no tiers, just powerful semantic search with persistence
263 lines (262 loc) • 8.66 kB
JavaScript
/**
* Mock Memory Engine - Testing and Development Mode
* Provides deterministic responses for testing
*/
export class MockMemoryEngine {
constructor(config = {}) {
this.mockMemories = new Map();
this.callCount = 0;
this.config = {
simulateDelay: false,
delayMs: 100,
failureRate: 0,
deterministicResponses: true,
...config,
};
// Pre-populate with mock data
this.initializeMockData();
}
/**
* Mock remember operation
*/
async remember(memory) {
await this.simulateDelay();
this.simulateFailure();
this.mockMemories.set(memory.id, memory);
this.callCount++;
}
/**
* Mock recall operation with deterministic responses
*/
async recall(query) {
await this.simulateDelay();
this.simulateFailure();
this.callCount++;
if (this.config.deterministicResponses) {
return this.getDeterministicResults(query);
}
// Filter mock memories based on query
const results = [];
for (const memory of this.mockMemories.values()) {
if (memory.tenant_id === query.tenant_id) {
if (!query.type || memory.type === query.type) {
if (!query.agent_id || memory.agent_id === query.agent_id) {
const score = this.calculateMockScore(memory, query);
if (score >= query.threshold) {
results.push({
memory,
score,
relevance_reason: `Mock match for "${query.query}"`,
});
}
}
}
}
}
// Sort by score and limit
results.sort((a, b) => b.score - a.score);
return results.slice(0, query.limit);
}
/**
* Mock context retrieval
*/
async getContext(tenantId, agentId, limit = 10) {
await this.simulateDelay();
this.simulateFailure();
this.callCount++;
const memories = Array.from(this.mockMemories.values())
.filter(memory => {
if (memory.tenant_id !== tenantId)
return false;
if (agentId && memory.agent_id !== agentId)
return false;
return true;
})
.slice(0, limit);
return memories;
}
/**
* Mock forget operation
*/
async forget(memoryId) {
await this.simulateDelay();
this.simulateFailure();
this.callCount++;
return this.mockMemories.delete(memoryId);
}
/**
* Get deterministic test results based on query
*/
getDeterministicResults(query) {
const queryLower = query.query.toLowerCase();
// Predefined test responses for common queries
const testResponses = {
hello: [
{
memory: this.createMockMemory('test-1', 'personality', 'User prefers friendly greetings', query.tenant_id, query.agent_id),
score: 0.9,
relevance_reason: 'Matches greeting pattern',
},
],
test: [
{
memory: this.createMockMemory('test-2', 'fact', 'This is a test memory for validation', query.tenant_id, query.agent_id),
score: 0.85,
relevance_reason: 'Direct test match',
},
{
memory: this.createMockMemory('test-3', 'procedure', 'Testing procedure: run all tests', query.tenant_id, query.agent_id),
score: 0.7,
relevance_reason: 'Testing procedure',
},
],
user: [
{
memory: this.createMockMemory('user-1', 'preference', 'User likes dark mode', query.tenant_id, query.agent_id),
score: 0.8,
relevance_reason: 'User preference',
},
],
project: [
{
memory: this.createMockMemory('proj-1', 'task', 'Project deadline is next week', query.tenant_id, query.agent_id),
score: 0.75,
relevance_reason: 'Project information',
},
],
};
// Find matching test response
for (const [key, results] of Object.entries(testResponses)) {
if (queryLower.includes(key)) {
return results
.filter(result => !query.type || result.memory.type === query.type)
.slice(0, query.limit);
}
}
// Default empty response for unknown queries
return [];
}
/**
* Create a mock memory for testing
*/
createMockMemory(id, type, content, tenantId, agentId) {
const now = new Date();
return {
id,
type,
content,
confidence: 0.9,
createdAt: now,
updatedAt: now,
lastAccessedAt: now,
accessCount: 1,
importance: 0.5,
tags: ['mock', 'test'],
tenant_id: tenantId,
agent_id: agentId,
};
}
/**
* Initialize mock data for testing
*/
initializeMockData() {
const mockData = [
{
id: 'mock-personality-1',
type: 'personality',
content: 'User is enthusiastic about technology and prefers detailed explanations',
tenant_id: 'test-tenant',
agent_id: 'test-agent',
},
{
id: 'mock-procedure-1',
type: 'procedure',
content: 'When debugging, always check logs first, then network requests',
tenant_id: 'test-tenant',
agent_id: 'test-agent',
},
{
id: 'mock-fact-1',
type: 'fact',
content: 'The project uses TypeScript and Node.js for backend development',
tenant_id: 'test-tenant',
agent_id: 'test-agent',
},
{
id: 'mock-preference-1',
type: 'preference',
content: 'User prefers concise responses with code examples',
tenant_id: 'test-tenant',
agent_id: 'test-agent',
},
];
for (const data of mockData) {
const memory = this.createMockMemory(data.id, data.type, data.content, data.tenant_id, data.agent_id);
this.mockMemories.set(memory.id, memory);
}
}
/**
* Calculate mock relevance score
*/
calculateMockScore(memory, query) {
const queryWords = query.query.toLowerCase().split(/\s+/);
const contentWords = memory.content.toLowerCase().split(/\s+/);
let matches = 0;
for (const word of queryWords) {
if (contentWords.some(cw => cw.includes(word) || word.includes(cw))) {
matches++;
}
}
return Math.min(matches / queryWords.length + 0.1, 1.0);
}
/**
* Simulate network delay if configured
*/
async simulateDelay() {
if (this.config.simulateDelay &&
this.config.delayMs &&
this.config.delayMs > 0) {
await new Promise(resolve => setTimeout(resolve, this.config.delayMs));
}
}
/**
* Simulate random failures if configured
*/
simulateFailure() {
if (this.config.failureRate && this.config.failureRate > 0) {
if (Math.random() < this.config.failureRate) {
throw new Error('Mock failure simulation');
}
}
}
/**
* Get mock engine statistics
*/
getStats() {
return {
totalCalls: this.callCount,
totalMemories: this.mockMemories.size,
config: this.config,
};
}
/**
* Reset mock engine state
*/
reset() {
this.callCount = 0;
this.mockMemories.clear();
this.initializeMockData();
}
/**
* Add custom mock memory for testing
*/
addMockMemory(memory) {
this.mockMemories.set(memory.id, memory);
}
/**
* Configure mock engine behavior
*/
configure(config) {
this.config = { ...this.config, ...config };
}
}