import type { RecallResult, EntityState, MemoryPromptOptions } from './types.js'; const DEFAULT_PREAMBLE = 'You have access to the following memories about this user from previous interactions:'; /** * Formats recalled memories and entity observations into a system prompt string. * * Returns an empty string when there are no memories or entities to include, * so callers can safely concatenate or conditionally append. */ export function formatMemoriesAsSystemPrompt( memories: RecallResult[], entities: Record | null | undefined, options?: MemoryPromptOptions ): string { const { preamble = DEFAULT_PREAMBLE, maxMemories, includeTypes, includeEntities = true, } = options ?? {}; let filtered = memories; if (includeTypes && includeTypes.length > 0) { filtered = filtered.filter( (m) => m.type != null && includeTypes.includes(m.type as never) ); } if (maxMemories != null && maxMemories > 0) { filtered = filtered.slice(0, maxMemories); } const hasMemories = filtered.length > 0; const entityEntries = entities ? Object.values(entities) : []; const hasEntities = includeEntities && entityEntries.length > 0; if (!hasMemories && !hasEntities) { return ''; } const parts: string[] = [preamble, '']; if (hasMemories) { parts.push(''); for (const memory of filtered) { const typeSuffix = memory.type ? ` [${memory.type}]` : ''; parts.push(`- ${memory.text}${typeSuffix}`); } parts.push(''); } if (hasEntities) { if (hasMemories) parts.push(''); parts.push(''); for (const entity of entityEntries) { parts.push(`## ${entity.canonical_name}`); for (const obs of entity.observations) { parts.push(`- ${obs.text}`); } } parts.push(''); } return parts.join('\n'); }