@ztimson/ai-utils 0.1.21 → 0.2.0
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 +8 -39
- package/dist/audio.d.ts +24 -0
- package/dist/index.js +11 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +240 -204
- package/dist/index.mjs.map +1 -1
- package/dist/llm.d.ts +20 -3
- package/dist/vision.d.ts +14 -0
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { deepCopy as
|
|
3
|
-
import { Anthropic as
|
|
4
|
-
import { Ollama as
|
|
5
|
-
import { OpenAI as
|
|
6
|
-
import
|
|
1
|
+
import { pipeline as T } from "@xenova/transformers";
|
|
2
|
+
import { deepCopy as j, objectMap as b, JSONAttemptParse as _, findByProp as k, JSONSanitize as y, Http as M, consoleInterceptor as A, fn as q, ASet as v } from "@ztimson/utils";
|
|
3
|
+
import { Anthropic as P } from "@anthropic-ai/sdk";
|
|
4
|
+
import { Ollama as E } from "ollama";
|
|
5
|
+
import { OpenAI as $ } from "openai";
|
|
6
|
+
import * as w from "@tensorflow/tfjs";
|
|
7
|
+
import { spawn as O } from "node:child_process";
|
|
8
|
+
import S from "node:fs/promises";
|
|
7
9
|
import U from "node:path";
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
class S {
|
|
10
|
+
import { createWorker as L } from "tesseract.js";
|
|
11
|
+
import { $ as R, $Sync as D } from "@ztimson/node-utils";
|
|
12
|
+
class x {
|
|
12
13
|
}
|
|
13
|
-
class
|
|
14
|
+
class z extends x {
|
|
14
15
|
constructor(t, e, n) {
|
|
15
|
-
super(), this.ai = t, this.apiToken = e, this.model = n, this.client = new
|
|
16
|
+
super(), this.ai = t, this.apiToken = e, this.model = n, this.client = new P({ apiKey: e });
|
|
16
17
|
}
|
|
17
18
|
client;
|
|
18
19
|
toStandard(t) {
|
|
@@ -21,8 +22,8 @@ class I extends S {
|
|
|
21
22
|
typeof t[n].content != "string" && (t[n].role == "assistant" ? t[n].content.filter((a) => a.type == "tool_use").forEach((a) => {
|
|
22
23
|
e++, t.splice(e, 0, { role: "tool", id: a.id, name: a.name, args: a.input, timestamp: Date.now() });
|
|
23
24
|
}) : t[n].role == "user" && t[n].content.filter((a) => a.type == "tool_result").forEach((a) => {
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const c = t.find((f) => f.id == a.tool_use_id);
|
|
26
|
+
c[a.is_error ? "error" : "content"] = a.content;
|
|
26
27
|
}), t[n].content = t[n].content.filter((a) => a.type == "text").map((a) => a.text).join(`
|
|
27
28
|
|
|
28
29
|
`)), t[n].timestamp || (t[n].timestamp = Date.now());
|
|
@@ -43,81 +44,76 @@ class I extends S {
|
|
|
43
44
|
return t.map(({ timestamp: e, ...n }) => n);
|
|
44
45
|
}
|
|
45
46
|
ask(t, e = {}) {
|
|
46
|
-
const n = new AbortController(), a = new Promise(async (
|
|
47
|
-
let
|
|
48
|
-
const l =
|
|
49
|
-
e.compress && (
|
|
50
|
-
const
|
|
47
|
+
const n = new AbortController(), a = new Promise(async (c, f) => {
|
|
48
|
+
let i = this.fromStandard([...e.history || [], { role: "user", content: t, timestamp: Date.now() }]);
|
|
49
|
+
const l = j(i);
|
|
50
|
+
e.compress && (i = await this.ai.language.compressHistory(i, e.compress.max, e.compress.min, e));
|
|
51
|
+
const m = {
|
|
51
52
|
model: e.model || this.model,
|
|
52
53
|
max_tokens: e.max_tokens || this.ai.options.max_tokens || 4096,
|
|
53
54
|
system: e.system || this.ai.options.system || "",
|
|
54
55
|
temperature: e.temperature || this.ai.options.temperature || 0.7,
|
|
55
|
-
tools: (e.tools || this.ai.options.tools || []).map((
|
|
56
|
-
name:
|
|
57
|
-
description:
|
|
56
|
+
tools: (e.tools || this.ai.options.tools || []).map((s) => ({
|
|
57
|
+
name: s.name,
|
|
58
|
+
description: s.description,
|
|
58
59
|
input_schema: {
|
|
59
60
|
type: "object",
|
|
60
|
-
properties:
|
|
61
|
-
required:
|
|
61
|
+
properties: s.args ? b(s.args, (r, u) => ({ ...u, required: void 0 })) : {},
|
|
62
|
+
required: s.args ? Object.entries(s.args).filter((r) => r[1].required).map((r) => r[0]) : []
|
|
62
63
|
},
|
|
63
64
|
fn: void 0
|
|
64
65
|
})),
|
|
65
|
-
messages:
|
|
66
|
+
messages: i,
|
|
66
67
|
stream: !!e.stream
|
|
67
68
|
};
|
|
68
|
-
let o;
|
|
69
|
-
const h = [];
|
|
69
|
+
let o, d = !0;
|
|
70
70
|
do {
|
|
71
|
-
if (o = await this.client.messages.create(
|
|
72
|
-
|
|
71
|
+
if (o = await this.client.messages.create(m), e.stream) {
|
|
72
|
+
d ? d = !1 : e.stream({ text: `
|
|
73
73
|
|
|
74
74
|
` }), o.content = [];
|
|
75
|
-
for await (const
|
|
75
|
+
for await (const r of o) {
|
|
76
76
|
if (n.signal.aborted) break;
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
else if (
|
|
80
|
-
if (
|
|
81
|
-
const
|
|
82
|
-
o.content.at(-1).text +=
|
|
83
|
-
} else
|
|
84
|
-
else if (
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
} else if (
|
|
77
|
+
if (r.type === "content_block_start")
|
|
78
|
+
r.content_block.type === "text" ? o.content.push({ type: "text", text: "" }) : r.content_block.type === "tool_use" && o.content.push({ type: "tool_use", id: r.content_block.id, name: r.content_block.name, input: "" });
|
|
79
|
+
else if (r.type === "content_block_delta")
|
|
80
|
+
if (r.delta.type === "text_delta") {
|
|
81
|
+
const u = r.delta.text;
|
|
82
|
+
o.content.at(-1).text += u, e.stream({ text: u });
|
|
83
|
+
} else r.delta.type === "input_json_delta" && (o.content.at(-1).input += r.delta.partial_json);
|
|
84
|
+
else if (r.type === "content_block_stop") {
|
|
85
|
+
const u = o.content.at(-1);
|
|
86
|
+
u.input != null && (u.input = u.input ? _(u.input, {}) : {});
|
|
87
|
+
} else if (r.type === "message_stop")
|
|
88
88
|
break;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
c.push({ role: "assistant", content: o.content }), l.push({ role: "assistant", content: o.content });
|
|
98
|
-
const s = await Promise.all(r.map(async (p) => {
|
|
99
|
-
const g = e.tools?.find(x("name", p.name));
|
|
100
|
-
if (!g) return { tool_use_id: p.id, is_error: !0, content: "Tool not found" };
|
|
91
|
+
const s = o.content.filter((r) => r.type === "tool_use");
|
|
92
|
+
if (s.length && !n.signal.aborted) {
|
|
93
|
+
i.push({ role: "assistant", content: o.content }), l.push({ role: "assistant", content: o.content });
|
|
94
|
+
const r = await Promise.all(s.map(async (u) => {
|
|
95
|
+
const h = e.tools?.find(k("name", u.name));
|
|
96
|
+
if (!h) return { tool_use_id: u.id, is_error: !0, content: "Tool not found" };
|
|
101
97
|
try {
|
|
102
|
-
const
|
|
103
|
-
return { type: "tool_result", tool_use_id:
|
|
104
|
-
} catch (
|
|
105
|
-
return { type: "tool_result", tool_use_id:
|
|
98
|
+
const g = await h.fn(u.input, this.ai);
|
|
99
|
+
return { type: "tool_result", tool_use_id: u.id, content: y(g) };
|
|
100
|
+
} catch (g) {
|
|
101
|
+
return { type: "tool_result", tool_use_id: u.id, is_error: !0, content: g?.message || g?.toString() || "Unknown" };
|
|
106
102
|
}
|
|
107
103
|
}));
|
|
108
|
-
|
|
104
|
+
i.push({ role: "user", content: r }), m.messages = i;
|
|
109
105
|
}
|
|
110
|
-
} while (!n.signal.aborted && o.content.some((
|
|
111
|
-
e.stream && e.stream({ done: !0 }),
|
|
106
|
+
} while (!n.signal.aborted && o.content.some((s) => s.type === "tool_use"));
|
|
107
|
+
e.stream && e.stream({ done: !0 }), c(this.toStandard([...i, { role: "assistant", content: o.content.filter((s) => s.type == "text").map((s) => s.text).join(`
|
|
112
108
|
|
|
113
|
-
`)
|
|
109
|
+
`) }]));
|
|
114
110
|
});
|
|
115
111
|
return Object.assign(a, { abort: () => n.abort() });
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
|
-
class
|
|
114
|
+
class H extends x {
|
|
119
115
|
constructor(t, e, n) {
|
|
120
|
-
super(), this.ai = t, this.host = e, this.model = n, this.client = new
|
|
116
|
+
super(), this.ai = t, this.host = e, this.model = n, this.client = new E({ host: e });
|
|
121
117
|
}
|
|
122
118
|
client;
|
|
123
119
|
toStandard(t) {
|
|
@@ -139,10 +135,10 @@ class J extends S {
|
|
|
139
135
|
});
|
|
140
136
|
}
|
|
141
137
|
ask(t, e = {}) {
|
|
142
|
-
const n = new AbortController(), a = new Promise(async (
|
|
143
|
-
let
|
|
144
|
-
l[0].roll == "system" && (
|
|
145
|
-
const
|
|
138
|
+
const n = new AbortController(), a = new Promise(async (c, f) => {
|
|
139
|
+
let i = e.system || this.ai.options.system, l = this.fromStandard([...e.history || [], { role: "user", content: t, timestamp: Date.now() }]);
|
|
140
|
+
l[0].roll == "system" && (i ? l.shift() : i = l.shift()), e.compress && (l = await this.ai.language.compressHistory(l, e.compress.max, e.compress.min)), e.system && l.unshift({ role: "system", content: i });
|
|
141
|
+
const m = {
|
|
146
142
|
model: e.model || this.model,
|
|
147
143
|
messages: l,
|
|
148
144
|
stream: !!e.stream,
|
|
@@ -151,72 +147,68 @@ class J extends S {
|
|
|
151
147
|
temperature: e.temperature || this.ai.options.temperature || 0.7,
|
|
152
148
|
num_predict: e.max_tokens || this.ai.options.max_tokens || 4096
|
|
153
149
|
},
|
|
154
|
-
tools: (e.tools || this.ai.options.tools || []).map((
|
|
150
|
+
tools: (e.tools || this.ai.options.tools || []).map((s) => ({
|
|
155
151
|
type: "function",
|
|
156
152
|
function: {
|
|
157
|
-
name:
|
|
158
|
-
description:
|
|
153
|
+
name: s.name,
|
|
154
|
+
description: s.description,
|
|
159
155
|
parameters: {
|
|
160
156
|
type: "object",
|
|
161
|
-
properties:
|
|
162
|
-
required:
|
|
157
|
+
properties: s.args ? b(s.args, (r, u) => ({ ...u, required: void 0 })) : {},
|
|
158
|
+
required: s.args ? Object.entries(s.args).filter((r) => r[1].required).map((r) => r[0]) : []
|
|
163
159
|
}
|
|
164
160
|
}
|
|
165
161
|
}))
|
|
166
162
|
};
|
|
167
|
-
let o;
|
|
168
|
-
const h = [];
|
|
163
|
+
let o, d = !0;
|
|
169
164
|
do {
|
|
170
|
-
if (o = await this.client.chat(
|
|
171
|
-
|
|
165
|
+
if (o = await this.client.chat(m), e.stream) {
|
|
166
|
+
d ? d = !1 : e.stream({ text: `
|
|
172
167
|
|
|
173
168
|
` }), o.message = { role: "assistant", content: "", tool_calls: [] };
|
|
174
|
-
for await (const
|
|
175
|
-
if (n.signal.aborted || (
|
|
169
|
+
for await (const s of o)
|
|
170
|
+
if (n.signal.aborted || (s.message?.content && (o.message.content += s.message.content, e.stream({ text: s.message.content })), s.message?.tool_calls && (o.message.tool_calls = s.message.tool_calls), s.done)) break;
|
|
176
171
|
}
|
|
177
|
-
if (
|
|
172
|
+
if (o.message?.tool_calls?.length && !n.signal.aborted) {
|
|
178
173
|
l.push(o.message);
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
if (!
|
|
182
|
-
const
|
|
174
|
+
const s = await Promise.all(o.message.tool_calls.map(async (r) => {
|
|
175
|
+
const u = (e.tools || this.ai.options.tools)?.find(k("name", r.function.name));
|
|
176
|
+
if (!u) return { role: "tool", tool_name: r.function.name, content: '{"error": "Tool not found"}' };
|
|
177
|
+
const h = typeof r.function.arguments == "string" ? _(r.function.arguments, {}) : r.function.arguments;
|
|
183
178
|
try {
|
|
184
|
-
const
|
|
185
|
-
return { role: "tool", tool_name:
|
|
186
|
-
} catch (
|
|
187
|
-
return { role: "tool", tool_name:
|
|
179
|
+
const g = await u.fn(h, this.ai);
|
|
180
|
+
return { role: "tool", tool_name: r.function.name, args: h, content: y(g) };
|
|
181
|
+
} catch (g) {
|
|
182
|
+
return { role: "tool", tool_name: r.function.name, args: h, content: y({ error: g?.message || g?.toString() || "Unknown" }) };
|
|
188
183
|
}
|
|
189
184
|
}));
|
|
190
|
-
l.push(...
|
|
185
|
+
l.push(...s), m.messages = l;
|
|
191
186
|
}
|
|
192
187
|
} while (!n.signal.aborted && o.message?.tool_calls?.length);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`);
|
|
196
|
-
e.stream && e.stream({ done: !0 }), i(this.toStandard([...l, { role: "assistant", content: m, timestamp: Date.now() }]));
|
|
188
|
+
e.stream && e.stream({ done: !0 }), c(this.toStandard([...l, { role: "assistant", content: o.message?.content }]));
|
|
197
189
|
});
|
|
198
190
|
return Object.assign(a, { abort: () => n.abort() });
|
|
199
191
|
}
|
|
200
192
|
}
|
|
201
|
-
class
|
|
193
|
+
class N extends x {
|
|
202
194
|
constructor(t, e, n) {
|
|
203
|
-
super(), this.ai = t, this.apiToken = e, this.model = n, this.client = new
|
|
195
|
+
super(), this.ai = t, this.apiToken = e, this.model = n, this.client = new $({ apiKey: e });
|
|
204
196
|
}
|
|
205
197
|
client;
|
|
206
198
|
toStandard(t) {
|
|
207
199
|
for (let e = 0; e < t.length; e++) {
|
|
208
200
|
const n = t[e];
|
|
209
201
|
if (n.role === "assistant" && n.tool_calls) {
|
|
210
|
-
const a = n.tool_calls.map((
|
|
202
|
+
const a = n.tool_calls.map((c) => ({
|
|
211
203
|
role: "tool",
|
|
212
|
-
id:
|
|
213
|
-
name:
|
|
214
|
-
args:
|
|
204
|
+
id: c.id,
|
|
205
|
+
name: c.function.name,
|
|
206
|
+
args: _(c.function.arguments, {}),
|
|
215
207
|
timestamp: n.timestamp
|
|
216
208
|
}));
|
|
217
209
|
t.splice(e, 1, ...a), e += a.length - 1;
|
|
218
210
|
} else if (n.role === "tool" && n.content) {
|
|
219
|
-
const a = t.find((
|
|
211
|
+
const a = t.find((c) => n.tool_call_id == c.id);
|
|
220
212
|
a && (n.content.includes('"error":') ? a.error = n.content : a.content = n.content), t.splice(e, 1), e--;
|
|
221
213
|
}
|
|
222
214
|
t[e]?.timestamp || (t[e].timestamp = Date.now());
|
|
@@ -238,76 +230,72 @@ class W extends S {
|
|
|
238
230
|
content: n.error || n.content
|
|
239
231
|
});
|
|
240
232
|
else {
|
|
241
|
-
const { timestamp: a, ...
|
|
242
|
-
e.push(
|
|
233
|
+
const { timestamp: a, ...c } = n;
|
|
234
|
+
e.push(c);
|
|
243
235
|
}
|
|
244
236
|
return e;
|
|
245
237
|
}, []);
|
|
246
238
|
}
|
|
247
239
|
ask(t, e = {}) {
|
|
248
|
-
const n = new AbortController(), a = new Promise(async (
|
|
249
|
-
let
|
|
250
|
-
e.compress && (
|
|
240
|
+
const n = new AbortController(), a = new Promise(async (c, f) => {
|
|
241
|
+
let i = this.fromStandard([...e.history || [], { role: "user", content: t, timestamp: Date.now() }]);
|
|
242
|
+
e.compress && (i = await this.ai.language.compressHistory(i, e.compress.max, e.compress.min, e));
|
|
251
243
|
const l = {
|
|
252
244
|
model: e.model || this.model,
|
|
253
|
-
messages:
|
|
245
|
+
messages: i,
|
|
254
246
|
stream: !!e.stream,
|
|
255
247
|
max_tokens: e.max_tokens || this.ai.options.max_tokens || 4096,
|
|
256
248
|
temperature: e.temperature || this.ai.options.temperature || 0.7,
|
|
257
|
-
tools: (e.tools || this.ai.options.tools || []).map((
|
|
249
|
+
tools: (e.tools || this.ai.options.tools || []).map((d) => ({
|
|
258
250
|
type: "function",
|
|
259
251
|
function: {
|
|
260
|
-
name:
|
|
261
|
-
description:
|
|
252
|
+
name: d.name,
|
|
253
|
+
description: d.description,
|
|
262
254
|
parameters: {
|
|
263
255
|
type: "object",
|
|
264
|
-
properties:
|
|
265
|
-
required:
|
|
256
|
+
properties: d.args ? b(d.args, (s, r) => ({ ...r, required: void 0 })) : {},
|
|
257
|
+
required: d.args ? Object.entries(d.args).filter((s) => s[1].required).map((s) => s[0]) : []
|
|
266
258
|
}
|
|
267
259
|
}
|
|
268
260
|
}))
|
|
269
261
|
};
|
|
270
|
-
let
|
|
271
|
-
const o = [];
|
|
262
|
+
let m, o = !0;
|
|
272
263
|
do {
|
|
273
|
-
if (
|
|
274
|
-
o
|
|
264
|
+
if (m = await this.client.chat.completions.create(l), e.stream) {
|
|
265
|
+
o ? o = !1 : e.stream({ text: `
|
|
275
266
|
|
|
276
|
-
` }),
|
|
277
|
-
for await (const
|
|
267
|
+
` }), m.choices = [{ message: { content: "", tool_calls: [] } }];
|
|
268
|
+
for await (const s of m) {
|
|
278
269
|
if (n.signal.aborted) break;
|
|
279
|
-
|
|
270
|
+
s.choices[0].delta.content && (m.choices[0].message.content += s.choices[0].delta.content, e.stream({ text: s.choices[0].delta.content })), s.choices[0].delta.tool_calls && (m.choices[0].message.tool_calls = s.choices[0].delta.tool_calls);
|
|
280
271
|
}
|
|
281
272
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (!p) return { role: "tool", tool_call_id: s.id, content: '{"error": "Tool not found"}' };
|
|
273
|
+
const d = m.choices[0].message.tool_calls || [];
|
|
274
|
+
if (d.length && !n.signal.aborted) {
|
|
275
|
+
i.push(m.choices[0].message);
|
|
276
|
+
const s = await Promise.all(d.map(async (r) => {
|
|
277
|
+
const u = e.tools?.find(k("name", r.function.name));
|
|
278
|
+
if (!u) return { role: "tool", tool_call_id: r.id, content: '{"error": "Tool not found"}' };
|
|
289
279
|
try {
|
|
290
|
-
const
|
|
291
|
-
return { role: "tool", tool_call_id:
|
|
292
|
-
} catch (
|
|
293
|
-
return { role: "tool", tool_call_id:
|
|
280
|
+
const h = _(r.function.arguments, {}), g = await u.fn(h, this.ai);
|
|
281
|
+
return { role: "tool", tool_call_id: r.id, content: y(g) };
|
|
282
|
+
} catch (h) {
|
|
283
|
+
return { role: "tool", tool_call_id: r.id, content: y({ error: h?.message || h?.toString() || "Unknown" }) };
|
|
294
284
|
}
|
|
295
285
|
}));
|
|
296
|
-
|
|
286
|
+
i.push(...s), l.messages = i;
|
|
297
287
|
}
|
|
298
|
-
} while (!n.signal.aborted &&
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
`);
|
|
302
|
-
e.stream && e.stream({ done: !0 }), i(this.toStandard([...c, { role: "assistant", content: h, timestamp: Date.now() }]));
|
|
288
|
+
} while (!n.signal.aborted && m.choices?.[0]?.message?.tool_calls?.length);
|
|
289
|
+
e.stream && e.stream({ done: !0 }), c(this.toStandard([...i, { role: "assistant", content: m.choices[0].message.content || "" }]));
|
|
303
290
|
});
|
|
304
291
|
return Object.assign(a, { abort: () => n.abort() });
|
|
305
292
|
}
|
|
306
293
|
}
|
|
307
|
-
class
|
|
308
|
-
constructor(t
|
|
309
|
-
this.ai = t, this.
|
|
294
|
+
class I {
|
|
295
|
+
constructor(t) {
|
|
296
|
+
this.ai = t, this.embedModel = T("feature-extraction", "Xenova/all-MiniLM-L6-v2"), t.options.anthropic?.token && (this.providers.anthropic = new z(this.ai, t.options.anthropic.token, t.options.anthropic.model)), t.options.ollama?.host && (this.providers.ollama = new H(this.ai, t.options.ollama.host, t.options.ollama.model)), t.options.openAi?.token && (this.providers.openAi = new N(this.ai, t.options.openAi.token, t.options.openAi.model));
|
|
310
297
|
}
|
|
298
|
+
embedModel;
|
|
311
299
|
providers = {};
|
|
312
300
|
/**
|
|
313
301
|
* Chat with LLM
|
|
@@ -317,7 +305,7 @@ class N {
|
|
|
317
305
|
*/
|
|
318
306
|
ask(t, e = {}) {
|
|
319
307
|
let n = [null, null];
|
|
320
|
-
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]}`);
|
|
308
|
+
if (e.model && (typeof e.model == "object" ? n = e.model : n = [e.model, this.ai.options[e.model]?.model]), (!e.model || n[1] == null) && (typeof this.ai.options.model == "object" ? n = this.ai.options.model : n = [this.ai.options.model, this.ai.options[this.ai.options.model]?.model]), !n[0] || !n[1]) throw new Error(`Unknown LLM provider or model: ${n[0]} / ${n[1]}`);
|
|
321
309
|
return this.providers[n[0]].ask(t, { ...e, model: n[1] });
|
|
322
310
|
}
|
|
323
311
|
/**
|
|
@@ -328,17 +316,49 @@ class N {
|
|
|
328
316
|
* @param {LLMRequest} options LLM options
|
|
329
317
|
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
330
318
|
*/
|
|
331
|
-
async
|
|
319
|
+
async compressHistory(t, e, n, a) {
|
|
332
320
|
if (this.estimateTokens(t) < e) return t;
|
|
333
|
-
let
|
|
321
|
+
let c = 0, f = 0;
|
|
334
322
|
for (let o of t.toReversed())
|
|
335
|
-
if (f += this.estimateTokens(o.content), f < n)
|
|
323
|
+
if (f += this.estimateTokens(o.content), f < n) c++;
|
|
336
324
|
else break;
|
|
337
|
-
if (t.length <=
|
|
338
|
-
const
|
|
325
|
+
if (t.length <= c) return t;
|
|
326
|
+
const i = c == 0 ? [] : t.slice(-c), l = (c == 0 ? t : t.slice(0, -c)).filter((o) => o.role === "assistant" || o.role === "user");
|
|
339
327
|
return [{ role: "assistant", content: `Conversation Summary: ${await this.summarize(l.map((o) => `${o.role}: ${o.content}`).join(`
|
|
340
328
|
|
|
341
|
-
`), 250, a)}`, timestamp: Date.now() }, ...
|
|
329
|
+
`), 250, a)}`, timestamp: Date.now() }, ...i];
|
|
330
|
+
}
|
|
331
|
+
embedding(t, e = 500, n = 50) {
|
|
332
|
+
const a = (o, d = "") => o == null ? [] : Object.entries(o).flatMap(([s, r]) => {
|
|
333
|
+
const u = d ? `${d}${isNaN(+s) ? `.${s}` : `[${s}]`}` : s;
|
|
334
|
+
if (typeof r == "object" && r !== null && !Array.isArray(r)) return a(r, u);
|
|
335
|
+
const h = Array.isArray(r) ? r.join(", ") : String(r);
|
|
336
|
+
return `${u}: ${h}`;
|
|
337
|
+
}), c = async (o) => {
|
|
338
|
+
const s = await (await this.embedModel)(o, { pooling: "mean", normalize: !0 });
|
|
339
|
+
return Array.from(s.data);
|
|
340
|
+
}, i = (typeof t == "object" ? a(t) : t.split(`
|
|
341
|
+
`)).flatMap((o) => [...o.split(/\s+/).filter((d) => d.trim()), `
|
|
342
|
+
`]), l = [];
|
|
343
|
+
let m = 0;
|
|
344
|
+
for (; m < i.length; ) {
|
|
345
|
+
let o = m, d = "";
|
|
346
|
+
for (; o < i.length; ) {
|
|
347
|
+
const r = i[o], u = d + (d ? " " : "") + r;
|
|
348
|
+
if (this.estimateTokens(u.replace(/\s*\n\s*/g, `
|
|
349
|
+
`)) > e && d) break;
|
|
350
|
+
d = u, o++;
|
|
351
|
+
}
|
|
352
|
+
const s = d.replace(/\s*\n\s*/g, `
|
|
353
|
+
`).trim();
|
|
354
|
+
s && l.push(s), m = o - n, m <= o - i.length + o && (m = o);
|
|
355
|
+
}
|
|
356
|
+
return Promise.all(l.map(async (o, d) => ({
|
|
357
|
+
index: d,
|
|
358
|
+
embedding: await c(o),
|
|
359
|
+
text: o,
|
|
360
|
+
tokens: this.estimateTokens(o)
|
|
361
|
+
})));
|
|
342
362
|
}
|
|
343
363
|
/**
|
|
344
364
|
* Estimate variable as tokens
|
|
@@ -349,6 +369,21 @@ class N {
|
|
|
349
369
|
const e = JSON.stringify(t);
|
|
350
370
|
return Math.ceil(e.length / 4 * 1.2);
|
|
351
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Compare the difference between two strings using tensor math
|
|
374
|
+
* @param target Text that will checked
|
|
375
|
+
* @param {string} searchTerms Multiple search terms to check against target
|
|
376
|
+
* @returns {{avg: number, max: number, similarities: number[]}} Similarity values 0-1: 0 = unique, 1 = identical
|
|
377
|
+
*/
|
|
378
|
+
fuzzyMatch(t, ...e) {
|
|
379
|
+
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
380
|
+
const n = (i, l = 10) => i.toLowerCase().split("").map((m, o) => m.charCodeAt(0) * (o + 1) % l / l).slice(0, l), a = (i, l) => {
|
|
381
|
+
if (i.length !== l.length) throw new Error("Vectors must be same length");
|
|
382
|
+
const m = w.tensor1d(i), o = w.tensor1d(l), d = w.dot(m, o), s = w.norm(m), r = w.norm(o);
|
|
383
|
+
return s.dataSync()[0] === 0 || r.dataSync()[0] === 0 ? 0 : d.dataSync()[0] / (s.dataSync()[0] * r.dataSync()[0]);
|
|
384
|
+
}, c = n(t), f = e.map((i) => n(i)).map((i) => a(c, i));
|
|
385
|
+
return { avg: f.reduce((i, l) => i + l, 0) / f.length, max: Math.max(...f), similarities: f };
|
|
386
|
+
}
|
|
352
387
|
/**
|
|
353
388
|
* Ask a question with JSON response
|
|
354
389
|
* @param {string} message Question
|
|
@@ -360,7 +395,7 @@ class N {
|
|
|
360
395
|
system: "Respond using a JSON blob",
|
|
361
396
|
...e
|
|
362
397
|
});
|
|
363
|
-
return n?.[0]?.content ?
|
|
398
|
+
return n?.[0]?.content ? _(new RegExp("{[sS]*}").exec(n[0].content), {}) : {};
|
|
364
399
|
}
|
|
365
400
|
/**
|
|
366
401
|
* Create a summary of some text
|
|
@@ -373,14 +408,12 @@ class N {
|
|
|
373
408
|
return this.ask(t, { system: `Generate a brief summary <= ${e} tokens. Output nothing else`, temperature: 0.3, ...n }).then((a) => a.pop()?.content || null);
|
|
374
409
|
}
|
|
375
410
|
}
|
|
376
|
-
class
|
|
411
|
+
class J {
|
|
377
412
|
constructor(t) {
|
|
378
|
-
this.
|
|
413
|
+
this.ai = t, t.options.whisper?.binary && (this.whisperModel = t.options.whisper?.model.endsWith(".bin") ? t.options.whisper?.model : t.options.whisper?.model + ".bin", this.downloadAsrModel());
|
|
379
414
|
}
|
|
380
415
|
downloads = {};
|
|
381
416
|
whisperModel;
|
|
382
|
-
/** Large Language Models */
|
|
383
|
-
llm;
|
|
384
417
|
/**
|
|
385
418
|
* Convert audio to text using Auditory Speech Recognition
|
|
386
419
|
* @param {string} path Path to audio
|
|
@@ -388,15 +421,15 @@ class ee {
|
|
|
388
421
|
* @returns {Promise<any>} Extracted text
|
|
389
422
|
*/
|
|
390
423
|
asr(t, e = this.whisperModel) {
|
|
391
|
-
if (!this.options.whisper?.binary) throw new Error("Whisper not configured");
|
|
424
|
+
if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
|
|
392
425
|
let n = () => {
|
|
393
426
|
};
|
|
394
|
-
return { response: new Promise((
|
|
395
|
-
this.downloadAsrModel(e).then((
|
|
427
|
+
return { response: new Promise((c, f) => {
|
|
428
|
+
this.downloadAsrModel(e).then((i) => {
|
|
396
429
|
let l = "";
|
|
397
|
-
const
|
|
398
|
-
n = () =>
|
|
399
|
-
o === 0 ?
|
|
430
|
+
const m = O(this.ai.options.whisper?.binary, ["-nt", "-np", "-m", i, "-f", t], { stdio: ["ignore", "pipe", "ignore"] });
|
|
431
|
+
n = () => m.kill("SIGTERM"), m.on("error", (o) => f(o)), m.stdout.on("data", (o) => l += o.toString()), m.on("close", (o) => {
|
|
432
|
+
o === 0 ? c(l.trim() || null) : f(new Error(`Exit code ${o}`));
|
|
400
433
|
});
|
|
401
434
|
});
|
|
402
435
|
}), abort: n };
|
|
@@ -408,10 +441,15 @@ class ee {
|
|
|
408
441
|
* @return {Promise<string>} Absolute path to model file, resolves once downloaded
|
|
409
442
|
*/
|
|
410
443
|
async downloadAsrModel(t = this.whisperModel) {
|
|
411
|
-
if (!this.options.whisper?.binary) throw new Error("Whisper not configured");
|
|
444
|
+
if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
|
|
412
445
|
t.endsWith(".bin") || (t += ".bin");
|
|
413
|
-
const e = U.join(this.options.whisper.path, t);
|
|
414
|
-
return await
|
|
446
|
+
const e = U.join(this.ai.options.whisper.path, t);
|
|
447
|
+
return await S.stat(e).then(() => !0).catch(() => !1) ? e : this.downloads[t] ? this.downloads[t] : (this.downloads[t] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${t}`).then((n) => n.arrayBuffer()).then((n) => Buffer.from(n)).then(async (n) => (await S.writeFile(e, n), delete this.downloads[t], e)), this.downloads[t]);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
class W {
|
|
451
|
+
constructor(t) {
|
|
452
|
+
this.ai = t;
|
|
415
453
|
}
|
|
416
454
|
/**
|
|
417
455
|
* Convert image to text using Optical Character Recognition
|
|
@@ -425,60 +463,58 @@ class ee {
|
|
|
425
463
|
e?.terminate();
|
|
426
464
|
},
|
|
427
465
|
response: new Promise(async (n) => {
|
|
428
|
-
e = await
|
|
466
|
+
e = await L("eng");
|
|
429
467
|
const { data: a } = await e.recognize(t);
|
|
430
468
|
await e.terminate(), n(a.text.trim() || null);
|
|
431
469
|
})
|
|
432
470
|
};
|
|
433
471
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
* @returns {{avg: number, max: number, similarities: number[]}} Similarity values 0-1: 0 = unique, 1 = identical
|
|
439
|
-
*/
|
|
440
|
-
semanticSimilarity(t, ...e) {
|
|
441
|
-
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
442
|
-
const n = (c, l = 10) => c.toLowerCase().split("").map((d, o) => d.charCodeAt(0) * (o + 1) % l / l).slice(0, l), a = (c, l) => {
|
|
443
|
-
if (c.length !== l.length) throw new Error("Vectors must be same length");
|
|
444
|
-
const d = _.tensor1d(c), o = _.tensor1d(l), h = _.dot(d, o), m = _.norm(d), r = _.norm(o);
|
|
445
|
-
return m.dataSync()[0] === 0 || r.dataSync()[0] === 0 ? 0 : h.dataSync()[0] / (m.dataSync()[0] * r.dataSync()[0]);
|
|
446
|
-
}, i = n(t), f = e.map((c) => n(c)).map((c) => a(i, c));
|
|
447
|
-
return { avg: f.reduce((c, l) => c + l, 0) / f.length, max: Math.max(...f), similarities: f };
|
|
472
|
+
}
|
|
473
|
+
class oe {
|
|
474
|
+
constructor(t) {
|
|
475
|
+
this.options = t, this.audio = new J(this), this.language = new I(this), this.vision = new W(this);
|
|
448
476
|
}
|
|
477
|
+
downloads = {};
|
|
478
|
+
whisperModel;
|
|
479
|
+
/** Audio processing AI */
|
|
480
|
+
audio;
|
|
481
|
+
/** Language processing AI */
|
|
482
|
+
language;
|
|
483
|
+
/** Vision processing AI */
|
|
484
|
+
vision;
|
|
449
485
|
}
|
|
450
|
-
const
|
|
486
|
+
const G = {
|
|
451
487
|
name: "cli",
|
|
452
488
|
description: "Use the command line interface, returns any output",
|
|
453
489
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
454
|
-
fn: (
|
|
455
|
-
},
|
|
490
|
+
fn: (p) => R`${p.command}`
|
|
491
|
+
}, se = {
|
|
456
492
|
name: "get_datetime",
|
|
457
493
|
description: "Get current date and time",
|
|
458
494
|
args: {},
|
|
459
495
|
fn: async () => (/* @__PURE__ */ new Date()).toISOString()
|
|
460
|
-
},
|
|
496
|
+
}, re = {
|
|
461
497
|
name: "exec",
|
|
462
498
|
description: "Run code/scripts",
|
|
463
499
|
args: {
|
|
464
500
|
language: { type: "string", description: "Execution language", enum: ["cli", "node", "python"], required: !0 },
|
|
465
501
|
code: { type: "string", description: "Code to execute", required: !0 }
|
|
466
502
|
},
|
|
467
|
-
fn: async (
|
|
503
|
+
fn: async (p, t) => {
|
|
468
504
|
try {
|
|
469
|
-
switch (
|
|
505
|
+
switch (p.type) {
|
|
470
506
|
case "bash":
|
|
471
|
-
return await
|
|
507
|
+
return await G.fn({ command: p.code }, t);
|
|
472
508
|
case "node":
|
|
473
|
-
return await
|
|
509
|
+
return await F.fn({ code: p.code }, t);
|
|
474
510
|
case "python":
|
|
475
|
-
return await
|
|
511
|
+
return await B.fn({ code: p.code }, t);
|
|
476
512
|
}
|
|
477
513
|
} catch (e) {
|
|
478
514
|
return { error: e?.message || e.toString() };
|
|
479
515
|
}
|
|
480
516
|
}
|
|
481
|
-
},
|
|
517
|
+
}, ae = {
|
|
482
518
|
name: "fetch",
|
|
483
519
|
description: "Make HTTP request to URL",
|
|
484
520
|
args: {
|
|
@@ -487,54 +523,54 @@ const z = {
|
|
|
487
523
|
headers: { type: "object", description: "HTTP headers to send", default: {} },
|
|
488
524
|
body: { type: "object", description: "HTTP body to send" }
|
|
489
525
|
},
|
|
490
|
-
fn: (
|
|
491
|
-
},
|
|
526
|
+
fn: (p) => new M({ url: p.url, headers: p.headers }).request({ method: p.method || "GET", body: p.body })
|
|
527
|
+
}, F = {
|
|
492
528
|
name: "exec_javascript",
|
|
493
529
|
description: "Execute commonjs javascript",
|
|
494
530
|
args: {
|
|
495
531
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
496
532
|
},
|
|
497
|
-
fn: async (
|
|
498
|
-
const t =
|
|
533
|
+
fn: async (p) => {
|
|
534
|
+
const t = A(null), e = await q({ console: t }, p.code, !0).catch((n) => t.output.error.push(n));
|
|
499
535
|
return { ...t.output, return: e, stdout: void 0, stderr: void 0 };
|
|
500
536
|
}
|
|
501
|
-
},
|
|
537
|
+
}, B = {
|
|
502
538
|
name: "exec_javascript",
|
|
503
539
|
description: "Execute commonjs javascript",
|
|
504
540
|
args: {
|
|
505
541
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
506
542
|
},
|
|
507
|
-
fn: async (
|
|
508
|
-
},
|
|
543
|
+
fn: async (p) => ({ result: D`python -c "${p.code}"` })
|
|
544
|
+
}, ie = {
|
|
509
545
|
name: "search",
|
|
510
546
|
description: "Use a search engine to find relevant URLs, should be changed with fetch to scrape sources",
|
|
511
547
|
args: {
|
|
512
548
|
query: { type: "string", description: "Search string", required: !0 },
|
|
513
549
|
length: { type: "string", description: "Number of results to return", default: 5 }
|
|
514
550
|
},
|
|
515
|
-
fn: async (
|
|
516
|
-
const t = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(
|
|
551
|
+
fn: async (p) => {
|
|
552
|
+
const t = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(p.query)}`, {
|
|
517
553
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
518
|
-
}).then((
|
|
554
|
+
}).then((c) => c.text());
|
|
519
555
|
let e, n = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
520
|
-
const a = new
|
|
556
|
+
const a = new v();
|
|
521
557
|
for (; (e = n.exec(t)) !== null; ) {
|
|
522
|
-
let
|
|
523
|
-
if (
|
|
558
|
+
let c = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
559
|
+
if (c && (c = decodeURIComponent(c)), c && a.add(c), a.size >= (p.length || 5)) break;
|
|
524
560
|
}
|
|
525
561
|
return a;
|
|
526
562
|
}
|
|
527
563
|
};
|
|
528
564
|
export {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
565
|
+
oe as Ai,
|
|
566
|
+
z as Anthropic,
|
|
567
|
+
G as CliTool,
|
|
568
|
+
se as DateTimeTool,
|
|
569
|
+
re as ExecTool,
|
|
570
|
+
ae as FetchTool,
|
|
571
|
+
F as JSTool,
|
|
572
|
+
I as LLM,
|
|
573
|
+
B as PythonTool,
|
|
574
|
+
ie as SearchTool
|
|
539
575
|
};
|
|
540
576
|
//# sourceMappingURL=index.mjs.map
|