Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04f038ba65 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ztimson/ai-utils",
|
"name": "@ztimson/ai-utils",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"description": "AI Utility library",
|
"description": "AI Utility library",
|
||||||
"author": "Zak Timson",
|
"author": "Zak Timson",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
30
src/llm.ts
30
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 {AbortablePromise, Ai} from './ai.ts';
|
||||||
import {Anthropic} from './antrhopic.ts';
|
import {Anthropic} from './antrhopic.ts';
|
||||||
import {OpenAi} from './open-ai.ts';
|
import {OpenAi} from './open-ai.ts';
|
||||||
@@ -7,7 +7,7 @@ import {AiTool, AiToolArg} from './tools.ts';
|
|||||||
import {fileURLToPath} from 'url';
|
import {fileURLToPath} from 'url';
|
||||||
import {dirname, join} from 'path';
|
import {dirname, join} from 'path';
|
||||||
import {spawn} from 'node:child_process';
|
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;
|
const MAX_AGENT_DEPTH = 5;
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ ${a.system}`,
|
|||||||
|
|
||||||
const list = allTools.map(t => `- ${t.name}: ${t.description}`).join('\n');
|
const list = allTools.map(t => `- ${t.name}: ${t.description}`).join('\n');
|
||||||
return {
|
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
|
tools: allTools
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ ${a.system}`,
|
|||||||
if(!skills?.length) return {prompt: '', tools: []};
|
if(!skills?.length) return {prompt: '', tools: []};
|
||||||
const list = skills.map(s => `- ${s.name}: ${s.description}`).join('\n');
|
const list = skills.map(s => `- ${s.name}: ${s.description}`).join('\n');
|
||||||
return {
|
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: [{
|
tools: [{
|
||||||
name: 'skill_read',
|
name: 'skill_read',
|
||||||
description: 'Read the full content of a skill/knowledge document',
|
description: 'Read the full content of a skill/knowledge document',
|
||||||
@@ -316,30 +316,26 @@ ${a.system}`,
|
|||||||
} else listed.push(r);
|
} 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
|
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
|
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
|
${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 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 ? `
|
${preloaded.length ? `### Prefetched Memories (Most relevant first):
|
||||||
Prefetched Memories (Most relevant first):
|
|
||||||
|
|
||||||
${preloaded.map(r => `Memory: ${r.name}
|
${preloaded.map(r => `Memory: ${r.name}
|
||||||
Description: ${r.description}
|
Description: ${r.description}
|
||||||
Linked: ${[r.links, ...r.backlinks].join(', ')}
|
Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')}
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${r.content}
|
${stripHeader(r.content)}
|
||||||
\`\`\``).join('\n\n')}` : ''}
|
\`\`\``).join('\n\n')}` : ''}
|
||||||
|
${mem.tool && listed.length ? '\n' + listed.map(r => `Memory: ${r.name}
|
||||||
${mem.tool && listed.length ? listed.map(r => `Memory: ${r.name}
|
|
||||||
Description: ${r.description}
|
Description: ${r.description}
|
||||||
Linked: ${[r.links, ...r.backlinks].join(', ')}
|
Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')}
|
||||||
<!-- Truncated -->`).join('\n\n') : ''}
|
<!-- Truncated -->`).join('\n\n') : ''}`.trim())
|
||||||
|
|
||||||
${mem.tool ? `Full memory list:
|
|
||||||
${mems.map(m => `- ${m.name}: ${m.description}`).join('\n')}` : ''}`.trim())
|
|
||||||
}
|
}
|
||||||
if(mem.tool) tools.push(this.memoryManager.tools.read(mem.memory));
|
if(mem.tool) tools.push(this.memoryManager.tools.read(mem.memory));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ function cosineSearch(query: number[], memories: Memory[], limit: number): Memor
|
|||||||
.map(s => s.ref);
|
.map(s => s.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stripHeader(content: string): string {
|
||||||
|
return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart();
|
||||||
|
}
|
||||||
|
|
||||||
export class MemoryCache {
|
export class MemoryCache {
|
||||||
private tree!: KDTree<MemoryRef>;
|
private tree!: KDTree<MemoryRef>;
|
||||||
public memories: Memory[];
|
public memories: Memory[];
|
||||||
@@ -249,7 +253,7 @@ ${m.content}
|
|||||||
|
|
||||||
private appendFacts(node: Memory, facts: string[]): void {
|
private appendFacts(node: Memory, facts: string[]): void {
|
||||||
this.ensureDoc(node);
|
this.ensureDoc(node);
|
||||||
const body = this.stripHeader(node.content);
|
const body = stripHeader(node.content);
|
||||||
const bullets = facts.map(f => `- ${f}`).join('\n');
|
const bullets = facts.map(f => `- ${f}`).join('\n');
|
||||||
const idx = body.indexOf(FACTS_HEADING);
|
const idx = body.indexOf(FACTS_HEADING);
|
||||||
const newBody = idx === -1
|
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<void> {
|
private async docAgent(node: Memory, memories: Memory[], options: LLMRequest, entry: {request: {abort?: () => void} | null}): Promise<void> {
|
||||||
const currentBody = this.stripHeader(node.content);
|
const currentBody = stripHeader(node.content);
|
||||||
let update;
|
let update;
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < 2 && !update?.content; i++) {
|
for (let i = 0; i < 2 && !update?.content; i++) {
|
||||||
@@ -420,10 +424,6 @@ ${currentBody}
|
|||||||
return {fm, body: match[2]};
|
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 {
|
private touchHeader(node: Memory, body: string): string {
|
||||||
const {fm} = this.parseFrontmatter(node.content);
|
const {fm} = this.parseFrontmatter(node.content);
|
||||||
fm.set('name', node.name);
|
fm.set('name', node.name);
|
||||||
|
|||||||
Reference in New Issue
Block a user