Compare commits

..

1 Commits

Author SHA1 Message Date
5eae84f6cf Added JSON / Summary LLM safeguard
All checks were successful
Publish Library / Build NPM Project (push) Successful in 1m1s
Publish Library / Tag Version (push) Successful in 14s
2026-03-26 12:24:20 -04:00
2 changed files with 48 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ztimson/ai-utils", "name": "@ztimson/ai-utils",
"version": "0.8.11", "version": "0.8.12",
"description": "AI Utility library", "description": "AI Utility library",
"author": "Zak Timson", "author": "Zak Timson",
"license": "MIT", "license": "MIT",

View File

@@ -1,3 +1,4 @@
import {sum} from '@tensorflow/tfjs';
import {JSONAttemptParse} from '@ztimson/utils'; import {JSONAttemptParse} 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';
@@ -357,11 +358,30 @@ class LLM {
* @returns {Promise<{} | {} | RegExpExecArray | null>} * @returns {Promise<{} | {} | RegExpExecArray | null>}
*/ */
async json(text: string, schema: string, options?: LLMRequest): Promise<any> { async json(text: string, schema: string, options?: LLMRequest): Promise<any> {
const code = await this.code(text, {...options, system: [ let system = `Your job is to convert input to JSON. Call \`submit\` exactly once with JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\``;
options?.system, if(options?.system) system += '\n\n' + options.system;
`Only respond using JSON matching this schema:\n\`\`\`json\n${schema}\n\`\`\`` return new Promise(async (resolve, reject) => {
].filter(t => !!t).join('\n')}); let done = false;
return code ? JSONAttemptParse(code, {}) : null; 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 * @param options LLM request options
* @returns {Promise<string>} Summary * @returns {Promise<string>} Summary
*/ */
summarize(text: string, tokens: number = 500, options?: LLMRequest): Promise<string | null> { async summarize(text: string, tokens: number = 500, options?: LLMRequest): Promise<string | null> {
return this.ask(text, {system: `Generate the shortest summary possible <= ${tokens} tokens. Output nothing else`, temperature: 0.3, ...options}); 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}`);
});
} }
} }