diff --git a/package.json b/package.json index 201b0b7..77de180 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/ai-utils", - "version": "1.4.5", + "version": "1.4.6", "description": "AI Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/llm.ts b/src/llm.ts index 8459eb9..03a0ba6 100644 --- a/src/llm.ts +++ b/src/llm.ts @@ -1,4 +1,4 @@ -import {clean, snakeCase} from '@ztimson/utils'; +import {clean, makeUnique, snakeCase} from '@ztimson/utils'; import {AbortablePromise, Ai} from './ai.ts'; import {Anthropic} from './antrhopic.ts'; import {OpenAi} from './open-ai.ts'; @@ -7,7 +7,7 @@ import {AiTool, AiToolArg} from './tools.ts'; import {fileURLToPath} from 'url'; import {dirname, join} from 'path'; import {spawn} from 'node:child_process'; -import {Memory, MemoryCache, MemoryManager, MemoryOptions} from './memory.ts'; +import {Memory, MemoryCache, MemoryManager, MemoryOptions, stripHeader} from './memory.ts'; const MAX_AGENT_DEPTH = 5; @@ -207,7 +207,7 @@ ${a.system}`, const list = allTools.map(t => `- ${t.name}: ${t.description}`).join('\n'); return { - prompt: `You have access to the following MCP tools:\n${list}`, + prompt: `## MCP\nYou have access to the following MCP tools:\n${list}`, tools: allTools }; } @@ -216,7 +216,7 @@ ${a.system}`, if(!skills?.length) return {prompt: '', tools: []}; const list = skills.map(s => `- ${s.name}: ${s.description}`).join('\n'); return { - prompt: `You have access to the following skill documents, whenever there is overlap between a question and a skill file, use \`skill_read\` to get instructions and background knowledge:\n${list}`, + prompt: `## Skills\nYou have access to the following skill documents, whenever there is overlap between a question and a skill file, use \`skill_read\` to get instructions and background knowledge:\n${list}`, tools: [{ name: 'skill_read', description: 'Read the full content of a skill/knowledge document', @@ -316,30 +316,26 @@ ${a.system}`, } else listed.push(r); } - prompts.unshift(`You have a background memory process which has prefetched relevant information${mem.update ? ' and will create new memories from this conversation' : ''} for you + prompts.unshift(`## Memory +You have a background memory process which has prefetched relevant information${mem.update ? ' and will create new memories from this conversation' : ''} for you Assume it is perfect and never mention this process to anyone ever Always use your memories to craft a personalized response, they contain links / [[wiki links]] which you use navigate between them ${mem.tool ? `You can access memory files via the \`memory_search\` and \`memory_recall\` tools When you need information about the user, \`memory_recall\` \`People/User\` before asking (fetch if not included bellow) -When you need information not provided, attempt 1-3 \`memory_search\` calls with unique queries before asking` : ''} +When you need information not provided, attempt 1-3 \`memory_search\` calls with distinct queries before asking` : ''} -${preloaded.length ? ` -Prefetched Memories (Most relevant first): +${preloaded.length ? `### Prefetched Memories (Most relevant first): ${preloaded.map(r => `Memory: ${r.name} Description: ${r.description} -Linked: ${[r.links, ...r.backlinks].join(', ')} +Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')} \`\`\` -${r.content} +${stripHeader(r.content)} \`\`\``).join('\n\n')}` : ''} - -${mem.tool && listed.length ? listed.map(r => `Memory: ${r.name} +${mem.tool && listed.length ? '\n' + listed.map(r => `Memory: ${r.name} Description: ${r.description} -Linked: ${[r.links, ...r.backlinks].join(', ')} -`).join('\n\n') : ''} - -${mem.tool ? `Full memory list: -${mems.map(m => `- ${m.name}: ${m.description}`).join('\n')}` : ''}`.trim()) +Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')} +`).join('\n\n') : ''}`.trim()) } if(mem.tool) tools.push(this.memoryManager.tools.read(mem.memory)); } diff --git a/src/memory.ts b/src/memory.ts index 96c3d53..734afca 100644 --- a/src/memory.ts +++ b/src/memory.ts @@ -61,6 +61,10 @@ function cosineSearch(query: number[], memories: Memory[], limit: number): Memor .map(s => s.ref); } +export function stripHeader(content: string): string { + return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart(); +} + export class MemoryCache { private tree!: KDTree; public memories: Memory[]; @@ -249,7 +253,7 @@ ${m.content} private appendFacts(node: Memory, facts: string[]): void { this.ensureDoc(node); - const body = this.stripHeader(node.content); + const body = stripHeader(node.content); const bullets = facts.map(f => `- ${f}`).join('\n'); const idx = body.indexOf(FACTS_HEADING); const newBody = idx === -1 @@ -355,7 +359,7 @@ ${ghosts.length ? `${ghosts.map(g => `- ${g}: (Ghost)`).join('\n')}` : ''}`, } private async docAgent(node: Memory, memories: Memory[], options: LLMRequest, entry: {request: {abort?: () => void} | null}): Promise { - const currentBody = this.stripHeader(node.content); + const currentBody = stripHeader(node.content); let update; try { for (let i = 0; i < 2 && !update?.content; i++) { @@ -420,10 +424,6 @@ ${currentBody} return {fm, body: match[2]}; } - private stripHeader(content: string): string { - return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart(); - } - private touchHeader(node: Memory, body: string): string { const {fm} = this.parseFrontmatter(node.content); fm.set('name', node.name);