Skip to main content
Glama
memory-recall.ts4.73 kB
/** * Memory recall tool - Token-aware semantic search * v3.0: Dual-response pattern (index + details) for skill-like progressive loading */ import type { DbDriver } from '../database/db-driver.js'; import type { SearchOptions, SearchOptionsInternal, RecallResponse, Memory, MinimalMemory, FormattedMemory, Entity, Provenance, } from '../types/index.js'; import { semanticSearch } from '../search/semantic-search.js'; import { formatMemory, formatMemoryList, getMemoryTokenCount } from './response-formatter.js'; import { now } from '../database/connection.js'; import { estimateTokens } from '../utils/token-estimator.js'; /** * Recall memories using semantic search with intelligent token budgeting * Returns: index (all matches as summaries) + details (top matches with full content) */ export async function memoryRecall( db: DbDriver, options: SearchOptions ): Promise<RecallResponse> { try { console.error('[memoryRecall] Starting recall with options:', JSON.stringify(options)); // Set defaults const limit = Math.min(options.limit || 20, 50); // Max 50 const maxTokens = options.max_tokens || 1000; // Default 1k token budget console.error(`[memoryRecall] Processed options - limit: ${limit}, maxTokens: ${maxTokens}`); // Perform semantic search (get all matches up to limit) const searchOptions: SearchOptionsInternal = { query: options.query, limit, offset: 0, includeExpired: false, }; if (options.type) searchOptions.type = options.type; if (options.entities) searchOptions.entities = options.entities; console.error('[memoryRecall] Calling semanticSearch...'); const { results, totalCount } = semanticSearch(db, searchOptions); console.error(`[memoryRecall] Search returned ${results.length} results, total count: ${totalCount}`); // Track access for frequency tracking const currentTime = now(); for (const result of results) { // Increment access_count and update last_accessed db.prepare( `UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?` ).run(currentTime, result.id); } // Build options map for formatting (includes entities and provenance) const optionsMap = new Map<string, { entities?: Entity[]; provenance?: Provenance[] }>(); for (const result of results) { optionsMap.set(result.id, { entities: result.entities, provenance: result.provenance, }); } // Convert MemorySearchResult[] to Memory[] for formatting const memories: Memory[] = results.map((result) => ({ id: result.id, content: result.content, summary: result.summary, type: result.type, importance: result.importance, created_at: result.created_at, last_accessed: result.last_accessed, access_count: result.access_count, expires_at: result.expires_at, metadata: result.metadata, is_deleted: result.is_deleted, })); // PHASE 1: Create index (all matches as minimal summaries) const index: MinimalMemory[] = formatMemoryList(memories, 'minimal') as MinimalMemory[]; const indexTokens = estimateTokens(index); // PHASE 2: Fill remaining budget with detailed content const details: FormattedMemory[] = []; let tokensUsed = indexTokens; for (const memory of memories) { // Format with standard detail (content + entities + timestamps) const options = optionsMap.get(memory.id) || {}; const formatted = formatMemory(memory, 'standard', options); const memoryTokens = getMemoryTokenCount(formatted); // Check if it fits in remaining budget if (tokensUsed + memoryTokens <= maxTokens) { details.push(formatted); tokensUsed += memoryTokens; } else { // Budget exhausted, stop adding details break; } } // Build response with dual structure const response: RecallResponse = { index, details, total_count: totalCount, has_more: totalCount > limit, tokens_used: tokensUsed, query: options.query, }; console.error(`[memoryRecall] Built response - index: ${index.length} items, details: ${details.length} items, tokens: ${tokensUsed}`); console.error('[memoryRecall] Recall completed successfully'); return response; } catch (error) { console.error('[memoryRecall] ERROR:', error); if (error instanceof Error) { console.error('[memoryRecall] Error stack:', error.stack); } throw error; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/WhenMoon-afk/claude-memory-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server