@ztimson/ai-utils 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai.d.ts +4 -2
- package/dist/index.js +6 -646
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +306 -441
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,648 +1,8 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@ztimson/node-utils"), require("tesseract.js"), require("@ztimson/utils"), require("@anthropic-ai/sdk"), require("ollama"), require("openai"), require("node:fs/promises"), require("node:path"), require("@tensorflow/tfjs")) : typeof define === "function" && define.amd ? define(["exports", "@ztimson/node-utils", "tesseract.js", "@ztimson/utils", "@anthropic-ai/sdk", "ollama", "openai", "node:fs/promises", "node:path", "@tensorflow/tfjs"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.utils = {}, global.nodeUtils, global.tesseract_js, global.utils, global.sdk, global.ollama, global.openai, global.fs, global.Path, global.tf));
|
|
3
|
-
})(this, function(exports2, nodeUtils, tesseract_js, utils, sdk, ollama, openai, fs, Path, tf) {
|
|
4
|
-
"use strict";var __defProp = Object.defineProperty;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1
|
+
(function(u,w){typeof exports=="object"&&typeof module<"u"?w(exports,require("@ztimson/node-utils"),require("tesseract.js"),require("@ztimson/utils"),require("@anthropic-ai/sdk"),require("ollama"),require("openai"),require("node:fs/promises"),require("node:path"),require("@tensorflow/tfjs")):typeof define=="function"&&define.amd?define(["exports","@ztimson/node-utils","tesseract.js","@ztimson/utils","@anthropic-ai/sdk","ollama","openai","node:fs/promises","node:path","@tensorflow/tfjs"],w):(u=typeof globalThis<"u"?globalThis:u||self,w(u.utils={},u.nodeUtils,u.tesseract_js,u.utils,u.sdk,u.ollama,u.openai,u.fs,u.Path,u.tf))})(this,(function(u,w,P,p,v,A,M,b,k,E){"use strict";function $(l){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(l){for(const e in l)if(e!=="default"){const n=Object.getOwnPropertyDescriptor(l,e);Object.defineProperty(t,e,n.get?n:{enumerable:!0,get:()=>l[e]})}}return t.default=l,Object.freeze(t)}const _=$(E);class S{}class j extends S{constructor(t,e,n){super(),this.ai=t,this.apiToken=e,this.model=n,this.client=new v.Anthropic({apiKey:e})}client;toStandard(t){for(let e=0;e<t.length;e++){const n=e;typeof t[n].content!="string"&&(t[n].role=="assistant"?t[n].content.filter(s=>s.type=="tool_use").forEach(s=>{e++,t.splice(e,0,{role:"tool",id:s.id,name:s.name,args:s.input})}):t[n].role=="user"&&t[n].content.filter(s=>s.type=="tool_result").forEach(s=>{const i=t.find(g=>g.id==s.tool_use_id);i[s.is_error?"error":"content"]=s.content}),t[n].content=t[n].content.filter(s=>s.type=="text").map(s=>s.text).join(`
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
-
enumerable: true,
|
|
16
|
-
get: () => e[k]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
n.default = e;
|
|
22
|
-
return Object.freeze(n);
|
|
23
|
-
}
|
|
24
|
-
const tf__namespace = /* @__PURE__ */ _interopNamespaceDefault(tf);
|
|
25
|
-
class LLMProvider {
|
|
26
|
-
}
|
|
27
|
-
class Anthropic extends LLMProvider {
|
|
28
|
-
constructor(ai, apiToken, model) {
|
|
29
|
-
super();
|
|
30
|
-
__publicField(this, "client");
|
|
31
|
-
this.ai = ai;
|
|
32
|
-
this.apiToken = apiToken;
|
|
33
|
-
this.model = model;
|
|
34
|
-
this.client = new sdk.Anthropic({ apiKey: apiToken });
|
|
35
|
-
}
|
|
36
|
-
toStandard(history) {
|
|
37
|
-
for (let i = 0; i < history.length; i++) {
|
|
38
|
-
const orgI = i;
|
|
39
|
-
if (typeof history[orgI].content != "string") {
|
|
40
|
-
if (history[orgI].role == "assistant") {
|
|
41
|
-
history[orgI].content.filter((c) => c.type == "tool_use").forEach((c) => {
|
|
42
|
-
i++;
|
|
43
|
-
history.splice(i, 0, { role: "tool", id: c.id, name: c.name, args: c.input });
|
|
44
|
-
});
|
|
45
|
-
} else if (history[orgI].role == "user") {
|
|
46
|
-
history[orgI].content.filter((c) => c.type == "tool_result").forEach((c) => {
|
|
47
|
-
const h = history.find((h2) => h2.id == c.tool_use_id);
|
|
48
|
-
h[c.is_error ? "error" : "content"] = c.content;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
history[orgI].content = history[orgI].content.filter((c) => c.type == "text").map((c) => c.text).join("\n\n");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return history.filter((h) => !!h.content);
|
|
55
|
-
}
|
|
56
|
-
fromStandard(history) {
|
|
57
|
-
for (let i = 0; i < history.length; i++) {
|
|
58
|
-
if (history[i].role == "tool") {
|
|
59
|
-
const h = history[i];
|
|
60
|
-
history.splice(
|
|
61
|
-
i,
|
|
62
|
-
1,
|
|
63
|
-
{ role: "assistant", content: [{ type: "tool_use", id: h.id, name: h.name, input: h.args }] },
|
|
64
|
-
{ role: "user", content: [{ type: "tool_result", tool_use_id: h.id, is_error: !!h.error, content: h.error || h.content }] }
|
|
65
|
-
);
|
|
66
|
-
i++;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return history;
|
|
70
|
-
}
|
|
71
|
-
ask(message, options = {}) {
|
|
72
|
-
const controller = new AbortController();
|
|
73
|
-
const response = new Promise(async (res, rej) => {
|
|
74
|
-
let history = this.fromStandard([...options.history || [], { role: "user", content: message }]);
|
|
75
|
-
if (options.compress) history = await this.ai.llm.compress(history, options.compress.max, options.compress.min, options);
|
|
76
|
-
const requestParams = {
|
|
77
|
-
model: options.model || this.model,
|
|
78
|
-
max_tokens: options.max_tokens || this.ai.options.max_tokens || 4096,
|
|
79
|
-
system: options.system || this.ai.options.system || "",
|
|
80
|
-
temperature: options.temperature || this.ai.options.temperature || 0.7,
|
|
81
|
-
tools: (options.tools || this.ai.options.tools || []).map((t) => ({
|
|
82
|
-
name: t.name,
|
|
83
|
-
description: t.description,
|
|
84
|
-
input_schema: {
|
|
85
|
-
type: "object",
|
|
86
|
-
properties: t.args ? utils.objectMap(t.args, (key, value) => ({ ...value, required: void 0 })) : {},
|
|
87
|
-
required: t.args ? Object.entries(t.args).filter((t2) => t2[1].required).map((t2) => t2[0]) : []
|
|
88
|
-
},
|
|
89
|
-
fn: void 0
|
|
90
|
-
})),
|
|
91
|
-
messages: history,
|
|
92
|
-
stream: !!options.stream
|
|
93
|
-
};
|
|
94
|
-
let resp;
|
|
95
|
-
do {
|
|
96
|
-
resp = await this.client.messages.create(requestParams);
|
|
97
|
-
if (options.stream) {
|
|
98
|
-
resp.content = [];
|
|
99
|
-
for await (const chunk of resp) {
|
|
100
|
-
if (controller.signal.aborted) break;
|
|
101
|
-
if (chunk.type === "content_block_start") {
|
|
102
|
-
if (chunk.content_block.type === "text") {
|
|
103
|
-
resp.content.push({ type: "text", text: "" });
|
|
104
|
-
} else if (chunk.content_block.type === "tool_use") {
|
|
105
|
-
resp.content.push({ type: "tool_use", id: chunk.content_block.id, name: chunk.content_block.name, input: "" });
|
|
106
|
-
}
|
|
107
|
-
} else if (chunk.type === "content_block_delta") {
|
|
108
|
-
if (chunk.delta.type === "text_delta") {
|
|
109
|
-
const text = chunk.delta.text;
|
|
110
|
-
resp.content.at(-1).text += text;
|
|
111
|
-
options.stream({ text });
|
|
112
|
-
} else if (chunk.delta.type === "input_json_delta") {
|
|
113
|
-
resp.content.at(-1).input += chunk.delta.partial_json;
|
|
114
|
-
}
|
|
115
|
-
} else if (chunk.type === "content_block_stop") {
|
|
116
|
-
const last = resp.content.at(-1);
|
|
117
|
-
if (last.input != null) last.input = last.input ? utils.JSONAttemptParse(last.input, {}) : {};
|
|
118
|
-
} else if (chunk.type === "message_stop") {
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const toolCalls = resp.content.filter((c) => c.type === "tool_use");
|
|
124
|
-
if (toolCalls.length && !controller.signal.aborted) {
|
|
125
|
-
history.push({ role: "assistant", content: resp.content });
|
|
126
|
-
const results = await Promise.all(toolCalls.map(async (toolCall) => {
|
|
127
|
-
var _a;
|
|
128
|
-
const tool = (_a = options.tools) == null ? void 0 : _a.find(utils.findByProp("name", toolCall.name));
|
|
129
|
-
if (!tool) return { tool_use_id: toolCall.id, is_error: true, content: "Tool not found" };
|
|
130
|
-
try {
|
|
131
|
-
const result = await tool.fn(toolCall.input, this.ai);
|
|
132
|
-
return { type: "tool_result", tool_use_id: toolCall.id, content: utils.JSONSanitize(result) };
|
|
133
|
-
} catch (err) {
|
|
134
|
-
return { type: "tool_result", tool_use_id: toolCall.id, is_error: true, content: (err == null ? void 0 : err.message) || (err == null ? void 0 : err.toString()) || "Unknown" };
|
|
135
|
-
}
|
|
136
|
-
}));
|
|
137
|
-
history.push({ role: "user", content: results });
|
|
138
|
-
requestParams.messages = history;
|
|
139
|
-
}
|
|
140
|
-
} while (!controller.signal.aborted && resp.content.some((c) => c.type === "tool_use"));
|
|
141
|
-
if (options.stream) options.stream({ done: true });
|
|
142
|
-
res(this.toStandard([...history, {
|
|
143
|
-
role: "assistant",
|
|
144
|
-
content: resp.content.filter((c) => c.type == "text").map((c) => c.text).join("\n\n")
|
|
145
|
-
}]));
|
|
146
|
-
});
|
|
147
|
-
return Object.assign(response, { abort: () => controller.abort() });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
class Ollama extends LLMProvider {
|
|
151
|
-
constructor(ai, host, model) {
|
|
152
|
-
super();
|
|
153
|
-
__publicField(this, "client");
|
|
154
|
-
this.ai = ai;
|
|
155
|
-
this.host = host;
|
|
156
|
-
this.model = model;
|
|
157
|
-
this.client = new ollama.Ollama({ host });
|
|
158
|
-
}
|
|
159
|
-
toStandard(history) {
|
|
160
|
-
for (let i = 0; i < history.length; i++) {
|
|
161
|
-
if (history[i].role == "assistant" && history[i].tool_calls) {
|
|
162
|
-
if (history[i].content) delete history[i].tool_calls;
|
|
163
|
-
else {
|
|
164
|
-
history.splice(i, 1);
|
|
165
|
-
i--;
|
|
166
|
-
}
|
|
167
|
-
} else if (history[i].role == "tool") {
|
|
168
|
-
const error = history[i].content.startsWith('{"error":');
|
|
169
|
-
history[i] = { role: "tool", name: history[i].tool_name, args: history[i].args, [error ? "error" : "content"]: history[i].content };
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return history;
|
|
173
|
-
}
|
|
174
|
-
fromStandard(history) {
|
|
175
|
-
return history.map((h) => {
|
|
176
|
-
if (h.role != "tool") return h;
|
|
177
|
-
return { role: "tool", tool_name: h.name, content: h.error || h.content };
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
ask(message, options = {}) {
|
|
181
|
-
const controller = new AbortController();
|
|
182
|
-
const response = new Promise(async (res, rej) => {
|
|
183
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
184
|
-
let system = options.system || this.ai.options.system;
|
|
185
|
-
let history = this.fromStandard([...options.history || [], { role: "user", content: message }]);
|
|
186
|
-
if (history[0].roll == "system") {
|
|
187
|
-
if (!system) system = history.shift();
|
|
188
|
-
else history.shift();
|
|
189
|
-
}
|
|
190
|
-
if (options.compress) history = await this.ai.llm.compress(history, options.compress.max, options.compress.min);
|
|
191
|
-
if (options.system) history.unshift({ role: "system", content: system });
|
|
192
|
-
const requestParams = {
|
|
193
|
-
model: options.model || this.model,
|
|
194
|
-
messages: history,
|
|
195
|
-
stream: !!options.stream,
|
|
196
|
-
signal: controller.signal,
|
|
197
|
-
options: {
|
|
198
|
-
temperature: options.temperature || this.ai.options.temperature || 0.7,
|
|
199
|
-
num_predict: options.max_tokens || this.ai.options.max_tokens || 4096
|
|
200
|
-
},
|
|
201
|
-
tools: (options.tools || this.ai.options.tools || []).map((t) => ({
|
|
202
|
-
type: "function",
|
|
203
|
-
function: {
|
|
204
|
-
name: t.name,
|
|
205
|
-
description: t.description,
|
|
206
|
-
parameters: {
|
|
207
|
-
type: "object",
|
|
208
|
-
properties: t.args ? utils.objectMap(t.args, (key, value) => ({ ...value, required: void 0 })) : {},
|
|
209
|
-
required: t.args ? Object.entries(t.args).filter((t2) => t2[1].required).map((t2) => t2[0]) : []
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}))
|
|
213
|
-
};
|
|
214
|
-
let resp;
|
|
215
|
-
do {
|
|
216
|
-
resp = await this.client.chat(requestParams);
|
|
217
|
-
if (options.stream) {
|
|
218
|
-
resp.message = { role: "assistant", content: "", tool_calls: [] };
|
|
219
|
-
for await (const chunk of resp) {
|
|
220
|
-
if (controller.signal.aborted) break;
|
|
221
|
-
if ((_a = chunk.message) == null ? void 0 : _a.content) {
|
|
222
|
-
resp.message.content += chunk.message.content;
|
|
223
|
-
options.stream({ text: chunk.message.content });
|
|
224
|
-
}
|
|
225
|
-
if ((_b = chunk.message) == null ? void 0 : _b.tool_calls) resp.message.tool_calls = chunk.message.tool_calls;
|
|
226
|
-
if (chunk.done) break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
if (((_d = (_c = resp.message) == null ? void 0 : _c.tool_calls) == null ? void 0 : _d.length) && !controller.signal.aborted) {
|
|
230
|
-
history.push(resp.message);
|
|
231
|
-
const results = await Promise.all(resp.message.tool_calls.map(async (toolCall) => {
|
|
232
|
-
var _a2;
|
|
233
|
-
const tool = (_a2 = options.tools || this.ai.options.tools) == null ? void 0 : _a2.find(utils.findByProp("name", toolCall.function.name));
|
|
234
|
-
if (!tool) return { role: "tool", tool_name: toolCall.function.name, content: '{"error": "Tool not found"}' };
|
|
235
|
-
const args = typeof toolCall.function.arguments === "string" ? utils.JSONAttemptParse(toolCall.function.arguments, {}) : toolCall.function.arguments;
|
|
236
|
-
try {
|
|
237
|
-
const result = await tool.fn(args, this.ai);
|
|
238
|
-
return { role: "tool", tool_name: toolCall.function.name, args, content: utils.JSONSanitize(result) };
|
|
239
|
-
} catch (err) {
|
|
240
|
-
return { role: "tool", tool_name: toolCall.function.name, args, content: utils.JSONSanitize({ error: (err == null ? void 0 : err.message) || (err == null ? void 0 : err.toString()) || "Unknown" }) };
|
|
241
|
-
}
|
|
242
|
-
}));
|
|
243
|
-
history.push(...results);
|
|
244
|
-
requestParams.messages = history;
|
|
245
|
-
}
|
|
246
|
-
} while (!controller.signal.aborted && ((_f = (_e = resp.message) == null ? void 0 : _e.tool_calls) == null ? void 0 : _f.length));
|
|
247
|
-
if (options.stream) options.stream({ done: true });
|
|
248
|
-
res(this.toStandard([...history, { role: "assistant", content: (_g = resp.message) == null ? void 0 : _g.content }]));
|
|
249
|
-
});
|
|
250
|
-
return Object.assign(response, { abort: () => controller.abort() });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
class OpenAi extends LLMProvider {
|
|
254
|
-
constructor(ai, apiToken, model) {
|
|
255
|
-
super();
|
|
256
|
-
__publicField(this, "client");
|
|
257
|
-
this.ai = ai;
|
|
258
|
-
this.apiToken = apiToken;
|
|
259
|
-
this.model = model;
|
|
260
|
-
this.client = new openai.OpenAI({ apiKey: apiToken });
|
|
261
|
-
}
|
|
262
|
-
toStandard(history) {
|
|
263
|
-
for (let i = 0; i < history.length; i++) {
|
|
264
|
-
const h = history[i];
|
|
265
|
-
if (h.role === "assistant" && h.tool_calls) {
|
|
266
|
-
const tools = h.tool_calls.map((tc) => ({
|
|
267
|
-
role: "tool",
|
|
268
|
-
id: tc.id,
|
|
269
|
-
name: tc.function.name,
|
|
270
|
-
args: utils.JSONAttemptParse(tc.function.arguments, {})
|
|
271
|
-
}));
|
|
272
|
-
history.splice(i, 1, ...tools);
|
|
273
|
-
i += tools.length - 1;
|
|
274
|
-
} else if (h.role === "tool" && h.content) {
|
|
275
|
-
const record = history.find((h2) => h.tool_call_id == h2.id);
|
|
276
|
-
if (record) {
|
|
277
|
-
if (h.content.includes('"error":')) record.error = h.content;
|
|
278
|
-
else record.content = h.content;
|
|
279
|
-
}
|
|
280
|
-
history.splice(i, 1);
|
|
281
|
-
i--;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return history;
|
|
285
|
-
}
|
|
286
|
-
fromStandard(history) {
|
|
287
|
-
return history.reduce((result, h) => {
|
|
288
|
-
if (h.role === "tool") {
|
|
289
|
-
result.push({
|
|
290
|
-
role: "assistant",
|
|
291
|
-
content: null,
|
|
292
|
-
tool_calls: [{ id: h.id, type: "function", function: { name: h.name, arguments: JSON.stringify(h.args) } }],
|
|
293
|
-
refusal: null,
|
|
294
|
-
annotations: []
|
|
295
|
-
}, {
|
|
296
|
-
role: "tool",
|
|
297
|
-
tool_call_id: h.id,
|
|
298
|
-
content: h.error || h.content
|
|
299
|
-
});
|
|
300
|
-
} else {
|
|
301
|
-
result.push(h);
|
|
302
|
-
}
|
|
303
|
-
return result;
|
|
304
|
-
}, []);
|
|
305
|
-
}
|
|
306
|
-
ask(message, options = {}) {
|
|
307
|
-
const controller = new AbortController();
|
|
308
|
-
const response = new Promise(async (res, rej) => {
|
|
309
|
-
var _a, _b, _c, _d;
|
|
310
|
-
let history = this.fromStandard([...options.history || [], { role: "user", content: message }]);
|
|
311
|
-
if (options.compress) history = await this.ai.llm.compress(history, options.compress.max, options.compress.min, options);
|
|
312
|
-
const requestParams = {
|
|
313
|
-
model: options.model || this.model,
|
|
314
|
-
messages: history,
|
|
315
|
-
stream: !!options.stream,
|
|
316
|
-
max_tokens: options.max_tokens || this.ai.options.max_tokens || 4096,
|
|
317
|
-
temperature: options.temperature || this.ai.options.temperature || 0.7,
|
|
318
|
-
tools: (options.tools || this.ai.options.tools || []).map((t) => ({
|
|
319
|
-
type: "function",
|
|
320
|
-
function: {
|
|
321
|
-
name: t.name,
|
|
322
|
-
description: t.description,
|
|
323
|
-
parameters: {
|
|
324
|
-
type: "object",
|
|
325
|
-
properties: t.args ? utils.objectMap(t.args, (key, value) => ({ ...value, required: void 0 })) : {},
|
|
326
|
-
required: t.args ? Object.entries(t.args).filter((t2) => t2[1].required).map((t2) => t2[0]) : []
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}))
|
|
330
|
-
};
|
|
331
|
-
let resp;
|
|
332
|
-
do {
|
|
333
|
-
resp = await this.client.chat.completions.create(requestParams);
|
|
334
|
-
if (options.stream) {
|
|
335
|
-
resp.choices = [];
|
|
336
|
-
for await (const chunk of resp) {
|
|
337
|
-
if (controller.signal.aborted) break;
|
|
338
|
-
if (chunk.choices[0].delta.content) {
|
|
339
|
-
options.stream({ text: chunk.choices[0].delta.content });
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
const toolCalls = resp.choices[0].message.tool_calls || [];
|
|
344
|
-
if (toolCalls.length && !controller.signal.aborted) {
|
|
345
|
-
history.push(resp.choices[0].message);
|
|
346
|
-
const results = await Promise.all(toolCalls.map(async (toolCall) => {
|
|
347
|
-
var _a2;
|
|
348
|
-
const tool = (_a2 = options.tools) == null ? void 0 : _a2.find(utils.findByProp("name", toolCall.function.name));
|
|
349
|
-
if (!tool) return { role: "tool", tool_call_id: toolCall.id, content: '{"error": "Tool not found"}' };
|
|
350
|
-
try {
|
|
351
|
-
const args = utils.JSONAttemptParse(toolCall.function.arguments, {});
|
|
352
|
-
const result = await tool.fn(args, this.ai);
|
|
353
|
-
return { role: "tool", tool_call_id: toolCall.id, content: utils.JSONSanitize(result) };
|
|
354
|
-
} catch (err) {
|
|
355
|
-
return { role: "tool", tool_call_id: toolCall.id, content: utils.JSONSanitize({ error: (err == null ? void 0 : err.message) || (err == null ? void 0 : err.toString()) || "Unknown" }) };
|
|
356
|
-
}
|
|
357
|
-
}));
|
|
358
|
-
history.push(...results);
|
|
359
|
-
requestParams.messages = history;
|
|
360
|
-
}
|
|
361
|
-
} while (!controller.signal.aborted && ((_d = (_c = (_b = (_a = resp.choices) == null ? void 0 : _a[0]) == null ? void 0 : _b.message) == null ? void 0 : _c.tool_calls) == null ? void 0 : _d.length));
|
|
362
|
-
if (options.stream) options.stream({ done: true });
|
|
363
|
-
res(this.toStandard([...history, { role: "assistant", content: resp.choices[0].message.content || "" }]));
|
|
364
|
-
});
|
|
365
|
-
return Object.assign(response, { abort: () => controller.abort() });
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
class LLM {
|
|
369
|
-
constructor(ai, options) {
|
|
370
|
-
__publicField(this, "providers", {});
|
|
371
|
-
var _a, _b, _c;
|
|
372
|
-
this.ai = ai;
|
|
373
|
-
this.options = options;
|
|
374
|
-
if ((_a = options.anthropic) == null ? void 0 : _a.token) this.providers.anthropic = new Anthropic(this.ai, options.anthropic.token, options.anthropic.model);
|
|
375
|
-
if ((_b = options.ollama) == null ? void 0 : _b.host) this.providers.ollama = new Ollama(this.ai, options.ollama.host, options.ollama.model);
|
|
376
|
-
if ((_c = options.openAi) == null ? void 0 : _c.token) this.providers.openAi = new OpenAi(this.ai, options.openAi.token, options.openAi.model);
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Chat with LLM
|
|
380
|
-
* @param {string} message Question
|
|
381
|
-
* @param {LLMRequest} options Configuration options and chat history
|
|
382
|
-
* @returns {{abort: () => void, response: Promise<LLMMessage[]>}} Function to abort response and chat history
|
|
383
|
-
*/
|
|
384
|
-
ask(message, options = {}) {
|
|
385
|
-
var _a, _b;
|
|
386
|
-
let model = [null, null];
|
|
387
|
-
if (options.model) {
|
|
388
|
-
if (typeof options.model == "object") model = options.model;
|
|
389
|
-
else model = [options.model, (_a = this.options[options.model]) == null ? void 0 : _a.model];
|
|
390
|
-
}
|
|
391
|
-
if (!options.model || model[1] == null) {
|
|
392
|
-
if (typeof this.options.model == "object") model = this.options.model;
|
|
393
|
-
else model = [this.options.model, (_b = this.options[this.options.model]) == null ? void 0 : _b.model];
|
|
394
|
-
}
|
|
395
|
-
if (!model[0] || !model[1]) throw new Error(`Unknown LLM provider or model: ${model[0]} / ${model[1]}`);
|
|
396
|
-
return this.providers[model[0]].ask(message, { ...options, model: model[1] });
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Compress chat history to reduce context size
|
|
400
|
-
* @param {LLMMessage[]} history Chatlog that will be compressed
|
|
401
|
-
* @param max Trigger compression once context is larger than max
|
|
402
|
-
* @param min Summarize until context size is less than min
|
|
403
|
-
* @param {LLMRequest} options LLM options
|
|
404
|
-
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
405
|
-
*/
|
|
406
|
-
async compress(history, max, min, options) {
|
|
407
|
-
if (this.estimateTokens(history) < max) return history;
|
|
408
|
-
let keep = 0, tokens = 0;
|
|
409
|
-
for (let m of history.toReversed()) {
|
|
410
|
-
tokens += this.estimateTokens(m.content);
|
|
411
|
-
if (tokens < min) keep++;
|
|
412
|
-
else break;
|
|
413
|
-
}
|
|
414
|
-
if (history.length <= keep) return history;
|
|
415
|
-
const recent = keep == 0 ? [] : history.slice(-keep), process = (keep == 0 ? history : history.slice(0, -keep)).filter((h) => h.role === "assistant" || h.role === "user");
|
|
416
|
-
const summary = await this.summarize(process.map((m) => `${m.role}: ${m.content}`).join("\n\n"), 250, options);
|
|
417
|
-
return [{ role: "assistant", content: `Conversation Summary: ${summary}` }, ...recent];
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* Estimate variable as tokens
|
|
421
|
-
* @param history Object to size
|
|
422
|
-
* @returns {number} Rough token count
|
|
423
|
-
*/
|
|
424
|
-
estimateTokens(history) {
|
|
425
|
-
const text = JSON.stringify(history);
|
|
426
|
-
return Math.ceil(text.length / 4 * 1.2);
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Ask a question with JSON response
|
|
430
|
-
* @param {string} message Question
|
|
431
|
-
* @param {LLMRequest} options Configuration options and chat history
|
|
432
|
-
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
433
|
-
*/
|
|
434
|
-
async json(message, options) {
|
|
435
|
-
var _a;
|
|
436
|
-
let resp = await this.ask(message, {
|
|
437
|
-
system: "Respond using a JSON blob",
|
|
438
|
-
...options
|
|
439
|
-
});
|
|
440
|
-
if (!((_a = resp == null ? void 0 : resp[0]) == null ? void 0 : _a.content)) return {};
|
|
441
|
-
return utils.JSONAttemptParse(new RegExp("{[sS]*}").exec(resp[0].content), {});
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Create a summary of some text
|
|
445
|
-
* @param {string} text Text to summarize
|
|
446
|
-
* @param {number} tokens Max number of tokens
|
|
447
|
-
* @param options LLM request options
|
|
448
|
-
* @returns {Promise<string>} Summary
|
|
449
|
-
*/
|
|
450
|
-
summarize(text, tokens, options) {
|
|
451
|
-
return this.ask(text, { system: `Generate a brief summary <= ${tokens} tokens. Output nothing else`, temperature: 0.3, ...options }).then((history) => {
|
|
452
|
-
var _a;
|
|
453
|
-
return ((_a = history.pop()) == null ? void 0 : _a.content) || null;
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
class Ai {
|
|
458
|
-
constructor(options) {
|
|
459
|
-
__publicField(this, "downloads", {});
|
|
460
|
-
__publicField(this, "whisperModel");
|
|
461
|
-
/** Large Language Models */
|
|
462
|
-
__publicField(this, "llm");
|
|
463
|
-
var _a;
|
|
464
|
-
this.options = options;
|
|
465
|
-
this.llm = new LLM(this, options);
|
|
466
|
-
if ((_a = this.options.whisper) == null ? void 0 : _a.binary) this.downloadAsrModel(this.options.whisper.model);
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Convert audio to text using Auditory Speech Recognition
|
|
470
|
-
* @param {string} path Path to audio
|
|
471
|
-
* @param model Whisper model
|
|
472
|
-
* @returns {Promise<any>} Extracted text
|
|
473
|
-
*/
|
|
474
|
-
async asr(path, model) {
|
|
475
|
-
var _a;
|
|
476
|
-
if (!((_a = this.options.whisper) == null ? void 0 : _a.binary)) throw new Error("Whisper not configured");
|
|
477
|
-
if (!model) model = this.options.whisper.model;
|
|
478
|
-
await this.downloadAsrModel(model);
|
|
479
|
-
const name = Math.random().toString(36).substring(2, 10) + "-" + path.split("/").pop();
|
|
480
|
-
const output = Path.join(this.options.whisper.path || "/tmp", name);
|
|
481
|
-
await nodeUtils.$`rm -f /tmp/${name}.txt && ${this.options.whisper.binary} -nt -np -m ${this.whisperModel} -f ${path} -otxt -of ${output}`;
|
|
482
|
-
return fs.readFile(output, "utf-8").then((text) => (text == null ? void 0 : text.trim()) || null).finally(() => fs.rm(output, { force: true }).catch(() => {
|
|
483
|
-
}));
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* Downloads the specified Whisper model if it is not already present locally.
|
|
487
|
-
*
|
|
488
|
-
* @param {string} model Whisper model that will be downloaded
|
|
489
|
-
* @return {Promise<void>} A promise that resolves once the model is downloaded and saved locally.
|
|
490
|
-
*/
|
|
491
|
-
async downloadAsrModel(model) {
|
|
492
|
-
var _a, _b, _c, _d;
|
|
493
|
-
if (!((_a = this.options.whisper) == null ? void 0 : _a.binary)) throw new Error("Whisper not configured");
|
|
494
|
-
this.whisperModel = Path.join((_b = this.options.whisper) == null ? void 0 : _b.path, ((_c = this.options.whisper) == null ? void 0 : _c.model) + ".bin");
|
|
495
|
-
if (await fs.stat(this.whisperModel).then(() => true).catch(() => false)) return;
|
|
496
|
-
if (!!this.downloads[model]) return this.downloads[model];
|
|
497
|
-
this.downloads[model] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${(_d = this.options.whisper) == null ? void 0 : _d.model}.bin`).then((resp) => resp.arrayBuffer()).then((arr) => Buffer.from(arr)).then(async (buffer) => {
|
|
498
|
-
await fs.writeFile(this.whisperModel, buffer);
|
|
499
|
-
delete this.downloads[model];
|
|
500
|
-
});
|
|
501
|
-
return this.downloads[model];
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* Convert image to text using Optical Character Recognition
|
|
505
|
-
* @param {string} path Path to image
|
|
506
|
-
* @returns {{abort: Function, response: Promise<string | null>}} Abort function & Promise of extracted text
|
|
507
|
-
*/
|
|
508
|
-
ocr(path) {
|
|
509
|
-
let worker;
|
|
510
|
-
return {
|
|
511
|
-
abort: () => {
|
|
512
|
-
worker == null ? void 0 : worker.terminate();
|
|
513
|
-
},
|
|
514
|
-
response: new Promise(async (res) => {
|
|
515
|
-
worker = await tesseract_js.createWorker("eng");
|
|
516
|
-
const { data } = await worker.recognize(path);
|
|
517
|
-
await worker.terminate();
|
|
518
|
-
res(data.text.trim() || null);
|
|
519
|
-
})
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Compare the difference between two strings using tensor math
|
|
524
|
-
* @param target Text that will checked
|
|
525
|
-
* @param {string} searchTerms Multiple search terms to check against target
|
|
526
|
-
* @returns {{avg: number, max: number, similarities: number[]}} Similarity values 0-1: 0 = unique, 1 = identical
|
|
527
|
-
*/
|
|
528
|
-
semanticSimilarity(target, ...searchTerms) {
|
|
529
|
-
if (searchTerms.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
530
|
-
const vector = (text, dimensions = 10) => {
|
|
531
|
-
return text.toLowerCase().split("").map((char, index) => char.charCodeAt(0) * (index + 1) % dimensions / dimensions).slice(0, dimensions);
|
|
532
|
-
};
|
|
533
|
-
const cosineSimilarity = (v1, v2) => {
|
|
534
|
-
if (v1.length !== v2.length) throw new Error("Vectors must be same length");
|
|
535
|
-
const tensor1 = tf__namespace.tensor1d(v1), tensor2 = tf__namespace.tensor1d(v2);
|
|
536
|
-
const dotProduct = tf__namespace.dot(tensor1, tensor2);
|
|
537
|
-
const magnitude1 = tf__namespace.norm(tensor1);
|
|
538
|
-
const magnitude2 = tf__namespace.norm(tensor2);
|
|
539
|
-
if (magnitude1.dataSync()[0] === 0 || magnitude2.dataSync()[0] === 0) return 0;
|
|
540
|
-
return dotProduct.dataSync()[0] / (magnitude1.dataSync()[0] * magnitude2.dataSync()[0]);
|
|
541
|
-
};
|
|
542
|
-
const v = vector(target);
|
|
543
|
-
const similarities = searchTerms.map((t) => vector(t)).map((refVector) => cosineSimilarity(v, refVector));
|
|
544
|
-
return { avg: similarities.reduce((acc, s) => acc + s, 0) / similarities.length, max: Math.max(...similarities), similarities };
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
const CliTool = {
|
|
548
|
-
name: "cli",
|
|
549
|
-
description: "Use the command line interface, returns any output",
|
|
550
|
-
args: { command: { type: "string", description: "Command to run", required: true } },
|
|
551
|
-
fn: (args) => nodeUtils.$`${args.command}`
|
|
552
|
-
};
|
|
553
|
-
const DateTimeTool = {
|
|
554
|
-
name: "get_datetime",
|
|
555
|
-
description: "Get current date and time",
|
|
556
|
-
args: {},
|
|
557
|
-
fn: async () => (/* @__PURE__ */ new Date()).toISOString()
|
|
558
|
-
};
|
|
559
|
-
const ExecTool = {
|
|
560
|
-
name: "exec",
|
|
561
|
-
description: "Run code/scripts",
|
|
562
|
-
args: {
|
|
563
|
-
language: { type: "string", description: "Execution language", enum: ["cli", "node", "python"], required: true },
|
|
564
|
-
code: { type: "string", description: "Code to execute", required: true }
|
|
565
|
-
},
|
|
566
|
-
fn: async (args, ai) => {
|
|
567
|
-
try {
|
|
568
|
-
switch (args.type) {
|
|
569
|
-
case "bash":
|
|
570
|
-
return await CliTool.fn({ command: args.code }, ai);
|
|
571
|
-
case "node":
|
|
572
|
-
return await JSTool.fn({ code: args.code }, ai);
|
|
573
|
-
case "python": {
|
|
574
|
-
return await PythonTool.fn({ code: args.code }, ai);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
} catch (err) {
|
|
578
|
-
return { error: (err == null ? void 0 : err.message) || err.toString() };
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
const FetchTool = {
|
|
583
|
-
name: "fetch",
|
|
584
|
-
description: "Make HTTP request to URL",
|
|
585
|
-
args: {
|
|
586
|
-
url: { type: "string", description: "URL to fetch", required: true },
|
|
587
|
-
method: { type: "string", description: "HTTP method to use", enum: ["GET", "POST", "PUT", "DELETE"], default: "GET" },
|
|
588
|
-
headers: { type: "object", description: "HTTP headers to send", default: {} },
|
|
589
|
-
body: { type: "object", description: "HTTP body to send" }
|
|
590
|
-
},
|
|
591
|
-
fn: (args) => new utils.Http({ url: args.url, headers: args.headers }).request({ method: args.method || "GET", body: args.body })
|
|
592
|
-
};
|
|
593
|
-
const JSTool = {
|
|
594
|
-
name: "exec_javascript",
|
|
595
|
-
description: "Execute commonjs javascript",
|
|
596
|
-
args: {
|
|
597
|
-
code: { type: "string", description: "CommonJS javascript", required: true }
|
|
598
|
-
},
|
|
599
|
-
fn: async (args) => {
|
|
600
|
-
const console = utils.consoleInterceptor(null);
|
|
601
|
-
const resp = await utils.fn({ console }, args.code, true).catch((err) => console.output.error.push(err));
|
|
602
|
-
return { ...console.output, return: resp, stdout: void 0, stderr: void 0 };
|
|
603
|
-
}
|
|
604
|
-
};
|
|
605
|
-
const PythonTool = {
|
|
606
|
-
name: "exec_javascript",
|
|
607
|
-
description: "Execute commonjs javascript",
|
|
608
|
-
args: {
|
|
609
|
-
code: { type: "string", description: "CommonJS javascript", required: true }
|
|
610
|
-
},
|
|
611
|
-
fn: async (args) => ({ result: nodeUtils.$Sync`python -c "${args.code}"` })
|
|
612
|
-
};
|
|
613
|
-
const SearchTool = {
|
|
614
|
-
name: "search",
|
|
615
|
-
description: "Use a search engine to find relevant URLs, should be changed with fetch to scrape sources",
|
|
616
|
-
args: {
|
|
617
|
-
query: { type: "string", description: "Search string", required: true },
|
|
618
|
-
length: { type: "string", description: "Number of results to return", default: 5 }
|
|
619
|
-
},
|
|
620
|
-
fn: async (args) => {
|
|
621
|
-
var _a;
|
|
622
|
-
const html = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(args.query)}`, {
|
|
623
|
-
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
624
|
-
}).then((resp) => resp.text());
|
|
625
|
-
let match, regex = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
626
|
-
const results = new utils.ASet();
|
|
627
|
-
while ((match = regex.exec(html)) !== null) {
|
|
628
|
-
let url = (_a = /uddg=(.+)&?/.exec(decodeURIComponent(match[1]))) == null ? void 0 : _a[1];
|
|
629
|
-
if (url) url = decodeURIComponent(url);
|
|
630
|
-
if (url) results.add(url);
|
|
631
|
-
if (results.size >= (args.length || 5)) break;
|
|
632
|
-
}
|
|
633
|
-
return results;
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
exports2.Ai = Ai;
|
|
637
|
-
exports2.Anthropic = Anthropic;
|
|
638
|
-
exports2.CliTool = CliTool;
|
|
639
|
-
exports2.DateTimeTool = DateTimeTool;
|
|
640
|
-
exports2.ExecTool = ExecTool;
|
|
641
|
-
exports2.FetchTool = FetchTool;
|
|
642
|
-
exports2.JSTool = JSTool;
|
|
643
|
-
exports2.LLM = LLM;
|
|
644
|
-
exports2.PythonTool = PythonTool;
|
|
645
|
-
exports2.SearchTool = SearchTool;
|
|
646
|
-
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
647
|
-
});
|
|
3
|
+
`))}return t.filter(e=>!!e.content)}fromStandard(t){for(let e=0;e<t.length;e++)if(t[e].role=="tool"){const n=t[e];t.splice(e,1,{role:"assistant",content:[{type:"tool_use",id:n.id,name:n.name,input:n.args}]},{role:"user",content:[{type:"tool_result",tool_use_id:n.id,is_error:!!n.error,content:n.error||n.content}]}),e++}return t}ask(t,e={}){const n=new AbortController,s=new Promise(async(i,g)=>{let c=this.fromStandard([...e.history||[],{role:"user",content:t}]);e.compress&&(c=await this.ai.llm.compress(c,e.compress.max,e.compress.min,e));const d={model:e.model||this.model,max_tokens:e.max_tokens||this.ai.options.max_tokens||4096,system:e.system||this.ai.options.system||"",temperature:e.temperature||this.ai.options.temperature||.7,tools:(e.tools||this.ai.options.tools||[]).map(o=>({name:o.name,description:o.description,input_schema:{type:"object",properties:o.args?p.objectMap(o.args,(r,a)=>({...a,required:void 0})):{},required:o.args?Object.entries(o.args).filter(r=>r[1].required).map(r=>r[0]):[]},fn:void 0})),messages:c,stream:!!e.stream};let m;do{if(m=await this.client.messages.create(d),e.stream){m.content=[];for await(const r of m){if(n.signal.aborted)break;if(r.type==="content_block_start")r.content_block.type==="text"?m.content.push({type:"text",text:""}):r.content_block.type==="tool_use"&&m.content.push({type:"tool_use",id:r.content_block.id,name:r.content_block.name,input:""});else if(r.type==="content_block_delta")if(r.delta.type==="text_delta"){const a=r.delta.text;m.content.at(-1).text+=a,e.stream({text:a})}else r.delta.type==="input_json_delta"&&(m.content.at(-1).input+=r.delta.partial_json);else if(r.type==="content_block_stop"){const a=m.content.at(-1);a.input!=null&&(a.input=a.input?p.JSONAttemptParse(a.input,{}):{})}else if(r.type==="message_stop")break}}const o=m.content.filter(r=>r.type==="tool_use");if(o.length&&!n.signal.aborted){c.push({role:"assistant",content:m.content});const r=await Promise.all(o.map(async a=>{const f=e.tools?.find(p.findByProp("name",a.name));if(!f)return{tool_use_id:a.id,is_error:!0,content:"Tool not found"};try{const h=await f.fn(a.input,this.ai);return{type:"tool_result",tool_use_id:a.id,content:p.JSONSanitize(h)}}catch(h){return{type:"tool_result",tool_use_id:a.id,is_error:!0,content:h?.message||h?.toString()||"Unknown"}}}));c.push({role:"user",content:r}),d.messages=c}}while(!n.signal.aborted&&m.content.some(o=>o.type==="tool_use"));e.stream&&e.stream({done:!0}),i(this.toStandard([...c,{role:"assistant",content:m.content.filter(o=>o.type=="text").map(o=>o.text).join(`
|
|
4
|
+
|
|
5
|
+
`)}]))});return Object.assign(s,{abort:()=>n.abort()})}}class J extends S{constructor(t,e,n){super(),this.ai=t,this.host=e,this.model=n,this.client=new A.Ollama({host:e})}client;toStandard(t){for(let e=0;e<t.length;e++)if(t[e].role=="assistant"&&t[e].tool_calls)t[e].content?delete t[e].tool_calls:(t.splice(e,1),e--);else if(t[e].role=="tool"){const n=t[e].content.startsWith('{"error":');t[e]={role:"tool",name:t[e].tool_name,args:t[e].args,[n?"error":"content"]:t[e].content}}return t}fromStandard(t){return t.map(e=>e.role!="tool"?e:{role:"tool",tool_name:e.name,content:e.error||e.content})}ask(t,e={}){const n=new AbortController,s=new Promise(async(i,g)=>{let c=e.system||this.ai.options.system,d=this.fromStandard([...e.history||[],{role:"user",content:t}]);d[0].roll=="system"&&(c?d.shift():c=d.shift()),e.compress&&(d=await this.ai.llm.compress(d,e.compress.max,e.compress.min)),e.system&&d.unshift({role:"system",content:c});const m={model:e.model||this.model,messages:d,stream:!!e.stream,signal:n.signal,options:{temperature:e.temperature||this.ai.options.temperature||.7,num_predict:e.max_tokens||this.ai.options.max_tokens||4096},tools:(e.tools||this.ai.options.tools||[]).map(r=>({type:"function",function:{name:r.name,description:r.description,parameters:{type:"object",properties:r.args?p.objectMap(r.args,(a,f)=>({...f,required:void 0})):{},required:r.args?Object.entries(r.args).filter(a=>a[1].required).map(a=>a[0]):[]}}}))};let o;do{if(o=await this.client.chat(m),e.stream){o.message={role:"assistant",content:"",tool_calls:[]};for await(const r of o)if(n.signal.aborted||(r.message?.content&&(o.message.content+=r.message.content,e.stream({text:r.message.content})),r.message?.tool_calls&&(o.message.tool_calls=r.message.tool_calls),r.done))break}if(o.message?.tool_calls?.length&&!n.signal.aborted){d.push(o.message);const r=await Promise.all(o.message.tool_calls.map(async a=>{const f=(e.tools||this.ai.options.tools)?.find(p.findByProp("name",a.function.name));if(!f)return{role:"tool",tool_name:a.function.name,content:'{"error": "Tool not found"}'};const h=typeof a.function.arguments=="string"?p.JSONAttemptParse(a.function.arguments,{}):a.function.arguments;try{const y=await f.fn(h,this.ai);return{role:"tool",tool_name:a.function.name,args:h,content:p.JSONSanitize(y)}}catch(y){return{role:"tool",tool_name:a.function.name,args:h,content:p.JSONSanitize({error:y?.message||y?.toString()||"Unknown"})}}}));d.push(...r),m.messages=d}}while(!n.signal.aborted&&o.message?.tool_calls?.length);e.stream&&e.stream({done:!0}),i(this.toStandard([...d,{role:"assistant",content:o.message?.content}]))});return Object.assign(s,{abort:()=>n.abort()})}}class N extends S{constructor(t,e,n){super(),this.ai=t,this.apiToken=e,this.model=n,this.client=new M.OpenAI({apiKey:e})}client;toStandard(t){for(let e=0;e<t.length;e++){const n=t[e];if(n.role==="assistant"&&n.tool_calls){const s=n.tool_calls.map(i=>({role:"tool",id:i.id,name:i.function.name,args:p.JSONAttemptParse(i.function.arguments,{})}));t.splice(e,1,...s),e+=s.length-1}else if(n.role==="tool"&&n.content){const s=t.find(i=>n.tool_call_id==i.id);s&&(n.content.includes('"error":')?s.error=n.content:s.content=n.content),t.splice(e,1),e--}}return t}fromStandard(t){return t.reduce((e,n)=>(n.role==="tool"?e.push({role:"assistant",content:null,tool_calls:[{id:n.id,type:"function",function:{name:n.name,arguments:JSON.stringify(n.args)}}],refusal:null,annotations:[]},{role:"tool",tool_call_id:n.id,content:n.error||n.content}):e.push(n),e),[])}ask(t,e={}){const n=new AbortController,s=new Promise(async(i,g)=>{let c=this.fromStandard([...e.history||[],{role:"user",content:t}]);e.compress&&(c=await this.ai.llm.compress(c,e.compress.max,e.compress.min,e));const d={model:e.model||this.model,messages:c,stream:!!e.stream,max_tokens:e.max_tokens||this.ai.options.max_tokens||4096,temperature:e.temperature||this.ai.options.temperature||.7,tools:(e.tools||this.ai.options.tools||[]).map(o=>({type:"function",function:{name:o.name,description:o.description,parameters:{type:"object",properties:o.args?p.objectMap(o.args,(r,a)=>({...a,required:void 0})):{},required:o.args?Object.entries(o.args).filter(r=>r[1].required).map(r=>r[0]):[]}}}))};let m;do{if(m=await this.client.chat.completions.create(d),e.stream){m.choices=[];for await(const r of m){if(n.signal.aborted)break;r.choices[0].delta.content&&e.stream({text:r.choices[0].delta.content})}}const o=m.choices[0].message.tool_calls||[];if(o.length&&!n.signal.aborted){c.push(m.choices[0].message);const r=await Promise.all(o.map(async a=>{const f=e.tools?.find(p.findByProp("name",a.function.name));if(!f)return{role:"tool",tool_call_id:a.id,content:'{"error": "Tool not found"}'};try{const h=p.JSONAttemptParse(a.function.arguments,{}),y=await f.fn(h,this.ai);return{role:"tool",tool_call_id:a.id,content:p.JSONSanitize(y)}}catch(h){return{role:"tool",tool_call_id:a.id,content:p.JSONSanitize({error:h?.message||h?.toString()||"Unknown"})}}}));c.push(...r),d.messages=c}}while(!n.signal.aborted&&m.choices?.[0]?.message?.tool_calls?.length);e.stream&&e.stream({done:!0}),i(this.toStandard([...c,{role:"assistant",content:m.choices[0].message.content||""}]))});return Object.assign(s,{abort:()=>n.abort()})}}class x{constructor(t,e){this.ai=t,this.options=e,e.anthropic?.token&&(this.providers.anthropic=new j(this.ai,e.anthropic.token,e.anthropic.model)),e.ollama?.host&&(this.providers.ollama=new J(this.ai,e.ollama.host,e.ollama.model)),e.openAi?.token&&(this.providers.openAi=new N(this.ai,e.openAi.token,e.openAi.model))}providers={};ask(t,e={}){let n=[null,null];if(e.model&&(typeof e.model=="object"?n=e.model:n=[e.model,this.options[e.model]?.model]),(!e.model||n[1]==null)&&(typeof this.options.model=="object"?n=this.options.model:n=[this.options.model,this.options[this.options.model]?.model]),!n[0]||!n[1])throw new Error(`Unknown LLM provider or model: ${n[0]} / ${n[1]}`);return this.providers[n[0]].ask(t,{...e,model:n[1]})}async compress(t,e,n,s){if(this.estimateTokens(t)<e)return t;let i=0,g=0;for(let o of t.toReversed())if(g+=this.estimateTokens(o.content),g<n)i++;else break;if(t.length<=i)return t;const c=i==0?[]:t.slice(-i),d=(i==0?t:t.slice(0,-i)).filter(o=>o.role==="assistant"||o.role==="user");return[{role:"assistant",content:`Conversation Summary: ${await this.summarize(d.map(o=>`${o.role}: ${o.content}`).join(`
|
|
6
|
+
|
|
7
|
+
`),250,s)}`},...c]}estimateTokens(t){const e=JSON.stringify(t);return Math.ceil(e.length/4*1.2)}async json(t,e){let n=await this.ask(t,{system:"Respond using a JSON blob",...e});return n?.[0]?.content?p.JSONAttemptParse(new RegExp("{[sS]*}").exec(n[0].content),{}):{}}summarize(t,e,n){return this.ask(t,{system:`Generate a brief summary <= ${e} tokens. Output nothing else`,temperature:.3,...n}).then(s=>s.pop()?.content||null)}}class z{constructor(t){this.options=t,this.llm=new x(this,t),this.options.whisper?.binary&&(this.whisperModel=k.join(this.options.whisper?.path,this.options.whisper?.model+this.options.whisper?.model.endsWith(".bin")?"":".bin"),this.downloadAsrModel())}downloads={};whisperModel;llm;async asr(t,e){if(!this.options.whisper?.binary)throw new Error("Whisper not configured");e||(e=this.options.whisper.model),await this.downloadAsrModel();const n=Math.random().toString(36).substring(2,10)+"-"+t.split("/").pop()+".txt",s=k.join(this.options.whisper.temp||"/tmp",n);return await w.$`rm -f ${s} && ${this.options.whisper.binary} -nt -np -m ${this.whisperModel} -f ${t} -otxt -of ${s}`,b.readFile(s,"utf-8").then(i=>i?.trim()||null).finally(()=>b.rm(s,{force:!0}).catch(()=>{}))}async downloadAsrModel(){if(!this.options.whisper?.binary)throw new Error("Whisper not configured");if(await b.stat(this.whisperModel).then(()=>!0).catch(()=>!1))return;const t=this.whisperModel.split("/").at(-1);return this.downloads[t]?this.downloads[t]:(this.downloads[t]=fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${t}`).then(e=>e.arrayBuffer()).then(e=>Buffer.from(e)).then(async e=>{await b.writeFile(this.whisperModel,e),delete this.downloads[t]}),this.downloads[t])}ocr(t){let e;return{abort:()=>{e?.terminate()},response:new Promise(async n=>{e=await P.createWorker("eng");const{data:s}=await e.recognize(t);await e.terminate(),n(s.text.trim()||null)})}}semanticSimilarity(t,...e){if(e.length<2)throw new Error("Requires at least 2 strings to compare");const n=(c,d=10)=>c.toLowerCase().split("").map((m,o)=>m.charCodeAt(0)*(o+1)%d/d).slice(0,d),s=(c,d)=>{if(c.length!==d.length)throw new Error("Vectors must be same length");const m=_.tensor1d(c),o=_.tensor1d(d),r=_.dot(m,o),a=_.norm(m),f=_.norm(o);return a.dataSync()[0]===0||f.dataSync()[0]===0?0:r.dataSync()[0]/(a.dataSync()[0]*f.dataSync()[0])},i=n(t),g=e.map(c=>n(c)).map(c=>s(i,c));return{avg:g.reduce((c,d)=>c+d,0)/g.length,max:Math.max(...g),similarities:g}}}const T={name:"cli",description:"Use the command line interface, returns any output",args:{command:{type:"string",description:"Command to run",required:!0}},fn:l=>w.$`${l.command}`},L={name:"get_datetime",description:"Get current date and time",args:{},fn:async()=>new Date().toISOString()},U={name:"exec",description:"Run code/scripts",args:{language:{type:"string",description:"Execution language",enum:["cli","node","python"],required:!0},code:{type:"string",description:"Code to execute",required:!0}},fn:async(l,t)=>{try{switch(l.type){case"bash":return await T.fn({command:l.code},t);case"node":return await q.fn({code:l.code},t);case"python":return await O.fn({code:l.code},t)}}catch(e){return{error:e?.message||e.toString()}}}},R={name:"fetch",description:"Make HTTP request to URL",args:{url:{type:"string",description:"URL to fetch",required:!0},method:{type:"string",description:"HTTP method to use",enum:["GET","POST","PUT","DELETE"],default:"GET"},headers:{type:"object",description:"HTTP headers to send",default:{}},body:{type:"object",description:"HTTP body to send"}},fn:l=>new p.Http({url:l.url,headers:l.headers}).request({method:l.method||"GET",body:l.body})},q={name:"exec_javascript",description:"Execute commonjs javascript",args:{code:{type:"string",description:"CommonJS javascript",required:!0}},fn:async l=>{const t=p.consoleInterceptor(null),e=await p.fn({console:t},l.code,!0).catch(n=>t.output.error.push(n));return{...t.output,return:e,stdout:void 0,stderr:void 0}}},O={name:"exec_javascript",description:"Execute commonjs javascript",args:{code:{type:"string",description:"CommonJS javascript",required:!0}},fn:async l=>({result:w.$Sync`python -c "${l.code}"`})},I={name:"search",description:"Use a search engine to find relevant URLs, should be changed with fetch to scrape sources",args:{query:{type:"string",description:"Search string",required:!0},length:{type:"string",description:"Number of results to return",default:5}},fn:async l=>{const t=await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(l.query)}`,{headers:{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)","Accept-Language":"en-US,en;q=0.9"}}).then(i=>i.text());let e,n=/<a .*?href="(.+?)".+?<\/a>/g;const s=new p.ASet;for(;(e=n.exec(t))!==null;){let i=/uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];if(i&&(i=decodeURIComponent(i)),i&&s.add(i),s.size>=(l.length||5))break}return s}};u.Ai=z,u.Anthropic=j,u.CliTool=T,u.DateTimeTool=L,u.ExecTool=U,u.FetchTool=R,u.JSTool=q,u.LLM=x,u.PythonTool=O,u.SearchTool=I,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|
|
648
8
|
//# sourceMappingURL=index.js.map
|