Kraft102's picture
Update backend source
34367da verified
import { Router } from 'express';
import { MemoryRepository } from './memoryRepository.js';
import { MemoryEntityInput, CmaContextRequest } from '@widget-tdc/mcp-types';
export const memoryRouter = Router();
const memoryRepo = new MemoryRepository();
// Simple in-memory cache with TTL
class SimpleCache {
private cache = new Map<string, { data: any; expiry: number }>();
set(key: string, data: any, ttlMs: number = 300000): void { // 5 min default
this.cache.set(key, { data, expiry: Date.now() + ttlMs });
}
get(key: string): any | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expiry) {
this.cache.delete(key);
return null;
}
return entry.data;
}
clear(): void {
this.cache.clear();
}
}
const contextCache = new SimpleCache();
// Ingest a memory entity
memoryRouter.post('/ingest', async (req, res) => {
try {
const input: MemoryEntityInput = req.body;
if (!input.orgId || !input.entityType || !input.content) {
return res.status(400).json({
error: 'Missing required fields: orgId, entityType, content',
});
}
const entityId = await memoryRepo.ingestEntity(input);
// Clear context cache when new memory is added to ensure freshness
contextCache.clear();
res.json({
success: true,
id: entityId,
});
} catch (error: any) {
console.error('Memory ingest error:', error);
res.status(500).json({
success: false,
error: error.message,
});
}
});
// Get contextual prompt with memories
memoryRouter.post('/contextual-prompt', async (req, res) => {
try {
const request: CmaContextRequest = req.body;
if (!request.orgId || !request.userId || !request.userQuery) {
return res.status(400).json({
error: 'Missing required fields: orgId, userId, userQuery',
});
}
// Create cache key from request parameters
const cacheKey = `${request.orgId}:${request.userId}:${request.userQuery}:${JSON.stringify(request.keywords || [])}:${request.widgetData || ''}`;
// Check cache first
const cachedResult = contextCache.get(cacheKey);
if (cachedResult) {
return res.json({
success: true,
...cachedResult,
cached: true,
});
}
// Search for relevant memories with enhanced semantic search
const memories = await memoryRepo.searchEntities({
orgId: request.orgId,
userId: request.userId,
keywords: request.keywords || [],
limit: 5,
});
// Build enhanced contextual prompt with semantic scoring
const memoryContext = memories.map(m => {
const score = m.semanticScore ? ` (semantic score: ${(m.semanticScore * 100).toFixed(1)}%)` : '';
return `[${m.entity_type}] ${m.content} (importance: ${m.importance})${score}`;
}).join('\n');
const prompt = `
Context from memory (enhanced with semantic search):
${memoryContext}
Widget data:
${request.widgetData || 'None'}
User query:
${request.userQuery}
Please provide a hyper-contextual response considering the semantic relationships and importance scores above.
`.trim();
const result = {
prompt,
memories: memories.map(m => ({
id: m.id,
content: m.content,
importance: m.importance,
semanticScore: m.semanticScore,
})),
};
// Cache the result for 5 minutes
contextCache.set(cacheKey, result, 300000);
res.json({
success: true,
...result,
cached: false,
});
} catch (error: any) {
console.error('Contextual prompt error:', error);
res.status(500).json({
success: false,
error: error.message,
});
}
});
// Search memories
memoryRouter.post('/search', async (req, res) => {
try {
const query = req.body;
const memories = await memoryRepo.searchEntities(query);
res.json({
success: true,
memories,
count: memories.length,
});
} catch (error: any) {
console.error('Memory search error:', error);
res.status(500).json({
success: false,
error: error.message,
});
}
});