From eda4eed87db40344183541fc2ae42fd940b835c7 Mon Sep 17 00:00:00 2001 From: ztimson Date: Thu, 26 Mar 2026 12:50:52 -0400 Subject: [PATCH] Added JSON / Summary LLM safeguard --- package.json | 2 +- src/llm.ts | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b6329c5..0ad25ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/ai-utils", - "version": "0.8.13", + "version": "0.8.14", "description": "AI Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/llm.ts b/src/llm.ts index 3a80664..7b0faa9 100644 --- a/src/llm.ts +++ b/src/llm.ts @@ -358,7 +358,7 @@ class LLM { * @returns {Promise<{} | {} | RegExpExecArray | null>} */ async json(text: string, schema: string, options?: LLMRequest): Promise { - let system = `Your job is to convert input to JSON. Call the \`submit\` tool exactly once with JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\``; + let system = `Your job is to convert input to JSON using tool calls. Call the \`submit\` tool at least once with JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\`\n\nResponses are ignored`; if(options?.system) system += '\n\n' + options.system; return new Promise(async (resolve, reject) => { let done = false; @@ -376,11 +376,11 @@ class LLM { resolve(json); done = true; } catch { return 'Invalid JSON'; } - return 'Done'; + return 'Saved'; } }, ...(options?.tools || [])], }); - if(!done) reject(`AI failed to create summary: ${resp}`); + if(!done) reject(`AI failed to create JSON:\n${resp}`); }); } @@ -392,7 +392,7 @@ class LLM { * @returns {Promise} Summary */ async summarize(text: string, tokens: number = 500, options?: LLMRequest): Promise { - let system = `Your job is to summarize the users message. Call the \`submit\` tool exactly once with the shortest summary possible that's <= ${tokens} tokens. Output nothing else`; + let system = `Your job is to summarize the users message using tool calls. Call the \`submit\` tool at least once with the shortest summary possible that's <= ${tokens} tokens. The tool call will respond with the token count. Responses are ignored`; if(options?.system) system += '\n\n' + options.system; return new Promise(async (resolve, reject) => { let done = false; @@ -405,13 +405,16 @@ class LLM { description: 'Submit summary', args: {summary: {type: 'string', description: 'Text summarization', required: true}}, fn: (args) => { + if(!args.summary) return 'No summary provided'; + const count = this.estimateTokens(args.summary); + if(count > tokens) return `Summary is too long (${count} tokens)`; done = true; resolve(args.summary || null); - return 'Done'; + return `Saved (${count} tokens)`; } }, ...(options?.tools || [])], }); - if(!done) reject(`AI failed to create summary: ${resp}`); + if(!done) reject(`AI failed to create summary:\n${resp}`); }); } }