UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

287 lines 12 kB
import { createSuccess, createFailure, createMemoryNotFoundError, createMemoryStorageError } from '../types'; /** * In-memory memory provider - no persistence across server restarts * Best for development, testing, or temporary conversations */ export function createInMemoryProvider(config = { type: 'memory', maxConversations: 1000, maxMessagesPerConversation: 1000 }) { const fullConfig = { ...config, type: 'memory', maxConversations: config.maxConversations ?? 1000, maxMessagesPerConversation: config.maxMessagesPerConversation ?? 1000 }; const conversations = new Map(); console.log(`[MEMORY:InMemory] Initialized with max ${fullConfig.maxConversations} conversations, ${fullConfig.maxMessagesPerConversation} messages each`); const enforceMemoryLimits = () => { if (conversations.size <= fullConfig.maxConversations) { return; } // Sort by last activity and remove oldest conversations const sorted = Array.from(conversations.entries()) .sort(([, a], [, b]) => { const aTime = a.metadata?.lastActivity?.getTime() || 0; const bTime = b.metadata?.lastActivity?.getTime() || 0; return aTime - bTime; // Oldest first }); const toRemove = sorted.slice(0, conversations.size - fullConfig.maxConversations); for (const [id] of toRemove) { conversations.delete(id); } console.log(`[MEMORY:InMemory] Enforced memory limits, removed ${toRemove.length} oldest conversations`); }; const storeMessages = async (conversationId, messages, metadata) => { try { const now = new Date(); const conversation = { conversationId, userId: metadata?.userId, messages, metadata: { createdAt: now, updatedAt: now, totalMessages: messages.length, lastActivity: now, traceId: metadata?.traceId, ...metadata } }; conversations.set(conversationId, conversation); enforceMemoryLimits(); console.log(`[MEMORY:InMemory] Stored ${messages.length} messages for conversation ${conversationId}`); return createSuccess(undefined); } catch (error) { return createFailure(createMemoryStorageError('store messages', 'InMemory', error)); } }; const getConversation = async (conversationId) => { try { const conversation = conversations.get(conversationId); if (!conversation) { return createSuccess(null); } // Update last activity const updatedConversation = { ...conversation, metadata: { ...conversation.metadata, lastActivity: new Date() } }; conversations.set(conversationId, updatedConversation); console.log(`[MEMORY:InMemory] Retrieved conversation ${conversationId} with ${conversation.messages.length} messages`); return createSuccess(updatedConversation); } catch (error) { return createFailure(createMemoryStorageError('get conversation', 'InMemory', error)); } }; const appendMessages = async (conversationId, messages, metadata) => { try { const existing = conversations.get(conversationId); if (!existing) { return createFailure(createMemoryNotFoundError(conversationId, 'InMemory')); } const updatedMessages = [...existing.messages, ...messages]; // Enforce per-conversation message limit const finalMessages = updatedMessages.length > fullConfig.maxMessagesPerConversation ? updatedMessages.slice(-fullConfig.maxMessagesPerConversation) : updatedMessages; const now = new Date(); const updatedConversation = { ...existing, messages: finalMessages, metadata: { ...existing.metadata, updatedAt: now, lastActivity: now, totalMessages: finalMessages.length, traceId: metadata?.traceId || existing.metadata?.traceId, ...metadata } }; conversations.set(conversationId, updatedConversation); console.log(`[MEMORY:InMemory] Appended ${messages.length} messages to conversation ${conversationId} (total: ${finalMessages.length})`); return createSuccess(undefined); } catch (error) { return createFailure(createMemoryStorageError('append messages', 'InMemory', error)); } }; const findConversations = async (query) => { try { const results = []; for (const [id, conversation] of conversations) { let matches = true; if (query.conversationId && id !== query.conversationId) { matches = false; } if (query.userId && conversation.userId !== query.userId) { matches = false; } if (query.traceId && conversation.metadata?.traceId !== query.traceId) { matches = false; } if (query.since && conversation.metadata?.createdAt && conversation.metadata.createdAt < query.since) { matches = false; } if (query.until && conversation.metadata?.createdAt && conversation.metadata.createdAt > query.until) { matches = false; } if (matches) { results.push(conversation); } } // Sort by last activity (most recent first) results.sort((a, b) => { const aTime = a.metadata?.lastActivity?.getTime() || 0; const bTime = b.metadata?.lastActivity?.getTime() || 0; return bTime - aTime; }); // Apply pagination const offset = query.offset || 0; const limit = query.limit || results.length; const paginatedResults = results.slice(offset, offset + limit); console.log(`[MEMORY:InMemory] Found ${paginatedResults.length} conversations matching query`); return createSuccess(paginatedResults); } catch (error) { return createFailure(createMemoryStorageError('find conversations', 'InMemory', error)); } }; const getRecentMessages = async (conversationId, limit = 50) => { const conversationResult = await getConversation(conversationId); if (!conversationResult.success) { return conversationResult; } if (!conversationResult.data) { return createSuccess([]); } const messages = conversationResult.data.messages.slice(-limit); console.log(`[MEMORY:InMemory] Retrieved ${messages.length} recent messages for conversation ${conversationId}`); return createSuccess(messages); }; const deleteConversation = async (conversationId) => { try { const existed = conversations.has(conversationId); conversations.delete(conversationId); console.log(`[MEMORY:InMemory] ${existed ? 'Deleted' : 'Attempted to delete non-existent'} conversation ${conversationId}`); return createSuccess(existed); } catch (error) { return createFailure(createMemoryStorageError('delete conversation', 'InMemory', error)); } }; const clearUserConversations = async (userId) => { try { let deletedCount = 0; for (const [id, conversation] of conversations) { if (conversation.userId === userId) { conversations.delete(id); deletedCount++; } } console.log(`[MEMORY:InMemory] Cleared ${deletedCount} conversations for user ${userId}`); return createSuccess(deletedCount); } catch (error) { return createFailure(createMemoryStorageError('clear user conversations', 'InMemory', error)); } }; const getStats = async (userId) => { try { let totalConversations = 0; let totalMessages = 0; let oldestDate; let newestDate; for (const conversation of conversations.values()) { if (userId && conversation.userId !== userId) { continue; } totalConversations++; totalMessages += conversation.messages.length; const createdAt = conversation.metadata?.createdAt; if (createdAt) { if (!oldestDate || createdAt < oldestDate) { oldestDate = createdAt; } if (!newestDate || createdAt > newestDate) { newestDate = createdAt; } } } return createSuccess({ totalConversations, totalMessages, oldestConversation: oldestDate, newestConversation: newestDate }); } catch (error) { return createFailure(createMemoryStorageError('get stats', 'InMemory', error)); } }; const healthCheck = async () => { const start = Date.now(); try { // Simple operation to test functionality const testId = `health-check-${Date.now()}`; const storeResult = await storeMessages(testId, [{ role: 'user', content: 'health check' }]); if (!storeResult.success) { return createSuccess({ healthy: false, latencyMs: Date.now() - start, error: storeResult.error.message }); } const getResult = await getConversation(testId); if (!getResult.success) { return createSuccess({ healthy: false, latencyMs: Date.now() - start, error: getResult.error.message }); } const deleteResult = await deleteConversation(testId); if (!deleteResult.success) { return createSuccess({ healthy: false, latencyMs: Date.now() - start, error: deleteResult.error.message }); } const latencyMs = Date.now() - start; return createSuccess({ healthy: true, latencyMs }); } catch (error) { return createSuccess({ healthy: false, latencyMs: Date.now() - start, error: error instanceof Error ? error.message : 'Unknown error' }); } }; const close = async () => { try { console.log(`[MEMORY:InMemory] Closing provider, clearing ${conversations.size} conversations`); conversations.clear(); return createSuccess(undefined); } catch (error) { return createFailure(createMemoryStorageError('close provider', 'InMemory', error)); } }; return { storeMessages, getConversation, appendMessages, findConversations, getRecentMessages, deleteConversation, clearUserConversations, getStats, healthCheck, close }; } //# sourceMappingURL=in-memory.js.map