From a7aec4ee2940bc959df27426ebf6a6914370b824 Mon Sep 17 00:00:00 2001 From: ztimson Date: Thu, 30 Jul 2026 16:00:03 -0400 Subject: [PATCH] Improved memory prompt slightly --- package.json | 2 +- src/memory.ts | 7 +- src/tools.ts | 202 +++++++++++++++++++++++++------------------------- 3 files changed, 105 insertions(+), 106 deletions(-) diff --git a/package.json b/package.json index e57a853..92894ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/ai-utils", - "version": "1.2.8", + "version": "1.2.10", "description": "AI Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/memory.ts b/src/memory.ts index 06f87c8..8c472a6 100644 --- a/src/memory.ts +++ b/src/memory.ts @@ -430,7 +430,7 @@ ${currentBody} } const {backlinks} = extractMetadata(node.content); - node.description = update.description; + node.description = node.name !== 'Person/User' ? update.description : 'All information about the current user'; node.content = this.applyHeader(update.content, this.buildHeader(node, week, newLinks, backlinks)); const [e] = await this.llm.embedding(node.content); if(e) node.embedding = e.embedding; @@ -454,7 +454,7 @@ Rules: When extracting facts, you MUST also decide the exact destination path: - Use an existing node name if the facts clearly belong there -- All information primary about the user should go under "Personal/..." (e.g., Personal/Info, Personal/Todos) +- All information primary about the user should go under "People/User" - When required, create a new path following collection/subject format (e.g., People/Sarah, Projects/Oxide) - For journal entries, use "Journal" @@ -470,8 +470,7 @@ ${this.listNodes(memories).filter(n => !n.name.includes('_temp_') && !n.name.inc }, fn: (args: any) => { const subject = args.destination.trim().toLowerCase() === 'journal' - ? `Journal/${weekKey}` - : args.destination.trim(); + ? `Journal/${weekKey}` : args.destination.trim(); const facts = buckets.get(subject) ?? []; facts.push(...dedupeFacts(String(args.facts).split(','))); buckets.set(subject, facts); diff --git a/src/tools.ts b/src/tools.ts index 69aaeec..79f6f9c 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -451,6 +451,99 @@ export const GetDevice: AiTool = { } } +export const GetWikipediaTool: AiTool = { + name: 'get_wikipedia', + description: 'Search Wikipedia for matching articles', + args: { + query: {type: 'string', description: 'Search term or article title', required: true}, + mode: {type: 'string', description: 'search - look for articles, summary - intro of first found article (default), full - complete first found article', enum: ['search', 'summary', 'full'], default: 'summary'}, + ua: {type: 'string', description: 'User Agent'}, + }, + fn: async ({query, mode, ua}) => { + class WikipediaClient { + useragent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'; + + constructor(useragent: string) { + this.useragent = useragent; + } + + async get(url) { + const resp = await fetch(url, {headers: {'User-Agent': this.useragent}}); + return resp.json(); + } + + api(params) { + const qs = new URLSearchParams({...params, format: 'json', utf8: '1'}).toString(); + return this.get(`https://en.wikipedia.org/w/api.php?${qs}`); + } + + clean(text) { + const cutoffs = ['== See also ==', '== References ==', '== Bibliography ==', '== External links ==']; + for (const marker of cutoffs) { + const idx = text.indexOf(marker); + if (idx !== -1) text = text.slice(0, idx); + } + + return text + .replace(/^={4}\s*(.+?)\s*={4}$/gm, '#### $1') + .replace(/^={3}\s*(.+?)\s*={3}$/gm, '### $1') + .replace(/^={2}\s*(.+?)\s*={2}$/gm, '## $1') + .replace(/\n{3,}/g, '\n\n') + .replace(/ {2,}/g, ' ') + .replace(/\[\d+]/g, '') + .trim(); + } + + async searchTitles(query: string, limit = 6) { + const data = await this.api({action: 'query', list: 'search', srsearch: query, srlimit: limit, srprop: 'snippet'}); + return data.query?.search || []; + } + + async fetchExtract(title: string, introOnly = false) { + const params: any = {action: 'query', prop: 'extracts', titles: title, explaintext: 1, redirects: 1}; + if(introOnly) params.exintro = 1; + const data = await this.api(params); + const page: any = Object.values(data.query?.pages || {})[0]; + return this.clean(page?.extract || ''); + } + + pageUrl(title: string) { + return `https://en.wikipedia.org/wiki/${encodeURIComponent(title.replace(/ /g, '_'))}`; + } + + stripHtml(text: string) { + return text.replace(/<[^>]+>/g, ''); + } + + async lookup(query: string, detail = 'summary') { + const results = await this.searchTitles(query, 6); + if(!results.length) return `āŒ No Wikipedia articles found for "${query}"`; + const title = results[0].title; + const url = this.pageUrl(title); + const introOnly = detail !== 'full'; + const content = await this.fetchExtract(title, introOnly); + return `## ${title}\nšŸ”— ${url}\n\n${content}`; + } + + async search(query: string) { + const results = await this.searchTitles(query, 8); + if(!results.length) return `āŒ No results for "${query}"`; + const lines = [`### Search results for "${query}"\n`]; + for(let i = 0; i < results.length; i++) { + const r = results[i]; + const snippet = this.stripHtml(r.snippet || '').trim(); + lines.push(`**${i + 1}. ${r.title}**\n${snippet}\n${this.pageUrl(r.title)}`); + } + return lines.join('\n\n'); + } + } + + const wiki = new WikipediaClient(ua); + if(mode === 'search') return wiki.search(query); + return wiki.lookup(query, mode || 'summary'); + } +}; + export const GeoCodeTool: AiTool = { name: 'geo_code', description: 'Converts coordinates to address OR vice versa', @@ -527,8 +620,8 @@ export const GeoWeatherTool: AiTool = { }, } -export const NetFetchTool: AiTool = { - name: 'net_fetch', +export const WebFetchTool: AiTool = { + name: 'web_fetch', description: 'Make HTTP request to URL', args: { url: {type: 'string', description: 'URL to fetch', required: true}, @@ -544,9 +637,9 @@ export const NetFetchTool: AiTool = { }) => new Http({url: args.url, headers: args.headers}).request({method: args.method || 'GET', body: args.body}) } -export const NetFlareSolverTool = (host: string) => { +export const WebFlareSolverTool = (host: string) => { return { - name: 'net_flaresolverr', + name: 'web_flaresolverr', description: 'Use a flaresolverr proxy to bypass cloudflare bot detection', args: { url: {type: 'string', description: 'URL to fetch', required: true}, @@ -595,8 +688,8 @@ export const NetFlareSolverTool = (host: string) => { } } -export const NetReadTool: AiTool = { - name: 'net_read', +export const WebReadTool: AiTool = { + name: 'web_read', description: 'Extract clean content from webpages, or convert media/documents to accessible formats', args: { url: {type: 'string', description: 'URL to read', required: true}, @@ -699,8 +792,8 @@ export const NetReadTool: AiTool = { } }; -export const NetSearchTool: AiTool = { - name: 'net_search', +export const WebSearchTool: AiTool = { + name: 'web_search', description: 'Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool', args: { query: {type: 'string', description: 'Search string', required: true}, @@ -724,96 +817,3 @@ export const NetSearchTool: AiTool = { return results; } } - -export const WikipediaTool: AiTool = { - name: 'get_wikipedia', - description: 'Search Wikipedia for matching articles', - args: { - query: {type: 'string', description: 'Search term or article title', required: true}, - mode: {type: 'string', description: 'search - look for articles, summary - intro of first found article (default), full - complete first found article', enum: ['search', 'summary', 'full'], default: 'summary'}, - ua: {type: 'string', description: 'User Agent'}, - }, - fn: async ({query, mode, ua}) => { - class WikipediaClient { - useragent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'; - - constructor(useragent: string) { - this.useragent = useragent; - } - - async get(url) { - const resp = await fetch(url, {headers: {'User-Agent': this.useragent}}); - return resp.json(); - } - - api(params) { - const qs = new URLSearchParams({...params, format: 'json', utf8: '1'}).toString(); - return this.get(`https://en.wikipedia.org/w/api.php?${qs}`); - } - - clean(text) { - const cutoffs = ['== See also ==', '== References ==', '== Bibliography ==', '== External links ==']; - for (const marker of cutoffs) { - const idx = text.indexOf(marker); - if (idx !== -1) text = text.slice(0, idx); - } - - return text - .replace(/^={4}\s*(.+?)\s*={4}$/gm, '#### $1') - .replace(/^={3}\s*(.+?)\s*={3}$/gm, '### $1') - .replace(/^={2}\s*(.+?)\s*={2}$/gm, '## $1') - .replace(/\n{3,}/g, '\n\n') - .replace(/ {2,}/g, ' ') - .replace(/\[\d+]/g, '') - .trim(); - } - - async searchTitles(query: string, limit = 6) { - const data = await this.api({action: 'query', list: 'search', srsearch: query, srlimit: limit, srprop: 'snippet'}); - return data.query?.search || []; - } - - async fetchExtract(title: string, introOnly = false) { - const params: any = {action: 'query', prop: 'extracts', titles: title, explaintext: 1, redirects: 1}; - if(introOnly) params.exintro = 1; - const data = await this.api(params); - const page: any = Object.values(data.query?.pages || {})[0]; - return this.clean(page?.extract || ''); - } - - pageUrl(title: string) { - return `https://en.wikipedia.org/wiki/${encodeURIComponent(title.replace(/ /g, '_'))}`; - } - - stripHtml(text: string) { - return text.replace(/<[^>]+>/g, ''); - } - - async lookup(query: string, detail = 'summary') { - const results = await this.searchTitles(query, 6); - if(!results.length) return `āŒ No Wikipedia articles found for "${query}"`; - const title = results[0].title; - const url = this.pageUrl(title); - const introOnly = detail !== 'full'; - const content = await this.fetchExtract(title, introOnly); - return `## ${title}\nšŸ”— ${url}\n\n${content}`; - } - - async search(query: string) { - const results = await this.searchTitles(query, 8); - if(!results.length) return `āŒ No results for "${query}"`; - const lines = [`### Search results for "${query}"\n`]; - for(let i = 0; i < results.length; i++) { - const r = results[i]; - const snippet = this.stripHtml(r.snippet || '').trim(); - lines.push(`**${i + 1}. ${r.title}**\n${snippet}\n${this.pageUrl(r.title)}`); - } - return lines.join('\n\n'); - } - } - - const wiki = new WikipediaClient(ua); - if(mode === 'search') return wiki.search(query); - return wiki.lookup(query, mode || 'summary'); - } -};