@ztimson/ai-utils 0.7.8 → 0.7.10
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.d.ts +0 -1
- package/dist/index.js +20 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -223
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -6,11 +6,10 @@ import { OpenAI as U } from "openai";
|
|
|
6
6
|
import { fileURLToPath as z } from "url";
|
|
7
7
|
import { join as N, dirname as L } from "path";
|
|
8
8
|
import { spawn as g, execSync as C } from "node:child_process";
|
|
9
|
-
import { mkdtempSync as J
|
|
10
|
-
import k from "node:fs/promises";
|
|
9
|
+
import { mkdtempSync as J } from "node:fs";
|
|
10
|
+
import k, { rm as W } from "node:fs/promises";
|
|
11
11
|
import _, { join as x } from "node:path";
|
|
12
12
|
import { createWorker as D } from "tesseract.js";
|
|
13
|
-
import "./embedder.mjs";
|
|
14
13
|
import * as I from "cheerio";
|
|
15
14
|
import { $ as F, $Sync as H } from "@ztimson/node-utils";
|
|
16
15
|
class E {
|
|
@@ -22,19 +21,19 @@ class G extends E {
|
|
|
22
21
|
client;
|
|
23
22
|
toStandard(r) {
|
|
24
23
|
const e = Date.now(), t = [];
|
|
25
|
-
for (let
|
|
26
|
-
if (typeof
|
|
27
|
-
t.push({ timestamp: e, ...
|
|
24
|
+
for (let m of r)
|
|
25
|
+
if (typeof m.content == "string")
|
|
26
|
+
t.push({ timestamp: e, ...m });
|
|
28
27
|
else {
|
|
29
|
-
const n =
|
|
28
|
+
const n = m.content?.filter((s) => s.type == "text").map((s) => s.text).join(`
|
|
30
29
|
|
|
31
30
|
`);
|
|
32
|
-
n && t.push({ timestamp: e, role:
|
|
31
|
+
n && t.push({ timestamp: e, role: m.role, content: n }), m.content.forEach((s) => {
|
|
33
32
|
if (s.type == "tool_use")
|
|
34
33
|
t.push({ timestamp: e, role: "tool", id: s.id, name: s.name, args: s.input, content: void 0 });
|
|
35
34
|
else if (s.type == "tool_result") {
|
|
36
|
-
const
|
|
37
|
-
|
|
35
|
+
const i = t.findLast((l) => l.id == s.tool_use_id);
|
|
36
|
+
i && (i[s.is_error ? "error" : "content"] = s.content);
|
|
38
37
|
}
|
|
39
38
|
});
|
|
40
39
|
}
|
|
@@ -55,78 +54,78 @@ class G extends E {
|
|
|
55
54
|
}
|
|
56
55
|
ask(r, e = {}) {
|
|
57
56
|
const t = new AbortController();
|
|
58
|
-
return Object.assign(new Promise(async (
|
|
57
|
+
return Object.assign(new Promise(async (m) => {
|
|
59
58
|
let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
60
|
-
const s = e.tools || this.ai.options.llm?.tools || [],
|
|
59
|
+
const s = e.tools || this.ai.options.llm?.tools || [], i = {
|
|
61
60
|
model: e.model || this.model,
|
|
62
61
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
63
62
|
system: e.system || this.ai.options.llm?.system || "",
|
|
64
63
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
65
|
-
tools: s.map((
|
|
66
|
-
name:
|
|
67
|
-
description:
|
|
64
|
+
tools: s.map((a) => ({
|
|
65
|
+
name: a.name,
|
|
66
|
+
description: a.description,
|
|
68
67
|
input_schema: {
|
|
69
68
|
type: "object",
|
|
70
|
-
properties:
|
|
71
|
-
required:
|
|
69
|
+
properties: a.args ? j(a.args, (o, c) => ({ ...c, required: void 0 })) : {},
|
|
70
|
+
required: a.args ? Object.entries(a.args).filter((o) => o[1].required).map((o) => o[0]) : []
|
|
72
71
|
},
|
|
73
72
|
fn: void 0
|
|
74
73
|
})),
|
|
75
74
|
messages: n,
|
|
76
75
|
stream: !!e.stream
|
|
77
76
|
};
|
|
78
|
-
let
|
|
77
|
+
let l, d = !0;
|
|
79
78
|
do {
|
|
80
|
-
if (
|
|
81
|
-
throw
|
|
79
|
+
if (l = await this.client.messages.create(i).catch((o) => {
|
|
80
|
+
throw o.message += `
|
|
82
81
|
|
|
83
82
|
Messages:
|
|
84
|
-
${JSON.stringify(n, null, 2)}`,
|
|
83
|
+
${JSON.stringify(n, null, 2)}`, o;
|
|
85
84
|
}), e.stream) {
|
|
86
|
-
|
|
85
|
+
d ? d = !1 : e.stream({ text: `
|
|
87
86
|
|
|
88
|
-
` }),
|
|
89
|
-
for await (const
|
|
87
|
+
` }), l.content = [];
|
|
88
|
+
for await (const o of l) {
|
|
90
89
|
if (t.signal.aborted) break;
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
else if (
|
|
94
|
-
if (
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
} else
|
|
98
|
-
else if (
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
} else if (
|
|
90
|
+
if (o.type === "content_block_start")
|
|
91
|
+
o.content_block.type === "text" ? l.content.push({ type: "text", text: "" }) : o.content_block.type === "tool_use" && l.content.push({ type: "tool_use", id: o.content_block.id, name: o.content_block.name, input: "" });
|
|
92
|
+
else if (o.type === "content_block_delta")
|
|
93
|
+
if (o.delta.type === "text_delta") {
|
|
94
|
+
const c = o.delta.text;
|
|
95
|
+
l.content.at(-1).text += c, e.stream({ text: c });
|
|
96
|
+
} else o.delta.type === "input_json_delta" && (l.content.at(-1).input += o.delta.partial_json);
|
|
97
|
+
else if (o.type === "content_block_stop") {
|
|
98
|
+
const c = l.content.at(-1);
|
|
99
|
+
c.input != null && (c.input = c.input ? w(c.input, {}) : {});
|
|
100
|
+
} else if (o.type === "message_stop")
|
|
102
101
|
break;
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
n.push({ role: "assistant", content:
|
|
108
|
-
const
|
|
109
|
-
const p = s.find(T("name",
|
|
110
|
-
if (e.stream && e.stream({ tool:
|
|
104
|
+
const a = l.content.filter((o) => o.type === "tool_use");
|
|
105
|
+
if (a.length && !t.signal.aborted) {
|
|
106
|
+
n.push({ role: "assistant", content: l.content });
|
|
107
|
+
const o = await Promise.all(a.map(async (c) => {
|
|
108
|
+
const p = s.find(T("name", c.name));
|
|
109
|
+
if (e.stream && e.stream({ tool: c.name }), !p) return { tool_use_id: c.id, is_error: !0, content: "Tool not found" };
|
|
111
110
|
try {
|
|
112
|
-
const u = await p.fn(
|
|
113
|
-
return { type: "tool_result", tool_use_id:
|
|
111
|
+
const u = await p.fn(c.input, e?.stream, this.ai);
|
|
112
|
+
return { type: "tool_result", tool_use_id: c.id, content: b(u) };
|
|
114
113
|
} catch (u) {
|
|
115
|
-
return { type: "tool_result", tool_use_id:
|
|
114
|
+
return { type: "tool_result", tool_use_id: c.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
|
|
116
115
|
}
|
|
117
116
|
}));
|
|
118
|
-
n.push({ role: "user", content:
|
|
117
|
+
n.push({ role: "user", content: o }), i.messages = n;
|
|
119
118
|
}
|
|
120
|
-
} while (!t.signal.aborted &&
|
|
121
|
-
n.push({ role: "assistant", content:
|
|
119
|
+
} while (!t.signal.aborted && l.content.some((a) => a.type === "tool_use"));
|
|
120
|
+
n.push({ role: "assistant", content: l.content.filter((a) => a.type == "text").map((a) => a.text).join(`
|
|
122
121
|
|
|
123
|
-
`) }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n),
|
|
122
|
+
`) }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), m(n.at(-1)?.content);
|
|
124
123
|
}), { abort: () => t.abort() });
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
126
|
class S extends E {
|
|
128
|
-
constructor(r, e, t,
|
|
129
|
-
super(), this.ai = r, this.host = e, this.token = t, this.model =
|
|
127
|
+
constructor(r, e, t, m) {
|
|
128
|
+
super(), this.ai = r, this.host = e, this.token = t, this.model = m, this.client = new U(M({
|
|
130
129
|
baseURL: e,
|
|
131
130
|
apiKey: t
|
|
132
131
|
}));
|
|
@@ -136,17 +135,17 @@ class S extends E {
|
|
|
136
135
|
for (let e = 0; e < r.length; e++) {
|
|
137
136
|
const t = r[e];
|
|
138
137
|
if (t.role === "assistant" && t.tool_calls) {
|
|
139
|
-
const
|
|
138
|
+
const m = t.tool_calls.map((n) => ({
|
|
140
139
|
role: "tool",
|
|
141
140
|
id: n.id,
|
|
142
141
|
name: n.function.name,
|
|
143
142
|
args: w(n.function.arguments, {}),
|
|
144
143
|
timestamp: t.timestamp
|
|
145
144
|
}));
|
|
146
|
-
r.splice(e, 1, ...
|
|
145
|
+
r.splice(e, 1, ...m), e += m.length - 1;
|
|
147
146
|
} else if (t.role === "tool" && t.content) {
|
|
148
|
-
const
|
|
149
|
-
|
|
147
|
+
const m = r.find((n) => t.tool_call_id == n.id);
|
|
148
|
+
m && (t.content.includes('"error":') ? m.error = t.content : m.content = t.content), r.splice(e, 1), e--;
|
|
150
149
|
}
|
|
151
150
|
r[e]?.timestamp || (r[e].timestamp = Date.now());
|
|
152
151
|
}
|
|
@@ -167,7 +166,7 @@ class S extends E {
|
|
|
167
166
|
content: t.error || t.content
|
|
168
167
|
});
|
|
169
168
|
else {
|
|
170
|
-
const { timestamp:
|
|
169
|
+
const { timestamp: m, ...n } = t;
|
|
171
170
|
e.push(n);
|
|
172
171
|
}
|
|
173
172
|
return e;
|
|
@@ -175,49 +174,49 @@ class S extends E {
|
|
|
175
174
|
}
|
|
176
175
|
ask(r, e = {}) {
|
|
177
176
|
const t = new AbortController();
|
|
178
|
-
return Object.assign(new Promise(async (
|
|
177
|
+
return Object.assign(new Promise(async (m, n) => {
|
|
179
178
|
e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
|
|
180
179
|
let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
181
|
-
const
|
|
180
|
+
const i = e.tools || this.ai.options.llm?.tools || [], l = {
|
|
182
181
|
model: e.model || this.model,
|
|
183
182
|
messages: s,
|
|
184
183
|
stream: !!e.stream,
|
|
185
184
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
186
185
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
187
|
-
tools:
|
|
186
|
+
tools: i.map((o) => ({
|
|
188
187
|
type: "function",
|
|
189
188
|
function: {
|
|
190
|
-
name:
|
|
191
|
-
description:
|
|
189
|
+
name: o.name,
|
|
190
|
+
description: o.description,
|
|
192
191
|
parameters: {
|
|
193
192
|
type: "object",
|
|
194
|
-
properties:
|
|
195
|
-
required:
|
|
193
|
+
properties: o.args ? j(o.args, (c, p) => ({ ...p, required: void 0 })) : {},
|
|
194
|
+
required: o.args ? Object.entries(o.args).filter((c) => c[1].required).map((c) => c[0]) : []
|
|
196
195
|
}
|
|
197
196
|
}
|
|
198
197
|
}))
|
|
199
198
|
};
|
|
200
|
-
let
|
|
199
|
+
let d, a = !0;
|
|
201
200
|
do {
|
|
202
|
-
if (
|
|
203
|
-
throw
|
|
201
|
+
if (d = await this.client.chat.completions.create(l).catch((c) => {
|
|
202
|
+
throw c.message += `
|
|
204
203
|
|
|
205
204
|
Messages:
|
|
206
|
-
${JSON.stringify(s, null, 2)}`,
|
|
205
|
+
${JSON.stringify(s, null, 2)}`, c;
|
|
207
206
|
}), e.stream) {
|
|
208
|
-
|
|
207
|
+
a ? a = !1 : e.stream({ text: `
|
|
209
208
|
|
|
210
|
-
` }),
|
|
211
|
-
for await (const
|
|
209
|
+
` }), d.choices = [{ message: { content: "", tool_calls: [] } }];
|
|
210
|
+
for await (const c of d) {
|
|
212
211
|
if (t.signal.aborted) break;
|
|
213
|
-
|
|
212
|
+
c.choices[0].delta.content && (d.choices[0].message.content += c.choices[0].delta.content, e.stream({ text: c.choices[0].delta.content })), c.choices[0].delta.tool_calls && (d.choices[0].message.tool_calls = c.choices[0].delta.tool_calls);
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
s.push(
|
|
219
|
-
const
|
|
220
|
-
const u =
|
|
215
|
+
const o = d.choices[0].message.tool_calls || [];
|
|
216
|
+
if (o.length && !t.signal.aborted) {
|
|
217
|
+
s.push(d.choices[0].message);
|
|
218
|
+
const c = await Promise.all(o.map(async (p) => {
|
|
219
|
+
const u = i?.find(T("name", p.function.name));
|
|
221
220
|
if (e.stream && e.stream({ tool: p.function.name }), !u) return { role: "tool", tool_call_id: p.id, content: '{"error": "Tool not found"}' };
|
|
222
221
|
try {
|
|
223
222
|
const f = w(p.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
|
|
@@ -226,10 +225,10 @@ ${JSON.stringify(s, null, 2)}`, d;
|
|
|
226
225
|
return { role: "tool", tool_call_id: p.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
227
226
|
}
|
|
228
227
|
}));
|
|
229
|
-
s.push(...
|
|
228
|
+
s.push(...c), l.messages = s;
|
|
230
229
|
}
|
|
231
|
-
} while (!t.signal.aborted &&
|
|
232
|
-
s.push({ role: "assistant", content:
|
|
230
|
+
} while (!t.signal.aborted && d.choices?.[0]?.message?.tool_calls?.length);
|
|
231
|
+
s.push({ role: "assistant", content: d.choices[0].message.content || "" }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), m(s.at(-1)?.content);
|
|
233
232
|
}), { abort: () => t.abort() });
|
|
234
233
|
}
|
|
235
234
|
}
|
|
@@ -250,22 +249,22 @@ class B {
|
|
|
250
249
|
ask(r, e = {}) {
|
|
251
250
|
const t = e.model || this.defaultModel;
|
|
252
251
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
253
|
-
let
|
|
252
|
+
let m = () => {
|
|
254
253
|
};
|
|
255
254
|
return Object.assign(new Promise(async (n) => {
|
|
256
255
|
if (e.history || (e.history = []), e.memory) {
|
|
257
256
|
e.system = (e.system || "") + `
|
|
258
257
|
You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
|
|
259
258
|
`;
|
|
260
|
-
const
|
|
261
|
-
const [
|
|
262
|
-
|
|
263
|
-
|
|
259
|
+
const i = async (d, a, o = 50) => {
|
|
260
|
+
const [c, p] = await Promise.all([
|
|
261
|
+
a ? this.embedding(a) : Promise.resolve(null),
|
|
262
|
+
d ? this.embedding(d) : Promise.resolve(null)
|
|
264
263
|
]);
|
|
265
|
-
return (e.memory || []).map((u) => ({ ...u, score:
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
` +
|
|
264
|
+
return (e.memory || []).map((u) => ({ ...u, score: c ? this.cosineSimilarity(u.embeddings[0], c[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: p ? this.cosineSimilarity(u.embeddings[1], p[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, o);
|
|
265
|
+
}, l = await i(r);
|
|
266
|
+
l.length && e.history.push({ role: "assistant", content: `Things I remembered:
|
|
267
|
+
` + l.map((d) => `${d.owner}: ${d.fact}`).join(`
|
|
269
268
|
`) }), e.tools = [...e.tools || [], {
|
|
270
269
|
name: "read_memory",
|
|
271
270
|
description: "Check your long-term memory for more information",
|
|
@@ -274,32 +273,32 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
274
273
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
275
274
|
limit: { type: "number", description: "Result limit, default 5" }
|
|
276
275
|
},
|
|
277
|
-
fn: (
|
|
278
|
-
if (!
|
|
279
|
-
return
|
|
276
|
+
fn: (d) => {
|
|
277
|
+
if (!d.subject && !d.query) throw new Error("Either a subject or query argument is required");
|
|
278
|
+
return i(d.query, d.subject, d.limit || 5);
|
|
280
279
|
}
|
|
281
280
|
}];
|
|
282
281
|
}
|
|
283
282
|
const s = await this.models[t].ask(r, e);
|
|
284
283
|
if (e.memory) {
|
|
285
|
-
const
|
|
286
|
-
|
|
284
|
+
const i = e.history?.findIndex((l) => l.role == "assistant" && l.content.startsWith("Things I remembered:"));
|
|
285
|
+
i != null && i >= 0 && e.history?.splice(i, 1);
|
|
287
286
|
}
|
|
288
287
|
if (e.compress || e.memory) {
|
|
289
|
-
let
|
|
288
|
+
let i = null;
|
|
290
289
|
if (e.compress)
|
|
291
|
-
|
|
290
|
+
i = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...i.history);
|
|
292
291
|
else {
|
|
293
|
-
const
|
|
294
|
-
|
|
292
|
+
const l = e.history?.findLastIndex((d) => d.role == "user") ?? -1;
|
|
293
|
+
i = await this.ai.language.compressHistory(l != -1 ? e.history.slice(l) : e.history, 0, 0, e);
|
|
295
294
|
}
|
|
296
295
|
if (e.memory) {
|
|
297
|
-
const
|
|
298
|
-
e.memory.splice(0, e.memory.length, ...
|
|
296
|
+
const l = e.memory.filter((d) => !i.memory.some((a) => this.cosineSimilarity(d.embeddings[1], a.embeddings[1]) > 0.8)).concat(i.memory);
|
|
297
|
+
e.memory.splice(0, e.memory.length, ...l);
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
300
|
return n(s);
|
|
302
|
-
}), { abort:
|
|
301
|
+
}), { abort: m });
|
|
303
302
|
}
|
|
304
303
|
/**
|
|
305
304
|
* Compress chat history to reduce context size
|
|
@@ -309,24 +308,24 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
309
308
|
* @param {LLMRequest} options LLM options
|
|
310
309
|
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
311
310
|
*/
|
|
312
|
-
async compressHistory(r, e, t,
|
|
311
|
+
async compressHistory(r, e, t, m) {
|
|
313
312
|
if (this.estimateTokens(r) < e) return { history: r, memory: [] };
|
|
314
313
|
let n = 0, s = 0;
|
|
315
314
|
for (let u of r.toReversed())
|
|
316
315
|
if (s += this.estimateTokens(u.content), s < t) n++;
|
|
317
316
|
else break;
|
|
318
317
|
if (r.length <= n) return { history: r, memory: [] };
|
|
319
|
-
const
|
|
318
|
+
const i = r[0].role == "system" ? r[0] : null, l = n == 0 ? [] : r.slice(-n), d = (n == 0 ? r : r.slice(0, -n)).filter((u) => u.role === "assistant" || u.role === "user"), a = await this.json(d.map((u) => `${u.role}: ${u.content}`).join(`
|
|
320
319
|
|
|
321
320
|
`), "{summary: string, facts: [[subject, fact]]}", {
|
|
322
321
|
system: "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.",
|
|
323
|
-
model:
|
|
324
|
-
temperature:
|
|
325
|
-
}),
|
|
322
|
+
model: m?.model,
|
|
323
|
+
temperature: m?.temperature || 0.3
|
|
324
|
+
}), o = /* @__PURE__ */ new Date(), c = await Promise.all((a?.facts || [])?.map(async ([u, f]) => {
|
|
326
325
|
const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
|
|
327
|
-
return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp:
|
|
328
|
-
})), p = [{ role: "assistant", content: `Conversation Summary: ${
|
|
329
|
-
return
|
|
326
|
+
return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: o };
|
|
327
|
+
})), p = [{ role: "assistant", content: `Conversation Summary: ${a?.summary}`, timestamp: Date.now() }, ...l];
|
|
328
|
+
return i && p.splice(0, 0, i), { history: p, memory: c };
|
|
330
329
|
}
|
|
331
330
|
/**
|
|
332
331
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -336,10 +335,10 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
336
335
|
*/
|
|
337
336
|
cosineSimilarity(r, e) {
|
|
338
337
|
if (r.length !== e.length) throw new Error("Vectors must be same length");
|
|
339
|
-
let t = 0,
|
|
340
|
-
for (let
|
|
341
|
-
t += r[
|
|
342
|
-
const s = Math.sqrt(
|
|
338
|
+
let t = 0, m = 0, n = 0;
|
|
339
|
+
for (let i = 0; i < r.length; i++)
|
|
340
|
+
t += r[i] * e[i], m += r[i] * r[i], n += e[i] * e[i];
|
|
341
|
+
const s = Math.sqrt(m) * Math.sqrt(n);
|
|
343
342
|
return s === 0 ? 0 : t / s;
|
|
344
343
|
}
|
|
345
344
|
/**
|
|
@@ -350,25 +349,25 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
350
349
|
* @returns {string[]} Chunked strings
|
|
351
350
|
*/
|
|
352
351
|
chunk(r, e = 500, t = 50) {
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
return typeof
|
|
356
|
-
}) : [], s = (typeof r == "object" ?
|
|
357
|
-
`)).flatMap((
|
|
358
|
-
`]),
|
|
359
|
-
for (let
|
|
360
|
-
let
|
|
361
|
-
for (;
|
|
362
|
-
const
|
|
363
|
-
if (this.estimateTokens(
|
|
364
|
-
`)) > e &&
|
|
365
|
-
|
|
352
|
+
const m = (l, d = "") => l ? Object.entries(l).flatMap(([a, o]) => {
|
|
353
|
+
const c = d ? `${d}${isNaN(+a) ? `.${a}` : `[${a}]`}` : a;
|
|
354
|
+
return typeof o == "object" && !Array.isArray(o) ? m(o, c) : `${c}: ${Array.isArray(o) ? o.join(", ") : o}`;
|
|
355
|
+
}) : [], s = (typeof r == "object" ? m(r) : r.split(`
|
|
356
|
+
`)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
|
|
357
|
+
`]), i = [];
|
|
358
|
+
for (let l = 0; l < s.length; ) {
|
|
359
|
+
let d = "", a = l;
|
|
360
|
+
for (; a < s.length; ) {
|
|
361
|
+
const c = d + (d ? " " : "") + s[a];
|
|
362
|
+
if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
|
|
363
|
+
`)) > e && d) break;
|
|
364
|
+
d = c, a++;
|
|
366
365
|
}
|
|
367
|
-
const
|
|
366
|
+
const o = d.replace(/\s*\n\s*/g, `
|
|
368
367
|
`).trim();
|
|
369
|
-
|
|
368
|
+
o && i.push(o), l = Math.max(a - t, a === l ? l + 1 : a);
|
|
370
369
|
}
|
|
371
|
-
return
|
|
370
|
+
return i;
|
|
372
371
|
}
|
|
373
372
|
/**
|
|
374
373
|
* Create a vector representation of a string
|
|
@@ -377,39 +376,39 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
377
376
|
* @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
|
|
378
377
|
*/
|
|
379
378
|
embedding(r, e = {}) {
|
|
380
|
-
let { maxTokens: t = 500, overlapTokens:
|
|
379
|
+
let { maxTokens: t = 500, overlapTokens: m = 50 } = e, n = !1;
|
|
381
380
|
const s = () => {
|
|
382
381
|
n = !0;
|
|
383
|
-
},
|
|
384
|
-
if (n) return
|
|
385
|
-
const
|
|
382
|
+
}, i = (d) => new Promise((a, o) => {
|
|
383
|
+
if (n) return o(new Error("Aborted"));
|
|
384
|
+
const c = [
|
|
386
385
|
N(L(z(import.meta.url)), "embedder.js"),
|
|
387
386
|
this.ai.options.path,
|
|
388
387
|
this.ai.options?.embedder || "bge-small-en-v1.5"
|
|
389
|
-
], p = g("node",
|
|
390
|
-
p.stdin.write(
|
|
388
|
+
], p = g("node", c, { stdio: ["pipe", "pipe", "ignore"] });
|
|
389
|
+
p.stdin.write(d), p.stdin.end();
|
|
391
390
|
let u = "";
|
|
392
391
|
p.stdout.on("data", (f) => u += f.toString()), p.on("close", (f) => {
|
|
393
|
-
if (n) return
|
|
392
|
+
if (n) return o(new Error("Aborted"));
|
|
394
393
|
if (f === 0)
|
|
395
394
|
try {
|
|
396
395
|
const y = JSON.parse(u);
|
|
397
|
-
|
|
396
|
+
a(y.embedding);
|
|
398
397
|
} catch {
|
|
399
|
-
|
|
398
|
+
o(new Error("Failed to parse embedding output"));
|
|
400
399
|
}
|
|
401
400
|
else
|
|
402
|
-
|
|
403
|
-
}), p.on("error",
|
|
404
|
-
}),
|
|
405
|
-
const
|
|
406
|
-
for (let
|
|
407
|
-
const
|
|
408
|
-
|
|
401
|
+
o(new Error(`Embedder process exited with code ${f}`));
|
|
402
|
+
}), p.on("error", o);
|
|
403
|
+
}), l = (async () => {
|
|
404
|
+
const d = this.chunk(r, t, m), a = [];
|
|
405
|
+
for (let o = 0; o < d.length && !n; o++) {
|
|
406
|
+
const c = d[o], p = await i(c);
|
|
407
|
+
a.push({ index: o, embedding: p, text: c, tokens: this.estimateTokens(c) });
|
|
409
408
|
}
|
|
410
|
-
return
|
|
409
|
+
return a;
|
|
411
410
|
})();
|
|
412
|
-
return Object.assign(
|
|
411
|
+
return Object.assign(l, { abort: s });
|
|
413
412
|
}
|
|
414
413
|
/**
|
|
415
414
|
* Estimate variable as tokens
|
|
@@ -428,8 +427,8 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
428
427
|
*/
|
|
429
428
|
fuzzyMatch(r, ...e) {
|
|
430
429
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
431
|
-
const t = (s,
|
|
432
|
-
return { avg: n.reduce((s,
|
|
430
|
+
const t = (s, i = 10) => s.toLowerCase().split("").map((l, d) => l.charCodeAt(0) * (d + 1) % i / i).slice(0, i), m = t(r), n = e.map((s) => t(s)).map((s) => this.cosineSimilarity(m, s));
|
|
431
|
+
return { avg: n.reduce((s, i) => s + i, 0) / n.length, max: Math.max(...n), similarities: n };
|
|
433
432
|
}
|
|
434
433
|
/**
|
|
435
434
|
* Ask a question with JSON response
|
|
@@ -439,13 +438,13 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
439
438
|
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
440
439
|
*/
|
|
441
440
|
async json(r, e, t) {
|
|
442
|
-
let
|
|
441
|
+
let m = await this.ask(r, { ...t, system: (t?.system ? `${t.system}
|
|
443
442
|
` : "") + `Only respond using a JSON code block matching this schema:
|
|
444
443
|
\`\`\`json
|
|
445
444
|
${e}
|
|
446
445
|
\`\`\`` });
|
|
447
|
-
if (!
|
|
448
|
-
const n = /```(?:.+)?\s*([\s\S]*?)```/.exec(
|
|
446
|
+
if (!m) return {};
|
|
447
|
+
const n = /```(?:.+)?\s*([\s\S]*?)```/.exec(m), s = n ? n[1].trim() : m;
|
|
449
448
|
return w(s, {});
|
|
450
449
|
}
|
|
451
450
|
/**
|
|
@@ -483,96 +482,98 @@ print(json.dumps(segments))
|
|
|
483
482
|
whisperModel;
|
|
484
483
|
runAsr(r, e = {}) {
|
|
485
484
|
let t;
|
|
486
|
-
const
|
|
487
|
-
this.downloadAsrModel(e.model).then((
|
|
488
|
-
let
|
|
489
|
-
const
|
|
490
|
-
t = g(this.ai.options.whisper,
|
|
491
|
-
if (
|
|
485
|
+
const m = new Promise((n, s) => {
|
|
486
|
+
this.downloadAsrModel(e.model).then((i) => {
|
|
487
|
+
let l = "";
|
|
488
|
+
const d = [e.diarization ? "-owts" : "-nt", "-m", i, "-f", r];
|
|
489
|
+
console.log(this.ai.options.whisper + " " + d.join(" ")), t = g(this.ai.options.whisper, d, { stdio: ["ignore", "pipe", "ignore"] }), t.on("error", (a) => s(a)), t.stdout.on("data", (a) => l += a.toString()), t.on("close", (a) => {
|
|
490
|
+
if (a === 0)
|
|
492
491
|
if (e.diarization)
|
|
493
492
|
try {
|
|
494
|
-
n(JSON.parse(
|
|
493
|
+
n(JSON.parse(l));
|
|
495
494
|
} catch {
|
|
496
495
|
s(new Error("Failed to parse whisper JSON"));
|
|
497
496
|
}
|
|
498
497
|
else
|
|
499
|
-
n(
|
|
498
|
+
n(l.trim() || null);
|
|
500
499
|
else
|
|
501
|
-
s(new Error(`Exit code ${
|
|
500
|
+
s(new Error(`Exit code ${a}`));
|
|
502
501
|
});
|
|
503
502
|
});
|
|
504
503
|
});
|
|
505
|
-
return Object.assign(
|
|
504
|
+
return Object.assign(m, { abort: () => t?.kill("SIGTERM") });
|
|
506
505
|
}
|
|
507
506
|
runDiarization(r) {
|
|
508
507
|
let e = !1, t = () => {
|
|
509
508
|
e = !0;
|
|
510
509
|
};
|
|
511
|
-
const
|
|
512
|
-
const
|
|
513
|
-
|
|
510
|
+
const m = (s) => new Promise((i) => {
|
|
511
|
+
const l = g(s, ["-c", "import pyannote.audio"]);
|
|
512
|
+
l.on("close", (d) => i(d === 0)), l.on("error", () => i(!1));
|
|
514
513
|
}), n = Promise.all([
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
]).then((async ([s,
|
|
514
|
+
m("python"),
|
|
515
|
+
m("python3")
|
|
516
|
+
]).then((async ([s, i]) => {
|
|
518
517
|
if (e) return;
|
|
519
|
-
if (!s && !
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if (u === 0)
|
|
518
|
+
if (!s && !i) throw new Error("Pyannote is not installed: pip install pyannote.audio");
|
|
519
|
+
const l = i ? "python3" : "python";
|
|
520
|
+
return new Promise((d, a) => {
|
|
521
|
+
if (e) return;
|
|
522
|
+
let o = "";
|
|
523
|
+
const c = g(l, ["-c", this.pyannote, r]);
|
|
524
|
+
c.stdout.on("data", (p) => o += p.toString()), c.stderr.on("data", (p) => console.error(p.toString())), c.on("close", (p) => {
|
|
525
|
+
if (p === 0)
|
|
528
526
|
try {
|
|
529
|
-
|
|
527
|
+
d(JSON.parse(o));
|
|
530
528
|
} catch {
|
|
531
|
-
|
|
529
|
+
a(new Error("Failed to parse diarization output"));
|
|
532
530
|
}
|
|
533
531
|
else
|
|
534
|
-
|
|
535
|
-
}),
|
|
536
|
-
}).finally(() => {
|
|
537
|
-
a && W(_.dirname(a), { recursive: !0, force: !0 });
|
|
532
|
+
a(new Error(`Python process exited with code ${p}`));
|
|
533
|
+
}), c.on("error", a), t = () => c.kill("SIGTERM");
|
|
538
534
|
});
|
|
539
535
|
}));
|
|
540
536
|
return Object.assign(n, { abort: t });
|
|
541
537
|
}
|
|
542
538
|
combineSpeakerTranscript(r, e) {
|
|
543
539
|
const t = /* @__PURE__ */ new Map();
|
|
544
|
-
let
|
|
545
|
-
e.forEach((
|
|
546
|
-
t.has(
|
|
540
|
+
let m = 0;
|
|
541
|
+
e.forEach((l) => {
|
|
542
|
+
t.has(l.speaker) || t.set(l.speaker, ++m);
|
|
547
543
|
});
|
|
548
544
|
const n = [];
|
|
549
|
-
let s = -1,
|
|
550
|
-
return r.transcription.forEach((
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
}),
|
|
545
|
+
let s = -1, i = "";
|
|
546
|
+
return r.transcription.forEach((l) => {
|
|
547
|
+
const d = l.offsets.from / 1e3, a = e.find((c) => d >= c.start && d <= c.end), o = a ? t.get(a.speaker) : 1;
|
|
548
|
+
o !== s ? (i && n.push(`[Speaker ${s}]: ${i.trim()}`), s = o, i = l.text) : i += " " + l.text;
|
|
549
|
+
}), i && n.push(`[Speaker ${s}]: ${i.trim()}`), n.join(`
|
|
554
550
|
`);
|
|
555
551
|
}
|
|
556
552
|
asr(r, e = {}) {
|
|
557
553
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
558
|
-
const t =
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
554
|
+
const t = x(J(x(P(), "audio-")), "converted.wav");
|
|
555
|
+
C(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
|
|
556
|
+
const m = () => W(_.dirname(t), { recursive: !0, force: !0 }).catch(() => {
|
|
557
|
+
}), n = this.runAsr(t, { model: e.model, diarization: !!e.diarization }), s = e.diarization ? this.runDiarization(t) : Promise.resolve(null);
|
|
558
|
+
let i = !1, l = () => {
|
|
559
|
+
i = !0, n.abort(), s?.abort?.(), m();
|
|
560
|
+
};
|
|
561
|
+
const d = Promise.all([n, s]).then(async ([a, o]) => {
|
|
562
|
+
if (i || !e.diarization) return a;
|
|
563
|
+
if (a = this.combineSpeakerTranscript(a, o), !i && e.diarization === "id") {
|
|
563
564
|
if (!this.ai.language.defaultModel) throw new Error("Configure an LLM for advanced ASR speaker detection");
|
|
564
|
-
let
|
|
565
|
-
|
|
566
|
-
const
|
|
565
|
+
let c = this.ai.language.chunk(a, 500, 0);
|
|
566
|
+
c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
|
|
567
|
+
const p = await this.ai.language.json(c.join(`
|
|
567
568
|
`), '{1: "Detected Name", 2: "Second Name"}', {
|
|
568
569
|
system: "Use the following transcript to identify speakers. Only identify speakers you are positive about, dont mention speakers you are unsure about in your response",
|
|
569
570
|
temperature: 0.1
|
|
570
571
|
});
|
|
571
|
-
Object.entries(
|
|
572
|
+
Object.entries(p).forEach(([u, f]) => a = a.replaceAll(`[Speaker ${u}]`, `[${f}]`));
|
|
572
573
|
}
|
|
573
|
-
return
|
|
574
|
-
});
|
|
575
|
-
return Object.assign(
|
|
574
|
+
return a;
|
|
575
|
+
}).finally(() => m());
|
|
576
|
+
return Object.assign(d, { abort: l });
|
|
576
577
|
}
|
|
577
578
|
async downloadAsrModel(r = this.whisperModel) {
|
|
578
579
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
@@ -592,15 +593,15 @@ class V {
|
|
|
592
593
|
*/
|
|
593
594
|
ocr(r) {
|
|
594
595
|
let e;
|
|
595
|
-
const t = new Promise(async (
|
|
596
|
+
const t = new Promise(async (m) => {
|
|
596
597
|
e = await D(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
597
598
|
const { data: n } = await e.recognize(r);
|
|
598
|
-
await e.terminate(),
|
|
599
|
+
await e.terminate(), m(n.text.trim() || null);
|
|
599
600
|
});
|
|
600
601
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
601
602
|
}
|
|
602
603
|
}
|
|
603
|
-
class
|
|
604
|
+
class de {
|
|
604
605
|
constructor(r) {
|
|
605
606
|
this.options = r, r.path || (r.path = $.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new K(this), this.language = new B(this), this.vision = new V(this);
|
|
606
607
|
}
|
|
@@ -616,12 +617,12 @@ const Y = {
|
|
|
616
617
|
description: "Use the command line interface, returns any output",
|
|
617
618
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
618
619
|
fn: (h) => F`${h.command}`
|
|
619
|
-
},
|
|
620
|
+
}, ue = {
|
|
620
621
|
name: "get_datetime",
|
|
621
622
|
description: "Get current UTC date / time",
|
|
622
623
|
args: {},
|
|
623
624
|
fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
|
|
624
|
-
},
|
|
625
|
+
}, pe = {
|
|
625
626
|
name: "exec",
|
|
626
627
|
description: "Run code/scripts",
|
|
627
628
|
args: {
|
|
@@ -642,7 +643,7 @@ const Y = {
|
|
|
642
643
|
return { error: t?.message || t.toString() };
|
|
643
644
|
}
|
|
644
645
|
}
|
|
645
|
-
},
|
|
646
|
+
}, he = {
|
|
646
647
|
name: "fetch",
|
|
647
648
|
description: "Make HTTP request to URL",
|
|
648
649
|
args: {
|
|
@@ -669,7 +670,7 @@ const Y = {
|
|
|
669
670
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
670
671
|
},
|
|
671
672
|
fn: async (h) => ({ result: H`python -c "${h.code}"` })
|
|
672
|
-
},
|
|
673
|
+
}, fe = {
|
|
673
674
|
name: "read_webpage",
|
|
674
675
|
description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
|
|
675
676
|
args: {
|
|
@@ -685,18 +686,18 @@ const Y = {
|
|
|
685
686
|
title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
|
|
686
687
|
description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
|
|
687
688
|
};
|
|
688
|
-
let
|
|
689
|
+
let m = "";
|
|
689
690
|
const n = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
|
|
690
691
|
for (const s of n) {
|
|
691
|
-
const
|
|
692
|
-
if (
|
|
693
|
-
|
|
692
|
+
const i = e(s).first();
|
|
693
|
+
if (i.length && i.text().trim().length > 200) {
|
|
694
|
+
m = i.text();
|
|
694
695
|
break;
|
|
695
696
|
}
|
|
696
697
|
}
|
|
697
|
-
return
|
|
698
|
+
return m || (m = e("body").text()), m = m.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: m, focus: h.focus };
|
|
698
699
|
}
|
|
699
|
-
},
|
|
700
|
+
}, ye = {
|
|
700
701
|
name: "web_search",
|
|
701
702
|
description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
|
|
702
703
|
args: {
|
|
@@ -708,28 +709,28 @@ const Y = {
|
|
|
708
709
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
709
710
|
}).then((n) => n.text());
|
|
710
711
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
711
|
-
const
|
|
712
|
+
const m = new v();
|
|
712
713
|
for (; (e = t.exec(r)) !== null; ) {
|
|
713
714
|
let n = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
714
|
-
if (n && (n = decodeURIComponent(n)), n &&
|
|
715
|
+
if (n && (n = decodeURIComponent(n)), n && m.add(n), m.size >= (h.length || 5)) break;
|
|
715
716
|
}
|
|
716
|
-
return
|
|
717
|
+
return m;
|
|
717
718
|
}
|
|
718
719
|
};
|
|
719
720
|
export {
|
|
720
|
-
|
|
721
|
+
de as Ai,
|
|
721
722
|
G as Anthropic,
|
|
722
723
|
K as Audio,
|
|
723
724
|
Y as CliTool,
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
725
|
+
ue as DateTimeTool,
|
|
726
|
+
pe as ExecTool,
|
|
727
|
+
he as FetchTool,
|
|
727
728
|
Q as JSTool,
|
|
728
729
|
E as LLMProvider,
|
|
729
730
|
S as OpenAi,
|
|
730
731
|
X as PythonTool,
|
|
731
|
-
|
|
732
|
+
fe as ReadWebpageTool,
|
|
732
733
|
V as Vision,
|
|
733
|
-
|
|
734
|
+
ye as WebSearchTool
|
|
734
735
|
};
|
|
735
736
|
//# sourceMappingURL=index.mjs.map
|