@ztimson/ai-utils 0.8.1 → 0.8.3
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 +25 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +318 -311
- package/dist/index.mjs.map +1 -1
- package/dist/llm.d.ts +2 -7
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { tmpdir as
|
|
3
|
-
import { objectMap as
|
|
1
|
+
import * as z from "node:os";
|
|
2
|
+
import { tmpdir as U } from "node:os";
|
|
3
|
+
import { objectMap as A, JSONAttemptParse as _, findByProp as O, JSONSanitize as S, clean as W, Http as N, consoleInterceptor as L, fn as C, ASet as J } from "@ztimson/utils";
|
|
4
4
|
import { Anthropic as F } from "@anthropic-ai/sdk";
|
|
5
|
-
import { OpenAI as
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { join as
|
|
5
|
+
import { OpenAI as H } from "openai";
|
|
6
|
+
import { fileURLToPath as D } from "url";
|
|
7
|
+
import { join as G, dirname as I } from "path";
|
|
8
8
|
import { spawn as b, execSync as B } from "node:child_process";
|
|
9
9
|
import { mkdtempSync as K } from "node:fs";
|
|
10
10
|
import w from "node:fs/promises";
|
|
11
|
-
import * as
|
|
12
|
-
import
|
|
11
|
+
import * as v from "node:path";
|
|
12
|
+
import M, { join as P } from "node:path";
|
|
13
13
|
import { createWorker as V } from "tesseract.js";
|
|
14
14
|
import * as Y from "cheerio";
|
|
15
15
|
import { $ as Z, $Sync as Q } from "@ztimson/node-utils";
|
|
@@ -22,19 +22,19 @@ class X extends R {
|
|
|
22
22
|
client;
|
|
23
23
|
toStandard(r) {
|
|
24
24
|
const e = Date.now(), t = [];
|
|
25
|
-
for (let
|
|
26
|
-
if (typeof
|
|
27
|
-
t.push({ timestamp: e, ...
|
|
25
|
+
for (let a of r)
|
|
26
|
+
if (typeof a.content == "string")
|
|
27
|
+
t.push({ timestamp: e, ...a });
|
|
28
28
|
else {
|
|
29
|
-
const
|
|
29
|
+
const s = a.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 c = t.findLast((
|
|
37
|
-
c && (c[
|
|
32
|
+
s && t.push({ timestamp: e, role: a.role, content: s }), a.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 c = t.findLast((u) => u.id == n.tool_use_id);
|
|
37
|
+
c && (c[n.is_error ? "error" : "content"] = n.content);
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -55,80 +55,80 @@ class X extends R {
|
|
|
55
55
|
}
|
|
56
56
|
ask(r, e = {}) {
|
|
57
57
|
const t = new AbortController();
|
|
58
|
-
return Object.assign(new Promise(async (
|
|
59
|
-
let
|
|
60
|
-
const
|
|
58
|
+
return Object.assign(new Promise(async (a) => {
|
|
59
|
+
let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
60
|
+
const n = e.tools || this.ai.options.llm?.tools || [], c = {
|
|
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((m) => ({
|
|
66
66
|
name: m.name,
|
|
67
67
|
description: m.description,
|
|
68
68
|
input_schema: {
|
|
69
69
|
type: "object",
|
|
70
|
-
properties: m.args ?
|
|
71
|
-
required: m.args ? Object.entries(m.args).filter((
|
|
70
|
+
properties: m.args ? A(m.args, (o, l) => ({ ...l, required: void 0 })) : {},
|
|
71
|
+
required: m.args ? Object.entries(m.args).filter((o) => o[1].required).map((o) => o[0]) : []
|
|
72
72
|
},
|
|
73
73
|
fn: void 0
|
|
74
74
|
})),
|
|
75
|
-
messages:
|
|
75
|
+
messages: s,
|
|
76
76
|
stream: !!e.stream
|
|
77
77
|
};
|
|
78
|
-
let
|
|
78
|
+
let u, i = !0;
|
|
79
79
|
do {
|
|
80
|
-
if (
|
|
81
|
-
throw
|
|
80
|
+
if (u = await this.client.messages.create(c).catch((o) => {
|
|
81
|
+
throw o.message += `
|
|
82
82
|
|
|
83
83
|
Messages:
|
|
84
|
-
${JSON.stringify(
|
|
84
|
+
${JSON.stringify(s, null, 2)}`, o;
|
|
85
85
|
}), e.stream) {
|
|
86
|
-
|
|
86
|
+
i ? i = !1 : e.stream({ text: `
|
|
87
87
|
|
|
88
|
-
` }),
|
|
89
|
-
for await (const
|
|
88
|
+
` }), u.content = [];
|
|
89
|
+
for await (const o of u) {
|
|
90
90
|
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 (
|
|
91
|
+
if (o.type === "content_block_start")
|
|
92
|
+
o.content_block.type === "text" ? u.content.push({ type: "text", text: "" }) : o.content_block.type === "tool_use" && u.content.push({ type: "tool_use", id: o.content_block.id, name: o.content_block.name, input: "" });
|
|
93
|
+
else if (o.type === "content_block_delta")
|
|
94
|
+
if (o.delta.type === "text_delta") {
|
|
95
|
+
const l = o.delta.text;
|
|
96
|
+
u.content.at(-1).text += l, e.stream({ text: l });
|
|
97
|
+
} else o.delta.type === "input_json_delta" && (u.content.at(-1).input += o.delta.partial_json);
|
|
98
|
+
else if (o.type === "content_block_stop") {
|
|
99
|
+
const l = u.content.at(-1);
|
|
100
|
+
l.input != null && (l.input = l.input ? _(l.input, {}) : {});
|
|
101
|
+
} else if (o.type === "message_stop")
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
const m =
|
|
105
|
+
const m = u.content.filter((o) => o.type === "tool_use");
|
|
106
106
|
if (m.length && !t.signal.aborted) {
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
if (e.stream && e.stream({ tool:
|
|
107
|
+
s.push({ role: "assistant", content: u.content });
|
|
108
|
+
const o = await Promise.all(m.map(async (l) => {
|
|
109
|
+
const d = n.find(O("name", l.name));
|
|
110
|
+
if (e.stream && e.stream({ tool: l.name }), !d) return { tool_use_id: l.id, is_error: !0, content: "Tool not found" };
|
|
111
111
|
try {
|
|
112
|
-
const
|
|
113
|
-
return { type: "tool_result", tool_use_id:
|
|
114
|
-
} catch (
|
|
115
|
-
return { type: "tool_result", tool_use_id:
|
|
112
|
+
const p = await d.fn(l.input, e?.stream, this.ai);
|
|
113
|
+
return { type: "tool_result", tool_use_id: l.id, content: S(p) };
|
|
114
|
+
} catch (p) {
|
|
115
|
+
return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
|
|
116
116
|
}
|
|
117
117
|
}));
|
|
118
|
-
|
|
118
|
+
s.push({ role: "user", content: o }), c.messages = s;
|
|
119
119
|
}
|
|
120
|
-
} while (!t.signal.aborted &&
|
|
121
|
-
|
|
120
|
+
} while (!t.signal.aborted && u.content.some((m) => m.type === "tool_use"));
|
|
121
|
+
s.push({ role: "assistant", content: u.content.filter((m) => m.type == "text").map((m) => m.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), a(s.at(-1)?.content);
|
|
124
124
|
}), { abort: () => t.abort() });
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
class
|
|
128
|
-
constructor(r, e, t,
|
|
129
|
-
super(), this.ai = r, this.host = e, this.token = t, this.model =
|
|
127
|
+
class q extends R {
|
|
128
|
+
constructor(r, e, t, a) {
|
|
129
|
+
super(), this.ai = r, this.host = e, this.token = t, this.model = a, this.client = new H(W({
|
|
130
130
|
baseURL: e,
|
|
131
|
-
apiKey: t
|
|
131
|
+
apiKey: t || e ? "ignored" : void 0
|
|
132
132
|
}));
|
|
133
133
|
}
|
|
134
134
|
client;
|
|
@@ -136,17 +136,17 @@ class A extends R {
|
|
|
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
|
|
139
|
+
const a = 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: _(s.function.arguments, {}),
|
|
144
144
|
timestamp: t.timestamp
|
|
145
145
|
}));
|
|
146
|
-
r.splice(e, 1, ...
|
|
146
|
+
r.splice(e, 1, ...a), e += a.length - 1;
|
|
147
147
|
} else if (t.role === "tool" && t.content) {
|
|
148
|
-
const
|
|
149
|
-
|
|
148
|
+
const a = r.find((s) => t.tool_call_id == s.id);
|
|
149
|
+
a && (t.content.includes('"error":') ? a.error = t.content : a.content = t.content), r.splice(e, 1), e--;
|
|
150
150
|
}
|
|
151
151
|
r[e]?.timestamp || (r[e].timestamp = Date.now());
|
|
152
152
|
}
|
|
@@ -167,76 +167,76 @@ class A extends R {
|
|
|
167
167
|
content: t.error || t.content
|
|
168
168
|
});
|
|
169
169
|
else {
|
|
170
|
-
const { timestamp:
|
|
171
|
-
e.push(
|
|
170
|
+
const { timestamp: a, ...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 (
|
|
179
|
-
e.system && e.history?.[0]?.role != "system"
|
|
180
|
-
let
|
|
181
|
-
const c = e.tools || this.ai.options.llm?.tools || [],
|
|
178
|
+
return Object.assign(new Promise(async (a, s) => {
|
|
179
|
+
e.system && (e.history?.[0]?.role != "system" ? e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() }) : e.history[0].content = e.system);
|
|
180
|
+
let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
181
|
+
const c = e.tools || this.ai.options.llm?.tools || [], u = {
|
|
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,
|
|
187
|
-
tools: c.map((
|
|
187
|
+
tools: c.map((o) => ({
|
|
188
188
|
type: "function",
|
|
189
189
|
function: {
|
|
190
|
-
name:
|
|
191
|
-
description:
|
|
190
|
+
name: o.name,
|
|
191
|
+
description: o.description,
|
|
192
192
|
parameters: {
|
|
193
193
|
type: "object",
|
|
194
|
-
properties:
|
|
195
|
-
required:
|
|
194
|
+
properties: o.args ? A(o.args, (l, d) => ({ ...d, required: void 0 })) : {},
|
|
195
|
+
required: o.args ? Object.entries(o.args).filter((l) => l[1].required).map((l) => l[0]) : []
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}))
|
|
199
199
|
};
|
|
200
|
-
let
|
|
200
|
+
let i, m = !0;
|
|
201
201
|
do {
|
|
202
|
-
if (
|
|
203
|
-
throw
|
|
202
|
+
if (i = await this.client.chat.completions.create(u).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
|
m ? m = !1 : e.stream({ text: `
|
|
209
209
|
|
|
210
|
-
` }),
|
|
211
|
-
for await (const
|
|
210
|
+
` }), i.choices = [{ message: { content: "", tool_calls: [] } }];
|
|
211
|
+
for await (const l of i) {
|
|
212
212
|
if (t.signal.aborted) break;
|
|
213
|
-
|
|
213
|
+
l.choices[0].delta.content && (i.choices[0].message.content += l.choices[0].delta.content, e.stream({ text: l.choices[0].delta.content })), l.choices[0].delta.tool_calls && (i.choices[0].message.tool_calls = l.choices[0].delta.tool_calls);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
if (e.stream && e.stream({ tool:
|
|
216
|
+
const o = i.choices[0].message.tool_calls || [];
|
|
217
|
+
if (o.length && !t.signal.aborted) {
|
|
218
|
+
n.push(i.choices[0].message);
|
|
219
|
+
const l = await Promise.all(o.map(async (d) => {
|
|
220
|
+
const p = c?.find(O("name", d.function.name));
|
|
221
|
+
if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
|
|
222
222
|
try {
|
|
223
|
-
const f = _(
|
|
224
|
-
return { role: "tool", tool_call_id:
|
|
223
|
+
const f = _(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
|
|
224
|
+
return { role: "tool", tool_call_id: d.id, content: S(g) };
|
|
225
225
|
} catch (f) {
|
|
226
|
-
return { role: "tool", tool_call_id:
|
|
226
|
+
return { role: "tool", tool_call_id: d.id, content: S({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
227
227
|
}
|
|
228
228
|
}));
|
|
229
|
-
|
|
229
|
+
n.push(...l), u.messages = n;
|
|
230
230
|
}
|
|
231
|
-
} while (!t.signal.aborted &&
|
|
232
|
-
|
|
231
|
+
} while (!t.signal.aborted && i.choices?.[0]?.message?.tool_calls?.length);
|
|
232
|
+
n.push({ role: "assistant", content: i.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), a(n.at(-1)?.content);
|
|
233
233
|
}), { abort: () => t.abort() });
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
class ee {
|
|
237
237
|
constructor(r) {
|
|
238
238
|
this.ai = r, r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
|
|
239
|
-
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new X(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new
|
|
239
|
+
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new X(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new q(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new q(this.ai, t.host || null, t.token, e));
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
defaultModel;
|
|
@@ -248,66 +248,80 @@ class ee {
|
|
|
248
248
|
* @returns {{abort: () => void, response: Promise<string>}} Function to abort response and chat history
|
|
249
249
|
*/
|
|
250
250
|
ask(r, e = {}) {
|
|
251
|
+
e = {
|
|
252
|
+
system: "",
|
|
253
|
+
temperature: 0.8,
|
|
254
|
+
...this.ai.options.llm,
|
|
255
|
+
models: void 0,
|
|
256
|
+
history: [],
|
|
257
|
+
...e
|
|
258
|
+
};
|
|
251
259
|
const t = e.model || this.defaultModel;
|
|
252
260
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
253
|
-
let
|
|
261
|
+
let a = () => {
|
|
254
262
|
};
|
|
255
|
-
return Object.assign(new Promise(async (
|
|
263
|
+
return Object.assign(new Promise(async (s) => {
|
|
256
264
|
if (e.history || (e.history = []), e.memory) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
`;
|
|
260
|
-
const c = async (o, m, i = 50) => {
|
|
261
|
-
const [u, p] = await Promise.all([
|
|
265
|
+
const c = async (i, m, o = 10) => {
|
|
266
|
+
const [l, d] = await Promise.all([
|
|
262
267
|
m ? this.embedding(m) : Promise.resolve(null),
|
|
263
|
-
|
|
268
|
+
i ? this.embedding(i) : Promise.resolve(null)
|
|
264
269
|
]);
|
|
265
|
-
return (e.memory || []).map((
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
return (e.memory || []).map((p) => {
|
|
271
|
+
const f = (l ? this.cosineSimilarity(p.embeddings[0], l[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
|
|
272
|
+
return { ...p, score: f };
|
|
273
|
+
}).toSorted((p, f) => p.score - f.score).slice(0, o);
|
|
274
|
+
};
|
|
275
|
+
e.system += `
|
|
276
|
+
You have RAG memory and will be given the top_k closest memories regarding the users query. Save anything new you have learned worth remembering from the user message using the remember tool and feel free to recall memories manually.
|
|
277
|
+
`;
|
|
278
|
+
const u = await c(r);
|
|
279
|
+
u.length && e.history.push({ role: "tool", name: "recall", id: "auto_recall_" + Math.random().toString(), args: {}, content: `Things I remembered:
|
|
280
|
+
` + u.map((i) => `${i.owner}: ${i.fact}`).join(`
|
|
281
|
+
`) }), e.tools = [{
|
|
282
|
+
name: "recall",
|
|
283
|
+
description: "Recall the closest memories you have regarding a query using RAG",
|
|
272
284
|
args: {
|
|
273
285
|
subject: { type: "string", description: "Find information by a subject topic, can be used with or without query argument" },
|
|
274
286
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
275
|
-
|
|
287
|
+
topK: { type: "number", description: "Result limit, default 5" }
|
|
276
288
|
},
|
|
277
|
-
fn: (
|
|
278
|
-
if (!
|
|
279
|
-
return c(
|
|
289
|
+
fn: (i) => {
|
|
290
|
+
if (!i.subject && !i.query) throw new Error("Either a subject or query argument is required");
|
|
291
|
+
return c(i.query, i.subject, i.topK);
|
|
280
292
|
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
}, {
|
|
294
|
+
name: "remember",
|
|
295
|
+
description: "Store important facts user shares for future recall",
|
|
296
|
+
args: {
|
|
297
|
+
owner: { type: "string", description: "Subject/person this fact is about" },
|
|
298
|
+
fact: { type: "string", description: "The information to remember" }
|
|
299
|
+
},
|
|
300
|
+
fn: async (i) => {
|
|
301
|
+
if (!e.memory) return;
|
|
302
|
+
const m = await Promise.all([
|
|
303
|
+
this.embedding(i.owner),
|
|
304
|
+
this.embedding(`${i.owner}: ${i.fact}`)
|
|
305
|
+
]), o = { owner: i.owner, fact: i.fact, embeddings: [m[0][0].embedding, m[1][0].embedding] };
|
|
306
|
+
return e.memory.splice(0, e.memory.length, ...e.memory.filter((l) => this.cosineSimilarity(o.embeddings[0], l.embeddings[0]) < 0.9 && this.cosineSimilarity(o.embeddings[1], l.embeddings[1]) < 0.8), o), "Remembered!";
|
|
307
|
+
}
|
|
308
|
+
}, ...e.tools || []];
|
|
287
309
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
else {
|
|
293
|
-
const a = e.history?.findLastIndex((o) => o.role == "user") ?? -1;
|
|
294
|
-
c = await this.ai.language.compressHistory(a != -1 ? e.history.slice(a) : e.history, 0, 0, e);
|
|
295
|
-
}
|
|
296
|
-
if (e.memory) {
|
|
297
|
-
const a = e.memory.filter((o) => !c.memory.some((m) => this.cosineSimilarity(o.embeddings[1], m.embeddings[1]) > 0.8)).concat(c.memory);
|
|
298
|
-
e.memory.splice(0, e.memory.length, ...a);
|
|
299
|
-
}
|
|
310
|
+
const n = await this.models[t].ask(r, e);
|
|
311
|
+
if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((c) => c.role != "tool" || c.name != "recall" && c.name != "remember")), e.compress) {
|
|
312
|
+
const c = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
|
|
313
|
+
e.history.splice(0, e.history.length, ...c);
|
|
300
314
|
}
|
|
301
|
-
return n
|
|
302
|
-
}), { abort:
|
|
315
|
+
return s(n);
|
|
316
|
+
}), { abort: a });
|
|
303
317
|
}
|
|
304
318
|
async code(r, e) {
|
|
305
319
|
const t = await this.ask(r, { ...e, system: [
|
|
306
320
|
e?.system,
|
|
307
321
|
"Return your response in a code block"
|
|
308
|
-
].filter((
|
|
309
|
-
`) }),
|
|
310
|
-
return
|
|
322
|
+
].filter((s) => !!s).join(`
|
|
323
|
+
`) }), a = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
|
|
324
|
+
return a ? a[1].trim() : null;
|
|
311
325
|
}
|
|
312
326
|
/**
|
|
313
327
|
* Compress chat history to reduce context size
|
|
@@ -317,24 +331,17 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
317
331
|
* @param {LLMRequest} options LLM options
|
|
318
332
|
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
319
333
|
*/
|
|
320
|
-
async compressHistory(r, e, t,
|
|
321
|
-
if (this.estimateTokens(r) < e) return
|
|
322
|
-
let
|
|
334
|
+
async compressHistory(r, e, t, a) {
|
|
335
|
+
if (this.estimateTokens(r) < e) return r;
|
|
336
|
+
let s = 0, n = 0;
|
|
323
337
|
for (let d of r.toReversed())
|
|
324
|
-
if (
|
|
338
|
+
if (n += this.estimateTokens(d.content), n < t) s++;
|
|
325
339
|
else break;
|
|
326
|
-
if (r.length <=
|
|
327
|
-
const c = r[0].role == "system" ? r[0] : null,
|
|
340
|
+
if (r.length <= s) return r;
|
|
341
|
+
const c = r[0].role == "system" ? r[0] : null, u = s == 0 ? [] : r.slice(-s), i = (s == 0 ? r : r.slice(0, -s)).filter((d) => d.role === "assistant" || d.role === "user"), m = await this.summarize(i.map((d) => `[${d.role}]: ${d.content}`).join(`
|
|
328
342
|
|
|
329
|
-
`), "
|
|
330
|
-
|
|
331
|
-
model: l?.model,
|
|
332
|
-
temperature: l?.temperature || 0.3
|
|
333
|
-
}), i = /* @__PURE__ */ new Date(), u = await Promise.all((m?.facts || [])?.map(async ([d, f]) => {
|
|
334
|
-
const y = await Promise.all([this.embedding(d), this.embedding(`${d}: ${f}`)]);
|
|
335
|
-
return { owner: d, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: i };
|
|
336
|
-
})), p = [{ role: "assistant", content: `Conversation Summary: ${m?.summary}`, timestamp: Date.now() }, ...a];
|
|
337
|
-
return c && p.splice(0, 0, c), { history: p, memory: u };
|
|
343
|
+
`), 500, a), o = Date.now(), l = [{ role: "tool", name: "summary", id: "summary_" + o, args: {}, content: `Conversation Summary: ${m?.summary}`, timestamp: o }, ...u];
|
|
344
|
+
return c && l.splice(0, 0, c), l;
|
|
338
345
|
}
|
|
339
346
|
/**
|
|
340
347
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -344,11 +351,11 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
344
351
|
*/
|
|
345
352
|
cosineSimilarity(r, e) {
|
|
346
353
|
if (r.length !== e.length) throw new Error("Vectors must be same length");
|
|
347
|
-
let t = 0,
|
|
354
|
+
let t = 0, a = 0, s = 0;
|
|
348
355
|
for (let c = 0; c < r.length; c++)
|
|
349
|
-
t += r[c] * e[c],
|
|
350
|
-
const
|
|
351
|
-
return
|
|
356
|
+
t += r[c] * e[c], a += r[c] * r[c], s += e[c] * e[c];
|
|
357
|
+
const n = Math.sqrt(a) * Math.sqrt(s);
|
|
358
|
+
return n === 0 ? 0 : t / n;
|
|
352
359
|
}
|
|
353
360
|
/**
|
|
354
361
|
* Chunk text into parts for AI digestion
|
|
@@ -358,23 +365,23 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
358
365
|
* @returns {string[]} Chunked strings
|
|
359
366
|
*/
|
|
360
367
|
chunk(r, e = 500, t = 50) {
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
return typeof
|
|
364
|
-
}) : [],
|
|
365
|
-
`)).flatMap((
|
|
368
|
+
const a = (u, i = "") => u ? Object.entries(u).flatMap(([m, o]) => {
|
|
369
|
+
const l = i ? `${i}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
|
|
370
|
+
return typeof o == "object" && !Array.isArray(o) ? a(o, l) : `${l}: ${Array.isArray(o) ? o.join(", ") : o}`;
|
|
371
|
+
}) : [], n = (typeof r == "object" ? a(r) : r.toString().split(`
|
|
372
|
+
`)).flatMap((u) => [...u.split(/\s+/).filter(Boolean), `
|
|
366
373
|
`]), c = [];
|
|
367
|
-
for (let
|
|
368
|
-
let
|
|
369
|
-
for (; m <
|
|
370
|
-
const
|
|
371
|
-
if (this.estimateTokens(
|
|
372
|
-
`)) > e &&
|
|
373
|
-
|
|
374
|
+
for (let u = 0; u < n.length; ) {
|
|
375
|
+
let i = "", m = u;
|
|
376
|
+
for (; m < n.length; ) {
|
|
377
|
+
const l = i + (i ? " " : "") + n[m];
|
|
378
|
+
if (this.estimateTokens(l.replace(/\s*\n\s*/g, `
|
|
379
|
+
`)) > e && i) break;
|
|
380
|
+
i = l, m++;
|
|
374
381
|
}
|
|
375
|
-
const
|
|
382
|
+
const o = i.replace(/\s*\n\s*/g, `
|
|
376
383
|
`).trim();
|
|
377
|
-
|
|
384
|
+
o && c.push(o), u = Math.max(m - t, m === u ? u + 1 : m);
|
|
378
385
|
}
|
|
379
386
|
return c;
|
|
380
387
|
}
|
|
@@ -385,39 +392,39 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
385
392
|
* @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
|
|
386
393
|
*/
|
|
387
394
|
embedding(r, e = {}) {
|
|
388
|
-
let { maxTokens: t = 500, overlapTokens:
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
}, c = (
|
|
392
|
-
if (
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
+
let { maxTokens: t = 500, overlapTokens: a = 50 } = e, s = !1;
|
|
396
|
+
const n = () => {
|
|
397
|
+
s = !0;
|
|
398
|
+
}, c = (i) => new Promise((m, o) => {
|
|
399
|
+
if (s) return o(new Error("Aborted"));
|
|
400
|
+
const l = [
|
|
401
|
+
G(I(D(import.meta.url)), "embedder.js"),
|
|
395
402
|
this.ai.options.path,
|
|
396
403
|
this.ai.options?.embedder || "bge-small-en-v1.5"
|
|
397
|
-
],
|
|
398
|
-
|
|
399
|
-
let
|
|
400
|
-
|
|
401
|
-
if (
|
|
404
|
+
], d = b("node", l, { stdio: ["pipe", "pipe", "ignore"] });
|
|
405
|
+
d.stdin.write(i), d.stdin.end();
|
|
406
|
+
let p = "";
|
|
407
|
+
d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
|
|
408
|
+
if (s) return o(new Error("Aborted"));
|
|
402
409
|
if (f === 0)
|
|
403
410
|
try {
|
|
404
|
-
const
|
|
405
|
-
m(
|
|
411
|
+
const g = JSON.parse(p);
|
|
412
|
+
m(g.embedding);
|
|
406
413
|
} catch {
|
|
407
|
-
|
|
414
|
+
o(new Error("Failed to parse embedding output"));
|
|
408
415
|
}
|
|
409
416
|
else
|
|
410
|
-
|
|
411
|
-
}),
|
|
412
|
-
}),
|
|
413
|
-
const
|
|
414
|
-
for (let
|
|
415
|
-
const
|
|
416
|
-
m.push({ index:
|
|
417
|
+
o(new Error(`Embedder process exited with code ${f}`));
|
|
418
|
+
}), d.on("error", o);
|
|
419
|
+
}), u = (async () => {
|
|
420
|
+
const i = this.chunk(r, t, a), m = [];
|
|
421
|
+
for (let o = 0; o < i.length && !s; o++) {
|
|
422
|
+
const l = i[o], d = await c(l);
|
|
423
|
+
m.push({ index: o, embedding: d, text: l, tokens: this.estimateTokens(l) });
|
|
417
424
|
}
|
|
418
425
|
return m;
|
|
419
426
|
})();
|
|
420
|
-
return Object.assign(
|
|
427
|
+
return Object.assign(u, { abort: n });
|
|
421
428
|
}
|
|
422
429
|
/**
|
|
423
430
|
* Estimate variable as tokens
|
|
@@ -436,8 +443,8 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
436
443
|
*/
|
|
437
444
|
fuzzyMatch(r, ...e) {
|
|
438
445
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
439
|
-
const t = (
|
|
440
|
-
return { avg:
|
|
446
|
+
const t = (n, c = 10) => n.toLowerCase().split("").map((u, i) => u.charCodeAt(0) * (i + 1) % c / c).slice(0, c), a = t(r), s = e.map((n) => t(n)).map((n) => this.cosineSimilarity(a, n));
|
|
447
|
+
return { avg: s.reduce((n, c) => n + c, 0) / s.length, max: Math.max(...s), similarities: s };
|
|
441
448
|
}
|
|
442
449
|
/**
|
|
443
450
|
* Ask a question with JSON response
|
|
@@ -447,15 +454,15 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
447
454
|
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
448
455
|
*/
|
|
449
456
|
async json(r, e, t) {
|
|
450
|
-
const
|
|
457
|
+
const a = await this.code(r, { ...t, system: [
|
|
451
458
|
t?.system,
|
|
452
459
|
`Only respond using JSON matching this schema:
|
|
453
460
|
\`\`\`json
|
|
454
461
|
${e}
|
|
455
462
|
\`\`\``
|
|
456
|
-
].filter((
|
|
463
|
+
].filter((s) => !!s).join(`
|
|
457
464
|
`) });
|
|
458
|
-
return
|
|
465
|
+
return a ? _(a, {}) : null;
|
|
459
466
|
}
|
|
460
467
|
/**
|
|
461
468
|
* Create a summary of some text
|
|
@@ -464,8 +471,8 @@ ${e}
|
|
|
464
471
|
* @param options LLM request options
|
|
465
472
|
* @returns {Promise<string>} Summary
|
|
466
473
|
*/
|
|
467
|
-
summarize(r, e, t) {
|
|
468
|
-
return this.ask(r, { system: `Generate
|
|
474
|
+
summarize(r, e = 500, t) {
|
|
475
|
+
return this.ask(r, { system: `Generate the shortest summary possible <= ${e} tokens. Output nothing else`, temperature: 0.3, ...t });
|
|
469
476
|
}
|
|
470
477
|
}
|
|
471
478
|
class te {
|
|
@@ -491,21 +498,21 @@ print(json.dumps(segments))
|
|
|
491
498
|
pyannote;
|
|
492
499
|
whisperModel;
|
|
493
500
|
async addPunctuation(r, e, t = 150) {
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
496
|
-
const c =
|
|
497
|
-
let
|
|
498
|
-
return
|
|
501
|
+
const a = (n) => {
|
|
502
|
+
if (n = n.toLowerCase().replace(/[^a-z]/g, ""), n.length <= 3) return 1;
|
|
503
|
+
const c = n.match(/[aeiouy]+/g);
|
|
504
|
+
let u = c ? c.length : 1;
|
|
505
|
+
return n.endsWith("e") && u--, Math.max(1, u);
|
|
499
506
|
};
|
|
500
|
-
let
|
|
501
|
-
return r.transcription.filter((
|
|
502
|
-
let
|
|
503
|
-
const
|
|
504
|
-
return !
|
|
505
|
-
}).forEach((
|
|
506
|
-
const c = /^[A-Z]/.test(
|
|
507
|
-
c &&
|
|
508
|
-
}), e ? this.ai.language.ask(
|
|
507
|
+
let s = "";
|
|
508
|
+
return r.transcription.filter((n, c) => {
|
|
509
|
+
let u = !1;
|
|
510
|
+
const i = r.transcription[c - 1], m = r.transcription[c + 1];
|
|
511
|
+
return !n.text && m ? (m.offsets.from = n.offsets.from, m.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && i && (i.offsets.to = n.offsets.to, i.timestamps.to = n.timestamps.to, i.text += n.text, u = !0), !!n.text && !u;
|
|
512
|
+
}).forEach((n) => {
|
|
513
|
+
const c = /^[A-Z]/.test(n.text.trim()), u = n.offsets.to - n.offsets.from, m = a(n.text.trim()) * t;
|
|
514
|
+
c && u > m * 2 && n.text[0] == " " && (s += "."), s += n.text;
|
|
515
|
+
}), e ? this.ai.language.ask(s, {
|
|
509
516
|
system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
|
|
510
517
|
temperature: 0.1,
|
|
511
518
|
tools: [{
|
|
@@ -515,137 +522,137 @@ print(json.dumps(segments))
|
|
|
515
522
|
find: { type: "string", description: "Text to find", required: !0 },
|
|
516
523
|
replace: { type: "string", description: "Text to replace", required: !0 }
|
|
517
524
|
},
|
|
518
|
-
fn: (
|
|
525
|
+
fn: (n) => s = s.replace(n.find, n.replace)
|
|
519
526
|
}]
|
|
520
|
-
}).then(() =>
|
|
527
|
+
}).then(() => s) : s.trim();
|
|
521
528
|
}
|
|
522
529
|
async diarizeTranscript(r, e, t) {
|
|
523
|
-
const
|
|
524
|
-
let
|
|
525
|
-
e.forEach((
|
|
526
|
-
|
|
530
|
+
const a = /* @__PURE__ */ new Map();
|
|
531
|
+
let s = 0;
|
|
532
|
+
e.forEach((p) => {
|
|
533
|
+
a.has(p.speaker) || a.set(p.speaker, ++s);
|
|
527
534
|
});
|
|
528
|
-
const
|
|
529
|
-
if (
|
|
530
|
-
const f =
|
|
535
|
+
const n = await this.addPunctuation(r, t), c = n.match(/[^.!?]+[.!?]+/g) || [n], u = r.transcription.filter((p) => p.text.trim()), i = c.map((p) => {
|
|
536
|
+
if (p = p.trim(), !p) return null;
|
|
537
|
+
const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
|
|
531
538
|
f.forEach((k) => {
|
|
532
|
-
const x =
|
|
539
|
+
const x = u.find((y) => k === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
|
|
533
540
|
if (!x) return;
|
|
534
|
-
const E = x.offsets.from / 1e3,
|
|
535
|
-
if (
|
|
536
|
-
const
|
|
537
|
-
|
|
541
|
+
const E = x.offsets.from / 1e3, $ = e.find((y) => E >= y.start && E <= y.end);
|
|
542
|
+
if ($) {
|
|
543
|
+
const y = a.get($.speaker);
|
|
544
|
+
g.set(y, (g.get(y) || 0) + 1);
|
|
538
545
|
}
|
|
539
546
|
});
|
|
540
547
|
let j = 1, T = 0;
|
|
541
|
-
return
|
|
548
|
+
return g.forEach((k, x) => {
|
|
542
549
|
k > T && (T = k, j = x);
|
|
543
|
-
}), { speaker: j, text:
|
|
544
|
-
}).filter((
|
|
545
|
-
|
|
550
|
+
}), { speaker: j, text: p };
|
|
551
|
+
}).filter((p) => p !== null), m = [];
|
|
552
|
+
i.forEach((p) => {
|
|
546
553
|
const f = m[m.length - 1];
|
|
547
|
-
f && f.speaker ===
|
|
554
|
+
f && f.speaker === p.speaker ? f.text += " " + p.text : m.push({ ...p });
|
|
548
555
|
});
|
|
549
|
-
let
|
|
556
|
+
let o = m.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
|
|
550
557
|
`).trim();
|
|
551
|
-
if (!t) return
|
|
552
|
-
let
|
|
553
|
-
|
|
554
|
-
const
|
|
558
|
+
if (!t) return o;
|
|
559
|
+
let l = this.ai.language.chunk(o, 500, 0);
|
|
560
|
+
l.length > 4 && (l = [...l.slice(0, 3), l.at(-1)]);
|
|
561
|
+
const d = await this.ai.language.json(l.join(`
|
|
555
562
|
`), '{1: "Detected Name", 2: "Second Name"}', {
|
|
556
563
|
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",
|
|
557
564
|
temperature: 0.1
|
|
558
565
|
});
|
|
559
|
-
return Object.entries(
|
|
566
|
+
return Object.entries(d).forEach(([p, f]) => o = o.replaceAll(`[Speaker ${p}]`, `[${f}]`)), o;
|
|
560
567
|
}
|
|
561
568
|
runAsr(r, e = {}) {
|
|
562
569
|
let t;
|
|
563
|
-
const
|
|
570
|
+
const a = new Promise((s, n) => {
|
|
564
571
|
this.downloadAsrModel(e.model).then((c) => {
|
|
565
572
|
if (e.diarization) {
|
|
566
|
-
let
|
|
573
|
+
let u = v.join(v.dirname(r), "transcript");
|
|
567
574
|
t = b(
|
|
568
575
|
this.ai.options.whisper,
|
|
569
|
-
["-m", c, "-f", r, "-np", "-ml", "1", "-oj", "-of",
|
|
576
|
+
["-m", c, "-f", r, "-np", "-ml", "1", "-oj", "-of", u],
|
|
570
577
|
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
571
|
-
), t.on("error", (
|
|
572
|
-
if (
|
|
573
|
-
|
|
578
|
+
), t.on("error", (i) => n(i)), t.on("close", async (i) => {
|
|
579
|
+
if (i === 0) {
|
|
580
|
+
u = await w.readFile(u + ".json", "utf-8"), w.rm(u + ".json").catch(() => {
|
|
574
581
|
});
|
|
575
582
|
try {
|
|
576
|
-
|
|
583
|
+
s(JSON.parse(u));
|
|
577
584
|
} catch {
|
|
578
|
-
|
|
585
|
+
n(new Error("Failed to parse whisper JSON"));
|
|
579
586
|
}
|
|
580
587
|
} else
|
|
581
|
-
|
|
588
|
+
n(new Error(`Exit code ${i}`));
|
|
582
589
|
});
|
|
583
590
|
} else {
|
|
584
|
-
let
|
|
585
|
-
t = b(this.ai.options.whisper, ["-m", c, "-f", r, "-np", "-nt"]), t.on("error", (
|
|
586
|
-
|
|
591
|
+
let u = "";
|
|
592
|
+
t = b(this.ai.options.whisper, ["-m", c, "-f", r, "-np", "-nt"]), t.on("error", (i) => n(i)), t.stdout.on("data", (i) => u += i.toString()), t.on("close", async (i) => {
|
|
593
|
+
i === 0 ? s(u.trim() || null) : n(new Error(`Exit code ${i}`));
|
|
587
594
|
});
|
|
588
595
|
}
|
|
589
596
|
});
|
|
590
597
|
});
|
|
591
|
-
return Object.assign(
|
|
598
|
+
return Object.assign(a, { abort: () => t?.kill("SIGTERM") });
|
|
592
599
|
}
|
|
593
600
|
runDiarization(r) {
|
|
594
601
|
let e = !1, t = () => {
|
|
595
602
|
e = !0;
|
|
596
603
|
};
|
|
597
|
-
const
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
}),
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
]).then((async ([
|
|
604
|
+
const a = (n) => new Promise((c) => {
|
|
605
|
+
const u = b(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
|
|
606
|
+
u.on("close", (i) => c(i === 0)), u.on("error", () => c(!1));
|
|
607
|
+
}), s = Promise.all([
|
|
608
|
+
a("python"),
|
|
609
|
+
a("python3")
|
|
610
|
+
]).then((async ([n, c]) => {
|
|
604
611
|
if (e) return;
|
|
605
|
-
if (!
|
|
606
|
-
const
|
|
607
|
-
return new Promise((
|
|
612
|
+
if (!n && !c) throw new Error("Pyannote is not installed: pip install pyannote.audio");
|
|
613
|
+
const u = c ? "python3" : "python";
|
|
614
|
+
return new Promise((i, m) => {
|
|
608
615
|
if (e) return;
|
|
609
|
-
let
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
if (
|
|
616
|
+
let o = "";
|
|
617
|
+
const l = b(u, ["-W", "ignore", "-c", this.pyannote, r]);
|
|
618
|
+
l.stdout.on("data", (d) => o += d.toString()), l.stderr.on("data", (d) => console.error(d.toString())), l.on("close", (d) => {
|
|
619
|
+
if (d === 0)
|
|
613
620
|
try {
|
|
614
|
-
|
|
621
|
+
i(JSON.parse(o));
|
|
615
622
|
} catch {
|
|
616
623
|
m(new Error("Failed to parse diarization output"));
|
|
617
624
|
}
|
|
618
625
|
else
|
|
619
|
-
m(new Error(`Python process exited with code ${
|
|
620
|
-
}),
|
|
626
|
+
m(new Error(`Python process exited with code ${d}`));
|
|
627
|
+
}), l.on("error", m), t = () => l.kill("SIGTERM");
|
|
621
628
|
});
|
|
622
629
|
}));
|
|
623
|
-
return Object.assign(
|
|
630
|
+
return Object.assign(s, { abort: t });
|
|
624
631
|
}
|
|
625
632
|
asr(r, e = {}) {
|
|
626
633
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
627
|
-
const t =
|
|
634
|
+
const t = P(K(P(U(), "audio-")), "converted.wav");
|
|
628
635
|
B(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
|
|
629
|
-
const
|
|
636
|
+
const a = () => w.rm(M.dirname(t), { recursive: !0, force: !0 }).catch(() => {
|
|
630
637
|
});
|
|
631
638
|
if (!e.diarization) return this.runAsr(t, { model: e.model });
|
|
632
|
-
const
|
|
633
|
-
let c = !1,
|
|
634
|
-
c = !0,
|
|
639
|
+
const s = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
|
|
640
|
+
let c = !1, u = () => {
|
|
641
|
+
c = !0, s.abort(), n.abort(), a();
|
|
635
642
|
};
|
|
636
|
-
const
|
|
643
|
+
const i = Promise.allSettled([s, n]).then(async ([m, o]) => {
|
|
637
644
|
if (m.status == "rejected") throw new Error(`Whisper.cpp timestamps:
|
|
638
645
|
` + m.reason);
|
|
639
|
-
if (
|
|
640
|
-
` +
|
|
641
|
-
return c || !e.diarization ? m.value : this.diarizeTranscript(m.value,
|
|
642
|
-
}).finally(() =>
|
|
643
|
-
return Object.assign(
|
|
646
|
+
if (o.status == "rejected") throw new Error(`Pyannote:
|
|
647
|
+
` + o.reason);
|
|
648
|
+
return c || !e.diarization ? m.value : this.diarizeTranscript(m.value, o.value, e.diarization == "llm");
|
|
649
|
+
}).finally(() => a());
|
|
650
|
+
return Object.assign(i, { abort: u });
|
|
644
651
|
}
|
|
645
652
|
async downloadAsrModel(r = this.whisperModel) {
|
|
646
653
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
647
654
|
r.endsWith(".bin") || (r += ".bin");
|
|
648
|
-
const e =
|
|
655
|
+
const e = M.join(this.ai.options.path, r);
|
|
649
656
|
return await w.stat(e).then(() => !0).catch(() => !1) ? e : this.downloads[r] ? this.downloads[r] : (this.downloads[r] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${r}`).then((t) => t.arrayBuffer()).then((t) => Buffer.from(t)).then(async (t) => (await w.writeFile(e, t), delete this.downloads[r], e)), this.downloads[r]);
|
|
650
657
|
}
|
|
651
658
|
}
|
|
@@ -660,17 +667,17 @@ class re {
|
|
|
660
667
|
*/
|
|
661
668
|
ocr(r) {
|
|
662
669
|
let e;
|
|
663
|
-
const t = new Promise(async (
|
|
670
|
+
const t = new Promise(async (a) => {
|
|
664
671
|
e = await V(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
665
|
-
const { data:
|
|
666
|
-
await e.terminate(),
|
|
672
|
+
const { data: s } = await e.recognize(r);
|
|
673
|
+
await e.terminate(), a(s.text.trim() || null);
|
|
667
674
|
});
|
|
668
675
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
669
676
|
}
|
|
670
677
|
}
|
|
671
678
|
class we {
|
|
672
679
|
constructor(r) {
|
|
673
|
-
this.options = r, r.path || (r.path =
|
|
680
|
+
this.options = r, r.path || (r.path = z.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new te(this), this.language = new ee(this), this.vision = new re(this);
|
|
674
681
|
}
|
|
675
682
|
/** Audio processing AI */
|
|
676
683
|
audio;
|
|
@@ -679,7 +686,7 @@ class we {
|
|
|
679
686
|
/** Vision processing AI */
|
|
680
687
|
vision;
|
|
681
688
|
}
|
|
682
|
-
const
|
|
689
|
+
const ne = {
|
|
683
690
|
name: "cli",
|
|
684
691
|
description: "Use the command line interface, returns any output",
|
|
685
692
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
@@ -700,9 +707,9 @@ const se = {
|
|
|
700
707
|
try {
|
|
701
708
|
switch (h.type) {
|
|
702
709
|
case "bash":
|
|
703
|
-
return await
|
|
710
|
+
return await ne.fn({ command: h.code }, r, e);
|
|
704
711
|
case "node":
|
|
705
|
-
return await
|
|
712
|
+
return await se.fn({ code: h.code }, r, e);
|
|
706
713
|
case "python":
|
|
707
714
|
return await oe.fn({ code: h.code }, r, e);
|
|
708
715
|
}
|
|
@@ -720,14 +727,14 @@ const se = {
|
|
|
720
727
|
body: { type: "object", description: "HTTP body to send" }
|
|
721
728
|
},
|
|
722
729
|
fn: (h) => new N({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
|
|
723
|
-
},
|
|
730
|
+
}, se = {
|
|
724
731
|
name: "exec_javascript",
|
|
725
732
|
description: "Execute commonjs javascript",
|
|
726
733
|
args: {
|
|
727
734
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
728
735
|
},
|
|
729
736
|
fn: async (h) => {
|
|
730
|
-
const r =
|
|
737
|
+
const r = L(null), e = await C({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
|
|
731
738
|
return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
|
|
732
739
|
}
|
|
733
740
|
}, oe = {
|
|
@@ -745,24 +752,24 @@ const se = {
|
|
|
745
752
|
focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
|
|
746
753
|
},
|
|
747
754
|
fn: async (h) => {
|
|
748
|
-
const r = await fetch(h.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((
|
|
749
|
-
throw new Error(`Failed to fetch: ${
|
|
755
|
+
const r = await fetch(h.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((n) => n.text()).catch((n) => {
|
|
756
|
+
throw new Error(`Failed to fetch: ${n.message}`);
|
|
750
757
|
}), e = Y.load(r);
|
|
751
758
|
e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
|
|
752
759
|
const t = {
|
|
753
760
|
title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
|
|
754
761
|
description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
|
|
755
762
|
};
|
|
756
|
-
let
|
|
757
|
-
const
|
|
758
|
-
for (const
|
|
759
|
-
const c = e(
|
|
763
|
+
let a = "";
|
|
764
|
+
const s = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
|
|
765
|
+
for (const n of s) {
|
|
766
|
+
const c = e(n).first();
|
|
760
767
|
if (c.length && c.text().trim().length > 200) {
|
|
761
|
-
|
|
768
|
+
a = c.text();
|
|
762
769
|
break;
|
|
763
770
|
}
|
|
764
771
|
}
|
|
765
|
-
return
|
|
772
|
+
return a || (a = e("body").text()), a = a.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: a, focus: h.focus };
|
|
766
773
|
}
|
|
767
774
|
}, Se = {
|
|
768
775
|
name: "web_search",
|
|
@@ -774,27 +781,27 @@ const se = {
|
|
|
774
781
|
fn: async (h) => {
|
|
775
782
|
const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
|
|
776
783
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
777
|
-
}).then((
|
|
784
|
+
}).then((s) => s.text());
|
|
778
785
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
779
|
-
const
|
|
786
|
+
const a = new J();
|
|
780
787
|
for (; (e = t.exec(r)) !== null; ) {
|
|
781
|
-
let
|
|
782
|
-
if (
|
|
788
|
+
let s = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
789
|
+
if (s && (s = decodeURIComponent(s)), s && a.add(s), a.size >= (h.length || 5)) break;
|
|
783
790
|
}
|
|
784
|
-
return
|
|
791
|
+
return a;
|
|
785
792
|
}
|
|
786
793
|
};
|
|
787
794
|
export {
|
|
788
795
|
we as Ai,
|
|
789
796
|
X as Anthropic,
|
|
790
797
|
te as Audio,
|
|
791
|
-
|
|
798
|
+
ne as CliTool,
|
|
792
799
|
be as DateTimeTool,
|
|
793
800
|
ke as ExecTool,
|
|
794
801
|
xe as FetchTool,
|
|
795
|
-
|
|
802
|
+
se as JSTool,
|
|
796
803
|
R as LLMProvider,
|
|
797
|
-
|
|
804
|
+
q as OpenAi,
|
|
798
805
|
oe as PythonTool,
|
|
799
806
|
_e as ReadWebpageTool,
|
|
800
807
|
re as Vision,
|