diff --git a/package.json b/package.json index 347d2f2..d09c85e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/ai-utils", - "version": "0.8.11", + "version": "0.8.12", "description": "AI Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/llm.ts b/src/llm.ts index 3eb8e12..81d03f9 100644 --- a/src/llm.ts +++ b/src/llm.ts @@ -1,3 +1,4 @@ +import {sum} from '@tensorflow/tfjs'; import {JSONAttemptParse} from '@ztimson/utils'; import {AbortablePromise, Ai} from './ai.ts'; import {Anthropic} from './antrhopic.ts'; @@ -357,11 +358,30 @@ class LLM { * @returns {Promise<{} | {} | RegExpExecArray | null>} */ async json(text: string, schema: string, options?: LLMRequest): Promise { - const code = await this.code(text, {...options, system: [ - options?.system, - `Only respond using JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\`` - ].filter(t => !!t).join('\n')}); - return code ? JSONAttemptParse(code, {}) : null; + let system = `Your job is to convert input to JSON. Call \`submit\` exactly once with JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\``; + if(options?.system) system += '\n\n' + options.system; + return new Promise(async (resolve, reject) => { + let done = false; + const resp = await this.ask(text, { + temperature: 0.3, + ...options, + system, + tools: [{ + name: 'submit', + description: 'Submit JSON', + args: {json: {type: 'string', description: 'Javascript parsable JSON string', required: true}}, + fn: (args) => { + try { + const json = JSON.parse(args.json); + resolve(json); + done = true; + } catch { return 'Invalid JSON'; } + return 'Done'; + } + }, ...(options?.tools || [])], + }); + if(!done) reject(`AI failed to create summary: ${resp}`); + }); } /** @@ -371,8 +391,28 @@ class LLM { * @param options LLM request options * @returns {Promise} Summary */ - summarize(text: string, tokens: number = 500, options?: LLMRequest): Promise { - return this.ask(text, {system: `Generate the shortest summary possible <= ${tokens} tokens. Output nothing else`, temperature: 0.3, ...options}); + async summarize(text: string, tokens: number = 500, options?: LLMRequest): Promise { + let system = `Your job is to summarize the users message. Call \`submit\` exactly once with the shortest summary possible that's <= ${tokens} tokens. Output nothing else`; + if(options?.system) system += '\n\n' + options.system; + return new Promise(async (resolve, reject) => { + let done = false; + const resp = await this.ask(text, { + temperature: 0.3, + ...options, + system, + tools: [{ + name: 'submit', + description: 'Submit summary', + args: {summary: {type: 'string', description: 'Summarization', required: true}}, + fn: (args) => { + done = true; + resolve(args.summary || null); + return 'Done'; + } + }, ...(options?.tools || [])], + }); + if(!done) reject(`AI failed to create summary: ${resp}`); + }); } }