@ztimson/ai-utils 0.6.4 → 0.6.5
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/index.js +17 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +208 -199
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { objectMap as
|
|
3
|
-
import { Anthropic as
|
|
4
|
-
import { OpenAI as
|
|
5
|
-
import { Worker as
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { join as
|
|
8
|
-
import { spawn as
|
|
9
|
-
import { pipeline as
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
12
|
-
import { createWorker as
|
|
1
|
+
import * as P from "node:os";
|
|
2
|
+
import { objectMap as x, JSONAttemptParse as b, findByProp as S, JSONSanitize as w, clean as q, Http as $, consoleInterceptor as M, fn as E, ASet as A } from "@ztimson/utils";
|
|
3
|
+
import { Anthropic as O } from "@anthropic-ai/sdk";
|
|
4
|
+
import { OpenAI as v } from "openai";
|
|
5
|
+
import { Worker as R } from "worker_threads";
|
|
6
|
+
import { fileURLToPath as U } from "url";
|
|
7
|
+
import { join as z, dirname as L } from "path";
|
|
8
|
+
import { spawn as k } from "node:child_process";
|
|
9
|
+
import { pipeline as N } from "@xenova/transformers";
|
|
10
|
+
import * as W from "node:fs";
|
|
11
|
+
import C from "wavefile";
|
|
12
|
+
import { createWorker as D } from "tesseract.js";
|
|
13
13
|
import "./embedder.mjs";
|
|
14
|
-
import * as
|
|
15
|
-
import { $ as
|
|
16
|
-
class
|
|
14
|
+
import * as I from "cheerio";
|
|
15
|
+
import { $ as F, $Sync as H } from "@ztimson/node-utils";
|
|
16
|
+
class j {
|
|
17
17
|
}
|
|
18
|
-
class
|
|
18
|
+
class J extends j {
|
|
19
19
|
constructor(r, e, t) {
|
|
20
|
-
super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new
|
|
20
|
+
super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new O({ apiKey: e });
|
|
21
21
|
}
|
|
22
22
|
client;
|
|
23
23
|
toStandard(r) {
|
|
@@ -26,15 +26,15 @@ class H extends S {
|
|
|
26
26
|
if (typeof i.content == "string")
|
|
27
27
|
t.push({ timestamp: e, ...i });
|
|
28
28
|
else {
|
|
29
|
-
const
|
|
29
|
+
const s = i.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
|
|
30
30
|
|
|
31
31
|
`);
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
t.push({ timestamp: e, role: "tool", id:
|
|
35
|
-
else if (
|
|
36
|
-
const o = t.findLast((a) => a.id ==
|
|
37
|
-
o && (o[
|
|
32
|
+
s && t.push({ timestamp: e, role: i.role, content: s }), i.content.forEach((n) => {
|
|
33
|
+
if (n.type == "tool_use")
|
|
34
|
+
t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
|
|
35
|
+
else if (n.type == "tool_result") {
|
|
36
|
+
const o = t.findLast((a) => a.id == n.tool_use_id);
|
|
37
|
+
o && (o[n.is_error ? "error" : "content"] = n.content);
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -56,34 +56,34 @@ class H extends S {
|
|
|
56
56
|
ask(r, e = {}) {
|
|
57
57
|
const t = new AbortController();
|
|
58
58
|
return Object.assign(new Promise(async (i) => {
|
|
59
|
-
let
|
|
60
|
-
const
|
|
59
|
+
let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
60
|
+
const n = e.tools || this.ai.options.llm?.tools || [], o = {
|
|
61
61
|
model: e.model || this.model,
|
|
62
62
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
63
63
|
system: e.system || this.ai.options.llm?.system || "",
|
|
64
64
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
65
|
-
tools:
|
|
65
|
+
tools: n.map((d) => ({
|
|
66
66
|
name: d.name,
|
|
67
67
|
description: d.description,
|
|
68
68
|
input_schema: {
|
|
69
69
|
type: "object",
|
|
70
|
-
properties: d.args ?
|
|
70
|
+
properties: d.args ? x(d.args, (c, l) => ({ ...l, required: void 0 })) : {},
|
|
71
71
|
required: d.args ? Object.entries(d.args).filter((c) => c[1].required).map((c) => c[0]) : []
|
|
72
72
|
},
|
|
73
73
|
fn: void 0
|
|
74
74
|
})),
|
|
75
|
-
messages:
|
|
75
|
+
messages: s,
|
|
76
76
|
stream: !!e.stream
|
|
77
77
|
};
|
|
78
|
-
let a,
|
|
78
|
+
let a, m = !0;
|
|
79
79
|
do {
|
|
80
80
|
if (a = await this.client.messages.create(o).catch((c) => {
|
|
81
81
|
throw c.message += `
|
|
82
82
|
|
|
83
83
|
Messages:
|
|
84
|
-
${JSON.stringify(
|
|
84
|
+
${JSON.stringify(s, null, 2)}`, c;
|
|
85
85
|
}), e.stream) {
|
|
86
|
-
|
|
86
|
+
m ? m = !1 : e.stream({ text: `
|
|
87
87
|
|
|
88
88
|
` }), a.content = [];
|
|
89
89
|
for await (const c of a) {
|
|
@@ -92,41 +92,41 @@ ${JSON.stringify(n, null, 2)}`, c;
|
|
|
92
92
|
c.content_block.type === "text" ? a.content.push({ type: "text", text: "" }) : c.content_block.type === "tool_use" && a.content.push({ type: "tool_use", id: c.content_block.id, name: c.content_block.name, input: "" });
|
|
93
93
|
else if (c.type === "content_block_delta")
|
|
94
94
|
if (c.delta.type === "text_delta") {
|
|
95
|
-
const
|
|
96
|
-
a.content.at(-1).text +=
|
|
95
|
+
const l = c.delta.text;
|
|
96
|
+
a.content.at(-1).text += l, e.stream({ text: l });
|
|
97
97
|
} else c.delta.type === "input_json_delta" && (a.content.at(-1).input += c.delta.partial_json);
|
|
98
98
|
else if (c.type === "content_block_stop") {
|
|
99
|
-
const
|
|
100
|
-
|
|
99
|
+
const l = a.content.at(-1);
|
|
100
|
+
l.input != null && (l.input = l.input ? b(l.input, {}) : {});
|
|
101
101
|
} else if (c.type === "message_stop")
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
const d = a.content.filter((c) => c.type === "tool_use");
|
|
106
106
|
if (d.length && !t.signal.aborted) {
|
|
107
|
-
|
|
108
|
-
const c = await Promise.all(d.map(async (
|
|
109
|
-
const h =
|
|
110
|
-
if (e.stream && e.stream({ tool:
|
|
107
|
+
s.push({ role: "assistant", content: a.content });
|
|
108
|
+
const c = await Promise.all(d.map(async (l) => {
|
|
109
|
+
const h = n.find(S("name", l.name));
|
|
110
|
+
if (e.stream && e.stream({ tool: l.name }), !h) return { tool_use_id: l.id, is_error: !0, content: "Tool not found" };
|
|
111
111
|
try {
|
|
112
|
-
const u = await h.fn(
|
|
113
|
-
return { type: "tool_result", tool_use_id:
|
|
112
|
+
const u = await h.fn(l.input, e?.stream, this.ai);
|
|
113
|
+
return { type: "tool_result", tool_use_id: l.id, content: w(u) };
|
|
114
114
|
} catch (u) {
|
|
115
|
-
return { type: "tool_result", tool_use_id:
|
|
115
|
+
return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
|
|
116
116
|
}
|
|
117
117
|
}));
|
|
118
|
-
|
|
118
|
+
s.push({ role: "user", content: c }), o.messages = s;
|
|
119
119
|
}
|
|
120
120
|
} while (!t.signal.aborted && a.content.some((d) => d.type === "tool_use"));
|
|
121
|
-
|
|
121
|
+
s.push({ role: "assistant", content: a.content.filter((d) => d.type == "text").map((d) => d.text).join(`
|
|
122
122
|
|
|
123
|
-
`) }),
|
|
123
|
+
`) }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), i(s.at(-1)?.content);
|
|
124
124
|
}), { abort: () => t.abort() });
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
class
|
|
127
|
+
class _ extends j {
|
|
128
128
|
constructor(r, e, t, i) {
|
|
129
|
-
super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new
|
|
129
|
+
super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new v(q({
|
|
130
130
|
baseURL: e,
|
|
131
131
|
apiKey: t
|
|
132
132
|
}));
|
|
@@ -136,16 +136,16 @@ class k extends S {
|
|
|
136
136
|
for (let e = 0; e < r.length; e++) {
|
|
137
137
|
const t = r[e];
|
|
138
138
|
if (t.role === "assistant" && t.tool_calls) {
|
|
139
|
-
const i = t.tool_calls.map((
|
|
139
|
+
const i = t.tool_calls.map((s) => ({
|
|
140
140
|
role: "tool",
|
|
141
|
-
id:
|
|
142
|
-
name:
|
|
143
|
-
args:
|
|
141
|
+
id: s.id,
|
|
142
|
+
name: s.function.name,
|
|
143
|
+
args: b(s.function.arguments, {}),
|
|
144
144
|
timestamp: t.timestamp
|
|
145
145
|
}));
|
|
146
146
|
r.splice(e, 1, ...i), e += i.length - 1;
|
|
147
147
|
} else if (t.role === "tool" && t.content) {
|
|
148
|
-
const i = r.find((
|
|
148
|
+
const i = r.find((s) => t.tool_call_id == s.id);
|
|
149
149
|
i && (t.content.includes('"error":') ? i.error = t.content : i.content = t.content), r.splice(e, 1), e--;
|
|
150
150
|
}
|
|
151
151
|
r[e]?.timestamp || (r[e].timestamp = Date.now());
|
|
@@ -167,20 +167,20 @@ class k extends S {
|
|
|
167
167
|
content: t.error || t.content
|
|
168
168
|
});
|
|
169
169
|
else {
|
|
170
|
-
const { timestamp: i, ...
|
|
171
|
-
e.push(
|
|
170
|
+
const { timestamp: i, ...s } = t;
|
|
171
|
+
e.push(s);
|
|
172
172
|
}
|
|
173
173
|
return e;
|
|
174
174
|
}, []);
|
|
175
175
|
}
|
|
176
176
|
ask(r, e = {}) {
|
|
177
177
|
const t = new AbortController();
|
|
178
|
-
return Object.assign(new Promise(async (i,
|
|
178
|
+
return Object.assign(new Promise(async (i, s) => {
|
|
179
179
|
e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
|
|
180
|
-
let
|
|
180
|
+
let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
181
181
|
const o = e.tools || this.ai.options.llm?.tools || [], a = {
|
|
182
182
|
model: e.model || this.model,
|
|
183
|
-
messages:
|
|
183
|
+
messages: n,
|
|
184
184
|
stream: !!e.stream,
|
|
185
185
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
186
186
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
@@ -191,55 +191,55 @@ class k extends S {
|
|
|
191
191
|
description: c.description,
|
|
192
192
|
parameters: {
|
|
193
193
|
type: "object",
|
|
194
|
-
properties: c.args ?
|
|
195
|
-
required: c.args ? Object.entries(c.args).filter((
|
|
194
|
+
properties: c.args ? x(c.args, (l, h) => ({ ...h, required: void 0 })) : {},
|
|
195
|
+
required: c.args ? Object.entries(c.args).filter((l) => l[1].required).map((l) => l[0]) : []
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}))
|
|
199
199
|
};
|
|
200
|
-
let
|
|
200
|
+
let m, d = !0;
|
|
201
201
|
do {
|
|
202
|
-
if (
|
|
203
|
-
throw
|
|
202
|
+
if (m = await this.client.chat.completions.create(a).catch((l) => {
|
|
203
|
+
throw l.message += `
|
|
204
204
|
|
|
205
205
|
Messages:
|
|
206
|
-
${JSON.stringify(
|
|
206
|
+
${JSON.stringify(n, null, 2)}`, l;
|
|
207
207
|
}), e.stream) {
|
|
208
208
|
d ? d = !1 : e.stream({ text: `
|
|
209
209
|
|
|
210
|
-
` }),
|
|
211
|
-
for await (const
|
|
210
|
+
` }), m.choices = [{ message: { content: "", tool_calls: [] } }];
|
|
211
|
+
for await (const l of m) {
|
|
212
212
|
if (t.signal.aborted) break;
|
|
213
|
-
|
|
213
|
+
l.choices[0].delta.content && (m.choices[0].message.content += l.choices[0].delta.content, e.stream({ text: l.choices[0].delta.content })), l.choices[0].delta.tool_calls && (m.choices[0].message.tool_calls = l.choices[0].delta.tool_calls);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
-
const c =
|
|
216
|
+
const c = m.choices[0].message.tool_calls || [];
|
|
217
217
|
if (c.length && !t.signal.aborted) {
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
const u = o?.find(
|
|
218
|
+
n.push(m.choices[0].message);
|
|
219
|
+
const l = await Promise.all(c.map(async (h) => {
|
|
220
|
+
const u = o?.find(S("name", h.function.name));
|
|
221
221
|
if (e.stream && e.stream({ tool: h.function.name }), !u) return { role: "tool", tool_call_id: h.id, content: '{"error": "Tool not found"}' };
|
|
222
222
|
try {
|
|
223
|
-
const f =
|
|
224
|
-
return { role: "tool", tool_call_id: h.id, content:
|
|
223
|
+
const f = b(h.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
|
|
224
|
+
return { role: "tool", tool_call_id: h.id, content: w(y) };
|
|
225
225
|
} catch (f) {
|
|
226
|
-
return { role: "tool", tool_call_id: h.id, content:
|
|
226
|
+
return { role: "tool", tool_call_id: h.id, content: w({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
227
227
|
}
|
|
228
228
|
}));
|
|
229
|
-
|
|
229
|
+
n.push(...l), a.messages = n;
|
|
230
230
|
}
|
|
231
|
-
} while (!t.signal.aborted &&
|
|
232
|
-
|
|
231
|
+
} while (!t.signal.aborted && m.choices?.[0]?.message?.tool_calls?.length);
|
|
232
|
+
n.push({ role: "assistant", content: m.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), i(n.at(-1)?.content);
|
|
233
233
|
}), { abort: () => t.abort() });
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
|
-
class
|
|
236
|
+
class B {
|
|
237
237
|
constructor(r) {
|
|
238
|
-
this.ai = r, this.embedWorker = new
|
|
238
|
+
this.ai = r, this.embedWorker = new R(z(L(U(import.meta.url)), "embedder.js")), this.embedWorker.on("message", ({ id: e, embedding: t }) => {
|
|
239
239
|
const i = this.embedQueue.get(e);
|
|
240
240
|
i && (i.resolve(t), this.embedQueue.delete(e));
|
|
241
241
|
}), r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
|
|
242
|
-
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new
|
|
242
|
+
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new J(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new _(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new _(this.ai, t.host || null, t.token, e));
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
embedWorker = null;
|
|
@@ -258,20 +258,20 @@ class J {
|
|
|
258
258
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
259
259
|
let i = () => {
|
|
260
260
|
};
|
|
261
|
-
return Object.assign(new Promise(async (
|
|
261
|
+
return Object.assign(new Promise(async (s) => {
|
|
262
262
|
if (e.history || (e.history = []), e.memory) {
|
|
263
263
|
e.system = (e.system || "") + `
|
|
264
264
|
You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
|
|
265
265
|
`;
|
|
266
|
-
const o = async (
|
|
267
|
-
const [
|
|
266
|
+
const o = async (m, d, c = 50) => {
|
|
267
|
+
const [l, h] = await Promise.all([
|
|
268
268
|
d ? this.embedding(d) : Promise.resolve(null),
|
|
269
|
-
|
|
269
|
+
m ? this.embedding(m) : Promise.resolve(null)
|
|
270
270
|
]);
|
|
271
|
-
return (e.memory || []).map((u) => ({ ...u, score:
|
|
271
|
+
return (e.memory || []).map((u) => ({ ...u, score: l ? this.cosineSimilarity(u.embeddings[0], l[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: h ? this.cosineSimilarity(u.embeddings[1], h[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, c);
|
|
272
272
|
}, a = await o(r);
|
|
273
273
|
a.length && e.history.push({ role: "assistant", content: `Things I remembered:
|
|
274
|
-
` + a.map((
|
|
274
|
+
` + a.map((m) => `${m.owner}: ${m.fact}`).join(`
|
|
275
275
|
`) }), e.tools = [...e.tools || [], {
|
|
276
276
|
name: "read_memory",
|
|
277
277
|
description: "Check your long-term memory for more information",
|
|
@@ -280,13 +280,13 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
280
280
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
281
281
|
limit: { type: "number", description: "Result limit, default 5" }
|
|
282
282
|
},
|
|
283
|
-
fn: (
|
|
284
|
-
if (!
|
|
285
|
-
return o(
|
|
283
|
+
fn: (m) => {
|
|
284
|
+
if (!m.subject && !m.query) throw new Error("Either a subject or query argument is required");
|
|
285
|
+
return o(m.query, m.subject, m.limit || 5);
|
|
286
286
|
}
|
|
287
287
|
}];
|
|
288
288
|
}
|
|
289
|
-
const
|
|
289
|
+
const n = await this.models[t].ask(r, e);
|
|
290
290
|
if (e.memory) {
|
|
291
291
|
const o = e.history?.findIndex((a) => a.role == "assistant" && a.content.startsWith("Things I remembered:"));
|
|
292
292
|
o != null && o >= 0 && e.history?.splice(o, 1);
|
|
@@ -296,15 +296,15 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
296
296
|
if (e.compress)
|
|
297
297
|
o = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...o.history);
|
|
298
298
|
else {
|
|
299
|
-
const a = e.history?.findLastIndex((
|
|
299
|
+
const a = e.history?.findLastIndex((m) => m.role == "user") ?? -1;
|
|
300
300
|
o = await this.ai.language.compressHistory(a != -1 ? e.history.slice(a) : e.history, 0, 0, e);
|
|
301
301
|
}
|
|
302
302
|
if (e.memory) {
|
|
303
|
-
const a = e.memory.filter((
|
|
303
|
+
const a = e.memory.filter((m) => !o.memory.some((d) => this.cosineSimilarity(m.embeddings[1], d.embeddings[1]) > 0.8)).concat(o.memory);
|
|
304
304
|
e.memory.splice(0, e.memory.length, ...a);
|
|
305
305
|
}
|
|
306
306
|
}
|
|
307
|
-
return n
|
|
307
|
+
return s(n);
|
|
308
308
|
}), { abort: i });
|
|
309
309
|
}
|
|
310
310
|
/**
|
|
@@ -317,20 +317,20 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
317
317
|
*/
|
|
318
318
|
async compressHistory(r, e, t, i) {
|
|
319
319
|
if (this.estimateTokens(r) < e) return { history: r, memory: [] };
|
|
320
|
-
let
|
|
320
|
+
let s = 0, n = 0;
|
|
321
321
|
for (let u of r.toReversed())
|
|
322
|
-
if (
|
|
322
|
+
if (n += this.estimateTokens(u.content), n < t) s++;
|
|
323
323
|
else break;
|
|
324
|
-
if (r.length <=
|
|
325
|
-
const o = r[0].role == "system" ? r[0] : null, a =
|
|
324
|
+
if (r.length <= s) return { history: r, memory: [] };
|
|
325
|
+
const o = r[0].role == "system" ? r[0] : null, a = s == 0 ? [] : r.slice(-s), m = (s == 0 ? r : r.slice(0, -s)).filter((u) => u.role === "assistant" || u.role === "user"), d = await this.json(`Create the smallest summary possible, no more than 500 tokens. Create a list of NEW facts (split by subject [pro]noun and fact) about what you learned from this conversation that you didn't already know or get from a tool call or system prompt. Focus only on new information about people, topics, or facts. Avoid generating facts about the AI. Match this format: {summary: string, facts: [[subject, fact]]}
|
|
326
326
|
|
|
327
|
-
${
|
|
327
|
+
${m.map((u) => `${u.role}: ${u.content}`).join(`
|
|
328
328
|
|
|
329
|
-
`)}`, { model: i?.model, temperature: i?.temperature || 0.3 }), c = /* @__PURE__ */ new Date(),
|
|
329
|
+
`)}`, { model: i?.model, temperature: i?.temperature || 0.3 }), c = /* @__PURE__ */ new Date(), l = await Promise.all((d?.facts || [])?.map(async ([u, f]) => {
|
|
330
330
|
const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
|
|
331
331
|
return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: c };
|
|
332
332
|
})), h = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...a];
|
|
333
|
-
return o && h.splice(0, 0, o), { history: h, memory:
|
|
333
|
+
return o && h.splice(0, 0, o), { history: h, memory: l };
|
|
334
334
|
}
|
|
335
335
|
/**
|
|
336
336
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -340,11 +340,11 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
340
340
|
*/
|
|
341
341
|
cosineSimilarity(r, e) {
|
|
342
342
|
if (r.length !== e.length) throw new Error("Vectors must be same length");
|
|
343
|
-
let t = 0, i = 0,
|
|
343
|
+
let t = 0, i = 0, s = 0;
|
|
344
344
|
for (let o = 0; o < r.length; o++)
|
|
345
|
-
t += r[o] * e[o], i += r[o] * r[o],
|
|
346
|
-
const
|
|
347
|
-
return
|
|
345
|
+
t += r[o] * e[o], i += r[o] * r[o], s += e[o] * e[o];
|
|
346
|
+
const n = Math.sqrt(i) * Math.sqrt(s);
|
|
347
|
+
return n === 0 ? 0 : t / n;
|
|
348
348
|
}
|
|
349
349
|
/**
|
|
350
350
|
* Chunk text into parts for AI digestion
|
|
@@ -354,21 +354,21 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
354
354
|
* @returns {string[]} Chunked strings
|
|
355
355
|
*/
|
|
356
356
|
chunk(r, e = 500, t = 50) {
|
|
357
|
-
const i = (a,
|
|
358
|
-
const
|
|
359
|
-
return typeof c == "object" && !Array.isArray(c) ? i(c,
|
|
360
|
-
}) : [],
|
|
357
|
+
const i = (a, m = "") => a ? Object.entries(a).flatMap(([d, c]) => {
|
|
358
|
+
const l = m ? `${m}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
|
|
359
|
+
return typeof c == "object" && !Array.isArray(c) ? i(c, l) : `${l}: ${Array.isArray(c) ? c.join(", ") : c}`;
|
|
360
|
+
}) : [], n = (typeof r == "object" ? i(r) : r.split(`
|
|
361
361
|
`)).flatMap((a) => [...a.split(/\s+/).filter(Boolean), `
|
|
362
362
|
`]), o = [];
|
|
363
|
-
for (let a = 0; a <
|
|
364
|
-
let
|
|
365
|
-
for (; d <
|
|
366
|
-
const
|
|
367
|
-
if (this.estimateTokens(
|
|
368
|
-
`)) > e &&
|
|
369
|
-
|
|
363
|
+
for (let a = 0; a < n.length; ) {
|
|
364
|
+
let m = "", d = a;
|
|
365
|
+
for (; d < n.length; ) {
|
|
366
|
+
const l = m + (m ? " " : "") + n[d];
|
|
367
|
+
if (this.estimateTokens(l.replace(/\s*\n\s*/g, `
|
|
368
|
+
`)) > e && m) break;
|
|
369
|
+
m = l, d++;
|
|
370
370
|
}
|
|
371
|
-
const c =
|
|
371
|
+
const c = m.replace(/\s*\n\s*/g, `
|
|
372
372
|
`).trim();
|
|
373
373
|
c && o.push(c), a = Math.max(d - t, d === a ? a + 1 : d);
|
|
374
374
|
}
|
|
@@ -382,20 +382,20 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
382
382
|
* @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
|
|
383
383
|
*/
|
|
384
384
|
embedding(r, e = 500, t = 50) {
|
|
385
|
-
const i = (
|
|
386
|
-
const
|
|
387
|
-
this.embedQueue.set(
|
|
388
|
-
id:
|
|
389
|
-
text:
|
|
385
|
+
const i = (n) => new Promise((o, a) => {
|
|
386
|
+
const m = this.embedId++;
|
|
387
|
+
this.embedQueue.set(m, { resolve: o, reject: a }), this.embedWorker?.postMessage({
|
|
388
|
+
id: m,
|
|
389
|
+
text: n,
|
|
390
390
|
model: this.ai.options?.embedder || "bge-small-en-v1.5",
|
|
391
391
|
path: this.ai.options.path
|
|
392
392
|
});
|
|
393
|
-
}),
|
|
394
|
-
return Promise.all(
|
|
393
|
+
}), s = this.chunk(r, e, t);
|
|
394
|
+
return Promise.all(s.map(async (n, o) => ({
|
|
395
395
|
index: o,
|
|
396
|
-
embedding: await i(
|
|
397
|
-
text:
|
|
398
|
-
tokens: this.estimateTokens(
|
|
396
|
+
embedding: await i(n),
|
|
397
|
+
text: n,
|
|
398
|
+
tokens: this.estimateTokens(n)
|
|
399
399
|
})));
|
|
400
400
|
}
|
|
401
401
|
/**
|
|
@@ -415,8 +415,8 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
415
415
|
*/
|
|
416
416
|
fuzzyMatch(r, ...e) {
|
|
417
417
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
418
|
-
const t = (
|
|
419
|
-
return { avg:
|
|
418
|
+
const t = (n, o = 10) => n.toLowerCase().split("").map((a, m) => a.charCodeAt(0) * (m + 1) % o / o).slice(0, o), i = t(r), s = e.map((n) => t(n)).map((n) => this.cosineSimilarity(i, n));
|
|
419
|
+
return { avg: s.reduce((n, o) => n + o, 0) / s.length, max: Math.max(...s), similarities: s };
|
|
420
420
|
}
|
|
421
421
|
/**
|
|
422
422
|
* Ask a question with JSON response
|
|
@@ -427,8 +427,8 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
427
427
|
async json(r, e) {
|
|
428
428
|
let t = await this.ask(r, { system: "Respond using a JSON blob matching any provided examples", ...e });
|
|
429
429
|
if (!t) return {};
|
|
430
|
-
const i = /```(?:.+)?\s*([\s\S]*?)```/.exec(t),
|
|
431
|
-
return
|
|
430
|
+
const i = /```(?:.+)?\s*([\s\S]*?)```/.exec(t), s = i ? i[1].trim() : t;
|
|
431
|
+
return b(s, {});
|
|
432
432
|
}
|
|
433
433
|
/**
|
|
434
434
|
* Create a summary of some text
|
|
@@ -441,7 +441,7 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
|
|
|
441
441
|
return this.ask(r, { system: `Generate a brief summary <= ${e} tokens. Output nothing else`, temperature: 0.3, ...t });
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
|
-
class
|
|
444
|
+
class G {
|
|
445
445
|
constructor(r) {
|
|
446
446
|
this.ai = r;
|
|
447
447
|
}
|
|
@@ -452,17 +452,17 @@ class F {
|
|
|
452
452
|
e.forEach((a) => {
|
|
453
453
|
t.has(a.speaker) || t.set(a.speaker, ++i);
|
|
454
454
|
});
|
|
455
|
-
const
|
|
456
|
-
let
|
|
455
|
+
const s = [];
|
|
456
|
+
let n = -1, o = "";
|
|
457
457
|
return r.forEach((a) => {
|
|
458
|
-
const
|
|
459
|
-
c !==
|
|
460
|
-
}), o &&
|
|
458
|
+
const m = a.timestamp[0], d = e.find((l) => m >= l.start && m <= l.end), c = d ? t.get(d.speaker) : 1;
|
|
459
|
+
c !== n ? (o && s.push(`[speaker ${n}]: ${o.trim()}`), n = c, o = a.text) : o += a.text;
|
|
460
|
+
}), o && s.push(`[speaker ${n}]: ${o.trim()}`), s.join(`
|
|
461
461
|
`);
|
|
462
462
|
}
|
|
463
463
|
async canDiarization() {
|
|
464
464
|
return new Promise((r) => {
|
|
465
|
-
const e =
|
|
465
|
+
const e = k("python3", ["-c", "import pyannote.audio"]);
|
|
466
466
|
e.on("close", (t) => r(t === 0)), e.on("error", () => r(!1));
|
|
467
467
|
});
|
|
468
468
|
}
|
|
@@ -488,45 +488,54 @@ for turn, _, speaker in diarization.itertracks(yield_label=True):
|
|
|
488
488
|
print(json.dumps(segments))
|
|
489
489
|
`;
|
|
490
490
|
return new Promise((t, i) => {
|
|
491
|
-
let
|
|
492
|
-
const
|
|
493
|
-
|
|
491
|
+
let s = "";
|
|
492
|
+
const n = k("python3", ["-c", e, r]);
|
|
493
|
+
n.stdout.on("data", (o) => s += o.toString()), n.stderr.on("data", (o) => console.error(o.toString())), n.on("close", (o) => {
|
|
494
494
|
if (o === 0)
|
|
495
495
|
try {
|
|
496
|
-
t(JSON.parse(
|
|
496
|
+
t(JSON.parse(s));
|
|
497
497
|
} catch {
|
|
498
498
|
i(new Error("Failed to parse diarization output"));
|
|
499
499
|
}
|
|
500
500
|
else
|
|
501
501
|
i(new Error(`Python process exited with code ${o}`));
|
|
502
|
-
}),
|
|
502
|
+
}), n.on("error", i);
|
|
503
503
|
});
|
|
504
504
|
}
|
|
505
505
|
asr(r, e = {}) {
|
|
506
506
|
const { model: t = this.ai.options.asr || "whisper-base", speaker: i = !1 } = e;
|
|
507
|
-
let
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
}, o = new Promise(async (a,
|
|
507
|
+
let s = !1;
|
|
508
|
+
const n = () => {
|
|
509
|
+
s = !0;
|
|
510
|
+
}, o = new Promise(async (a, m) => {
|
|
511
511
|
try {
|
|
512
|
-
if (
|
|
513
|
-
const d =
|
|
514
|
-
|
|
515
|
-
const
|
|
512
|
+
if (s || (this.whisperPipeline || (this.whisperPipeline = await N("automatic-speech-recognition", `Xenova/${t}`, { cache_dir: this.ai.options.path, quantized: !0 })), s)) return a(null);
|
|
513
|
+
const d = new C.WaveFile(W.readFileSync(r));
|
|
514
|
+
d.toBitDepth("32f"), d.toSampleRate(16e3);
|
|
515
|
+
const c = d.getSamples();
|
|
516
|
+
let l;
|
|
517
|
+
if (Array.isArray(c)) {
|
|
518
|
+
const y = c[0], T = c[1];
|
|
519
|
+
l = new Float32Array(y.length);
|
|
520
|
+
for (let g = 0; g < y.length; g++) l[g] = (y[g] + T[g]) / 2;
|
|
521
|
+
} else
|
|
522
|
+
l = c;
|
|
523
|
+
if (s) return a(null);
|
|
524
|
+
const h = await this.whisperPipeline(l, { return_timestamps: i ? "word" : !1 });
|
|
516
525
|
if (!i) return a(h.text?.trim() || null);
|
|
517
|
-
if (
|
|
526
|
+
if (s) return a(null);
|
|
518
527
|
const u = await this.runDiarization(r);
|
|
519
|
-
if (
|
|
528
|
+
if (s) return a(null);
|
|
520
529
|
const f = this.combineSpeakerTranscript(h.chunks || [], u);
|
|
521
530
|
a(f);
|
|
522
531
|
} catch (d) {
|
|
523
|
-
|
|
532
|
+
m(d);
|
|
524
533
|
}
|
|
525
534
|
});
|
|
526
|
-
return Object.assign(o, { abort:
|
|
535
|
+
return Object.assign(o, { abort: n });
|
|
527
536
|
}
|
|
528
537
|
}
|
|
529
|
-
class
|
|
538
|
+
class Q {
|
|
530
539
|
constructor(r) {
|
|
531
540
|
this.ai = r;
|
|
532
541
|
}
|
|
@@ -538,16 +547,16 @@ class B {
|
|
|
538
547
|
ocr(r) {
|
|
539
548
|
let e;
|
|
540
549
|
const t = new Promise(async (i) => {
|
|
541
|
-
e = await
|
|
542
|
-
const { data:
|
|
543
|
-
await e.terminate(), i(
|
|
550
|
+
e = await D(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
551
|
+
const { data: s } = await e.recognize(r);
|
|
552
|
+
await e.terminate(), i(s.text.trim() || null);
|
|
544
553
|
});
|
|
545
554
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
546
555
|
}
|
|
547
556
|
}
|
|
548
|
-
class
|
|
557
|
+
class me {
|
|
549
558
|
constructor(r) {
|
|
550
|
-
this.options = r, r.path || (r.path =
|
|
559
|
+
this.options = r, r.path || (r.path = P.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new G(this), this.language = new B(this), this.vision = new Q(this);
|
|
551
560
|
}
|
|
552
561
|
/** Audio processing AI */
|
|
553
562
|
audio;
|
|
@@ -556,17 +565,17 @@ class ae {
|
|
|
556
565
|
/** Vision processing AI */
|
|
557
566
|
vision;
|
|
558
567
|
}
|
|
559
|
-
const
|
|
568
|
+
const K = {
|
|
560
569
|
name: "cli",
|
|
561
570
|
description: "Use the command line interface, returns any output",
|
|
562
571
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
563
|
-
fn: (p) =>
|
|
564
|
-
},
|
|
572
|
+
fn: (p) => F`${p.command}`
|
|
573
|
+
}, de = {
|
|
565
574
|
name: "get_datetime",
|
|
566
575
|
description: "Get current UTC date / time",
|
|
567
576
|
args: {},
|
|
568
577
|
fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
|
|
569
|
-
},
|
|
578
|
+
}, ue = {
|
|
570
579
|
name: "exec",
|
|
571
580
|
description: "Run code/scripts",
|
|
572
581
|
args: {
|
|
@@ -577,17 +586,17 @@ const G = {
|
|
|
577
586
|
try {
|
|
578
587
|
switch (p.type) {
|
|
579
588
|
case "bash":
|
|
580
|
-
return await
|
|
589
|
+
return await K.fn({ command: p.code }, r, e);
|
|
581
590
|
case "node":
|
|
582
|
-
return await
|
|
591
|
+
return await V.fn({ code: p.code }, r, e);
|
|
583
592
|
case "python":
|
|
584
|
-
return await
|
|
593
|
+
return await X.fn({ code: p.code }, r, e);
|
|
585
594
|
}
|
|
586
595
|
} catch (t) {
|
|
587
596
|
return { error: t?.message || t.toString() };
|
|
588
597
|
}
|
|
589
598
|
}
|
|
590
|
-
},
|
|
599
|
+
}, pe = {
|
|
591
600
|
name: "fetch",
|
|
592
601
|
description: "Make HTTP request to URL",
|
|
593
602
|
args: {
|
|
@@ -596,25 +605,25 @@ const G = {
|
|
|
596
605
|
headers: { type: "object", description: "HTTP headers to send", default: {} },
|
|
597
606
|
body: { type: "object", description: "HTTP body to send" }
|
|
598
607
|
},
|
|
599
|
-
fn: (p) => new
|
|
600
|
-
},
|
|
608
|
+
fn: (p) => new $({ url: p.url, headers: p.headers }).request({ method: p.method || "GET", body: p.body })
|
|
609
|
+
}, V = {
|
|
601
610
|
name: "exec_javascript",
|
|
602
611
|
description: "Execute commonjs javascript",
|
|
603
612
|
args: {
|
|
604
613
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
605
614
|
},
|
|
606
615
|
fn: async (p) => {
|
|
607
|
-
const r =
|
|
616
|
+
const r = M(null), e = await E({ console: r }, p.code, !0).catch((t) => r.output.error.push(t));
|
|
608
617
|
return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
|
|
609
618
|
}
|
|
610
|
-
},
|
|
619
|
+
}, X = {
|
|
611
620
|
name: "exec_javascript",
|
|
612
621
|
description: "Execute commonjs javascript",
|
|
613
622
|
args: {
|
|
614
623
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
615
624
|
},
|
|
616
|
-
fn: async (p) => ({ result:
|
|
617
|
-
},
|
|
625
|
+
fn: async (p) => ({ result: H`python -c "${p.code}"` })
|
|
626
|
+
}, he = {
|
|
618
627
|
name: "read_webpage",
|
|
619
628
|
description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
|
|
620
629
|
args: {
|
|
@@ -622,18 +631,18 @@ const G = {
|
|
|
622
631
|
focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
|
|
623
632
|
},
|
|
624
633
|
fn: async (p) => {
|
|
625
|
-
const r = await fetch(p.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((
|
|
626
|
-
throw new Error(`Failed to fetch: ${
|
|
627
|
-
}), e =
|
|
634
|
+
const r = await fetch(p.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((n) => n.text()).catch((n) => {
|
|
635
|
+
throw new Error(`Failed to fetch: ${n.message}`);
|
|
636
|
+
}), e = I.load(r);
|
|
628
637
|
e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
|
|
629
638
|
const t = {
|
|
630
639
|
title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
|
|
631
640
|
description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
|
|
632
641
|
};
|
|
633
642
|
let i = "";
|
|
634
|
-
const
|
|
635
|
-
for (const
|
|
636
|
-
const o = e(
|
|
643
|
+
const s = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
|
|
644
|
+
for (const n of s) {
|
|
645
|
+
const o = e(n).first();
|
|
637
646
|
if (o.length && o.text().trim().length > 200) {
|
|
638
647
|
i = o.text();
|
|
639
648
|
break;
|
|
@@ -641,7 +650,7 @@ const G = {
|
|
|
641
650
|
}
|
|
642
651
|
return i || (i = e("body").text()), i = i.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: p.url, title: t.title.trim(), description: t.description.trim(), content: i, focus: p.focus };
|
|
643
652
|
}
|
|
644
|
-
},
|
|
653
|
+
}, fe = {
|
|
645
654
|
name: "web_search",
|
|
646
655
|
description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
|
|
647
656
|
args: {
|
|
@@ -651,30 +660,30 @@ const G = {
|
|
|
651
660
|
fn: async (p) => {
|
|
652
661
|
const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(p.query)}`, {
|
|
653
662
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
654
|
-
}).then((
|
|
663
|
+
}).then((s) => s.text());
|
|
655
664
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
656
|
-
const i = new
|
|
665
|
+
const i = new A();
|
|
657
666
|
for (; (e = t.exec(r)) !== null; ) {
|
|
658
|
-
let
|
|
659
|
-
if (
|
|
667
|
+
let s = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
668
|
+
if (s && (s = decodeURIComponent(s)), s && i.add(s), i.size >= (p.length || 5)) break;
|
|
660
669
|
}
|
|
661
670
|
return i;
|
|
662
671
|
}
|
|
663
672
|
};
|
|
664
673
|
export {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
674
|
+
me as Ai,
|
|
675
|
+
J as Anthropic,
|
|
676
|
+
G as Audio,
|
|
677
|
+
K as CliTool,
|
|
678
|
+
de as DateTimeTool,
|
|
679
|
+
ue as ExecTool,
|
|
680
|
+
pe as FetchTool,
|
|
681
|
+
V as JSTool,
|
|
682
|
+
j as LLMProvider,
|
|
683
|
+
_ as OpenAi,
|
|
684
|
+
X as PythonTool,
|
|
685
|
+
he as ReadWebpageTool,
|
|
686
|
+
Q as Vision,
|
|
687
|
+
fe as WebSearchTool
|
|
679
688
|
};
|
|
680
689
|
//# sourceMappingURL=index.mjs.map
|