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