Fix anthropic message history
All checks were successful
Publish Library / Build NPM Project (push) Successful in 30s
Publish Library / Tag Version (push) Successful in 5s

This commit is contained in:
2026-02-11 22:45:30 -05:00
parent 8c64129200
commit 27506d20af
3 changed files with 23 additions and 24 deletions

View File

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

View File

@@ -13,25 +13,25 @@ export class Anthropic extends LLMProvider {
} }
private toStandard(history: any[]): LLMMessage[] { private toStandard(history: any[]): LLMMessage[] {
for(let i = 0; i < history.length; i++) { const timestamp = Date.now();
const orgI = i; const messages: LLMMessage[] = [];
if(typeof history[orgI].content != 'string') { for(let h of history) {
if(history[orgI].role == 'assistant') { if(typeof h.content == 'string') {
history[orgI].content.filter((c: any) => c.type =='tool_use').forEach((c: any) => { messages.push(<any>{timestamp, ...h});
history.splice(i + 1, 0, {role: 'tool', id: c.id, name: c.name, args: c.input, timestamp: Date.now()}); } else {
}); const textContent = h.content?.filter((c: any) => c.type == 'text').map((c: any) => c.text).join('\n\n');
} else if(history[orgI].role == 'user') { if(textContent) messages.push({timestamp, role: h.role, content: textContent});
history[orgI].content.filter((c: any) => c.type =='tool_result').forEach((c: any) => { h.content.forEach((c: any) => {
const h = history.find((h: any) => h.id == c.tool_use_id); if(c.type == 'tool_use') {
h[c.is_error ? 'error' : 'content'] = c.content; messages.push({timestamp, role: 'tool', id: c.id, name: c.name, args: c.input, content: undefined});
}); } else if(c.type == 'tool_result') {
} const m: any = messages.findLast(m => (<any>m).id == c.tool_use_id);
history[orgI].content = history[orgI].content?.filter((c: any) => c.type == 'text').map((c: any) => c.text).join('\n\n'); if(m) m[c.is_error ? 'error' : 'content'] = c.content;
if(!history[orgI].content) history.splice(orgI, 1); }
});
} }
if(!history[orgI].timestamp) history[orgI].timestamp = Date.now();
} }
return history.filter(h => !!h.content); return messages;
} }
private fromStandard(history: LLMMessage[]): any[] { private fromStandard(history: LLMMessage[]): any[] {
@@ -50,8 +50,8 @@ export class Anthropic extends LLMProvider {
ask(message: string, options: LLMRequest = {}): AbortablePromise<string> { ask(message: string, options: LLMRequest = {}): AbortablePromise<string> {
const controller = new AbortController(); const controller = new AbortController();
return Object.assign(new Promise<any>(async (res, rej) => { return Object.assign(new Promise<any>(async (res) => {
const history = this.fromStandard([...options.history || [], {role: 'user', content: message, timestamp: Date.now()}]); let history = this.fromStandard([...options.history || [], {role: 'user', content: message, timestamp: Date.now()}]);
const tools = options.tools || this.ai.options.llm?.tools || []; const tools = options.tools || this.ai.options.llm?.tools || [];
const requestParams: any = { const requestParams: any = {
model: options.model || this.model, model: options.model || this.model,
@@ -73,7 +73,6 @@ export class Anthropic extends LLMProvider {
}; };
let resp: any, isFirstMessage = true; let resp: any, isFirstMessage = true;
const assistantMessages: string[] = [];
do { do {
resp = await this.client.messages.create(requestParams).catch(err => { resp = await this.client.messages.create(requestParams).catch(err => {
err.message += `\n\nMessages:\n${JSON.stringify(history, null, 2)}`; err.message += `\n\nMessages:\n${JSON.stringify(history, null, 2)}`;
@@ -130,7 +129,7 @@ export class Anthropic extends LLMProvider {
} }
} while (!controller.signal.aborted && resp.content.some((c: any) => c.type === 'tool_use')); } while (!controller.signal.aborted && resp.content.some((c: any) => c.type === 'tool_use'));
history.push({role: 'assistant', content: resp.content.filter((c: any) => c.type == 'text').map((c: any) => c.text).join('\n\n')}); history.push({role: 'assistant', content: resp.content.filter((c: any) => c.type == 'text').map((c: any) => c.text).join('\n\n')});
this.toStandard(history); history = this.toStandard(history);
if(options.stream) options.stream({done: true}); if(options.stream) options.stream({done: true});
if(options.history) options.history.splice(0, options.history.length, ...history); if(options.history) options.history.splice(0, options.history.length, ...history);

View File

@@ -68,7 +68,7 @@ export class OpenAi extends LLMProvider {
const controller = new AbortController(); const controller = new AbortController();
return Object.assign(new Promise<any>(async (res, rej) => { return Object.assign(new Promise<any>(async (res, rej) => {
if(options.system && options.history?.[0]?.role != 'system') options.history?.splice(0, 0, {role: 'system', content: options.system, timestamp: Date.now()}); if(options.system && options.history?.[0]?.role != 'system') options.history?.splice(0, 0, {role: 'system', content: options.system, timestamp: Date.now()});
const history = this.fromStandard([...options.history || [], {role: 'user', content: message, timestamp: Date.now()}]); let history = this.fromStandard([...options.history || [], {role: 'user', content: message, timestamp: Date.now()}]);
const tools = options.tools || this.ai.options.llm?.tools || []; const tools = options.tools || this.ai.options.llm?.tools || [];
const requestParams: any = { const requestParams: any = {
model: options.model || this.model, model: options.model || this.model,
@@ -133,7 +133,7 @@ export class OpenAi extends LLMProvider {
} }
} while (!controller.signal.aborted && resp.choices?.[0]?.message?.tool_calls?.length); } while (!controller.signal.aborted && resp.choices?.[0]?.message?.tool_calls?.length);
history.push({role: 'assistant', content: resp.choices[0].message.content || ''}); history.push({role: 'assistant', content: resp.choices[0].message.content || ''});
this.toStandard(history); history = this.toStandard(history);
if(options.stream) options.stream({done: true}); if(options.stream) options.stream({done: true});
if(options.history) options.history.splice(0, options.history.length, ...history); if(options.history) options.history.splice(0, options.history.length, ...history);