import {Memory} from './memory/memory.ts'; export function cosineDistance(a: number[], b: number[]): number { let dot = 0, normA = 0, normB = 0; for(let i = 0; i < a.length; i++) { dot += a[i] * b[i]; normA += a[i] * a[i]; normB += b[i] * b[i]; } const denom = Math.sqrt(normA) * Math.sqrt(normB); return denom === 0 ? 1 : 1 - dot / denom; } export async function embedMemoryFields(node: Memory, llm: any): Promise { const body = stripHeader(node.content); const [titleE] = await llm.embedding(node.name.split('/').pop() || node.name); const [descE] = await llm.embedding(node.description || ''); const bodyChunks = body ? await llm.embedding(body) : []; if(titleE) node.titleEmbedding = titleE.embedding; if(descE) node.embedding = descE.embedding; node.bodyEmbeddings = bodyChunks.map((c: any) => c.embedding).filter(Boolean); } export function euclideanDistance(a: number[], b: number[]): number { let sum = 0; for(let i = 0; i < a.length; i++) { const d = a[i] - b[i]; sum += d * d; } return Math.sqrt(sum); } function getWeekStart(date: Date = new Date()): string { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const day = d.getUTCDay(); const diff = day === 0 ? -6 : 1 - day; d.setUTCDate(d.getUTCDate() + diff); return d.toISOString().slice(0, 10); } export function journalDescription(journalName?: string): string { const start = journalName?.split('/').pop() || getWeekStart(); const d = new Date(`${start}T00:00:00Z`); d.setUTCDate(d.getUTCDate() + 6); const end = d.toISOString().slice(0, 10); return `Log from ${start} - ${end}`; } function parseFrontmatter(content: string): {fm: Map, body: string} { const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/); if(!match) return {fm: new Map(), body: content}; const fm = new Map(); for(const line of match[1].split('\n')) { const i = line.indexOf(':'); if(i === -1) continue; const key = line.slice(0, i).trim(); const raw = line.slice(i + 1).trim(); let value = raw; try { value = JSON.parse(raw); } catch { } fm.set(key, value); } return {fm, body: match[2]}; } export function writeFrontmatter(fm: Map, body: string): string { const lines = [...fm.entries()].map(([k, v]) => `${k}: ${JSON.stringify(String(v).replace(/\s+/g, ' ').trim())}`); return `---\n${lines.join('\n')}\n---\n\n${body.trimStart()}`; } export function stripHeader(content: string): string { return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart(); } export function touchHeader(node: Memory, body: string): string { const {fm} = parseFrontmatter(node.content); fm.set('name', node.name); fm.set('description', (node.name.startsWith('Journal/') ? journalDescription(node.name) : node.description) || 'Persistent memory document'); fm.set('modified', new Date().toISOString()); return writeFrontmatter(fm, stripHeader(body)); }