@ztimson/ai-utils 0.8.10 → 0.8.12
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 +30 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +397 -312
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import * as O from "node:os";
|
|
2
|
-
import { tmpdir as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { OpenAI as
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { join as
|
|
8
|
-
import { spawn as
|
|
9
|
-
import { mkdtempSync as
|
|
10
|
-
import
|
|
2
|
+
import { tmpdir as U } from "node:os";
|
|
3
|
+
import { Anthropic as W } from "@anthropic-ai/sdk";
|
|
4
|
+
import { objectMap as z, JSONAttemptParse as S, findByProp as R, JSONSanitize as _, clean as N, Http as C, consoleInterceptor as J, fn as I, ASet as D } from "@ztimson/utils";
|
|
5
|
+
import { OpenAI as F } from "openai";
|
|
6
|
+
import { fileURLToPath as B } from "url";
|
|
7
|
+
import { join as H, dirname as G } from "path";
|
|
8
|
+
import { spawn as w, execSync as K } from "node:child_process";
|
|
9
|
+
import { mkdtempSync as Y } from "node:fs";
|
|
10
|
+
import b from "node:fs/promises";
|
|
11
11
|
import * as M from "node:path";
|
|
12
|
-
import
|
|
13
|
-
import { createWorker as
|
|
12
|
+
import q, { join as A } from "node:path";
|
|
13
|
+
import { createWorker as V } from "tesseract.js";
|
|
14
14
|
import * as Z from "cheerio";
|
|
15
15
|
import { $Sync as j } from "@ztimson/node-utils";
|
|
16
|
-
class
|
|
16
|
+
class L {
|
|
17
17
|
}
|
|
18
|
-
class Q extends
|
|
18
|
+
class Q extends L {
|
|
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 W({ apiKey: e });
|
|
21
21
|
}
|
|
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 i of r)
|
|
26
|
+
if (typeof i.content == "string")
|
|
27
|
+
t.push({ timestamp: e, ...i });
|
|
28
28
|
else {
|
|
29
|
-
const
|
|
29
|
+
const o = i.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
|
|
30
30
|
|
|
31
31
|
`);
|
|
32
|
-
|
|
32
|
+
o && t.push({ timestamp: e, role: i.role, content: o }), i.content.forEach((n) => {
|
|
33
33
|
if (n.type == "tool_use")
|
|
34
34
|
t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
|
|
35
35
|
else if (n.type == "tool_result") {
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
const m = t.findLast((l) => l.id == n.tool_use_id);
|
|
37
|
+
m && (m[n.is_error ? "error" : "content"] = n.content);
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -55,78 +55,78 @@ class Q extends z {
|
|
|
55
55
|
}
|
|
56
56
|
ask(r, e = {}) {
|
|
57
57
|
const t = new AbortController();
|
|
58
|
-
return Object.assign(new Promise(async (
|
|
59
|
-
let
|
|
60
|
-
const n = e.tools || this.ai.options.llm?.tools || [],
|
|
58
|
+
return Object.assign(new Promise(async (i) => {
|
|
59
|
+
let o = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
60
|
+
const n = e.tools || this.ai.options.llm?.tools || [], m = {
|
|
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: n.map((
|
|
66
|
-
name:
|
|
67
|
-
description:
|
|
65
|
+
tools: n.map((u) => ({
|
|
66
|
+
name: u.name,
|
|
67
|
+
description: u.description,
|
|
68
68
|
input_schema: {
|
|
69
69
|
type: "object",
|
|
70
|
-
properties:
|
|
71
|
-
required:
|
|
70
|
+
properties: u.args ? z(u.args, (s, c) => ({ ...c, required: void 0 })) : {},
|
|
71
|
+
required: u.args ? Object.entries(u.args).filter((s) => s[1].required).map((s) => s[0]) : []
|
|
72
72
|
},
|
|
73
73
|
fn: void 0
|
|
74
74
|
})),
|
|
75
|
-
messages:
|
|
75
|
+
messages: o,
|
|
76
76
|
stream: !!e.stream
|
|
77
77
|
};
|
|
78
|
-
let
|
|
78
|
+
let l, a = !0;
|
|
79
79
|
do {
|
|
80
|
-
if (
|
|
81
|
-
throw
|
|
80
|
+
if (l = await this.client.messages.create(m).catch((s) => {
|
|
81
|
+
throw s.message += `
|
|
82
82
|
|
|
83
83
|
Messages:
|
|
84
|
-
${JSON.stringify(
|
|
84
|
+
${JSON.stringify(o, null, 2)}`, s;
|
|
85
85
|
}), e.stream) {
|
|
86
|
-
|
|
86
|
+
a ? a = !1 : e.stream({ text: `
|
|
87
87
|
|
|
88
|
-
` }),
|
|
89
|
-
for await (const
|
|
88
|
+
` }), l.content = [];
|
|
89
|
+
for await (const s of l) {
|
|
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 (s.type === "content_block_start")
|
|
92
|
+
s.content_block.type === "text" ? l.content.push({ type: "text", text: "" }) : s.content_block.type === "tool_use" && l.content.push({ type: "tool_use", id: s.content_block.id, name: s.content_block.name, input: "" });
|
|
93
|
+
else if (s.type === "content_block_delta")
|
|
94
|
+
if (s.delta.type === "text_delta") {
|
|
95
|
+
const c = s.delta.text;
|
|
96
|
+
l.content.at(-1).text += c, e.stream({ text: c });
|
|
97
|
+
} else s.delta.type === "input_json_delta" && (l.content.at(-1).input += s.delta.partial_json);
|
|
98
|
+
else if (s.type === "content_block_stop") {
|
|
99
|
+
const c = l.content.at(-1);
|
|
100
|
+
c.input != null && (c.input = c.input ? S(c.input, {}) : {});
|
|
101
|
+
} else if (s.type === "message_stop")
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const d = n.find(
|
|
110
|
-
if (e.stream && e.stream({ tool:
|
|
105
|
+
const u = l.content.filter((s) => s.type === "tool_use");
|
|
106
|
+
if (u.length && !t.signal.aborted) {
|
|
107
|
+
o.push({ role: "assistant", content: l.content });
|
|
108
|
+
const s = await Promise.all(u.map(async (c) => {
|
|
109
|
+
const d = n.find(R("name", c.name));
|
|
110
|
+
if (e.stream && e.stream({ tool: c.name }), !d) return { tool_use_id: c.id, is_error: !0, content: "Tool not found" };
|
|
111
111
|
try {
|
|
112
|
-
const p = await d.fn(
|
|
113
|
-
return { type: "tool_result", tool_use_id:
|
|
112
|
+
const p = await d.fn(c.input, e?.stream, this.ai);
|
|
113
|
+
return { type: "tool_result", tool_use_id: c.id, content: _(p) };
|
|
114
114
|
} catch (p) {
|
|
115
|
-
return { type: "tool_result", tool_use_id:
|
|
115
|
+
return { type: "tool_result", tool_use_id: c.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
|
|
116
116
|
}
|
|
117
117
|
}));
|
|
118
|
-
|
|
118
|
+
o.push({ role: "user", content: s }), m.messages = o;
|
|
119
119
|
}
|
|
120
|
-
} while (!t.signal.aborted &&
|
|
121
|
-
|
|
120
|
+
} while (!t.signal.aborted && l.content.some((u) => u.type === "tool_use"));
|
|
121
|
+
o.push({ role: "assistant", content: l.content.filter((u) => u.type == "text").map((u) => u.text).join(`
|
|
122
122
|
|
|
123
|
-
`) }),
|
|
123
|
+
`) }), o = this.toStandard(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), i(o.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 P extends L {
|
|
128
|
+
constructor(r, e, t, i) {
|
|
129
|
+
super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new F(N({
|
|
130
130
|
baseURL: e,
|
|
131
131
|
apiKey: t || e ? "ignored" : void 0
|
|
132
132
|
}));
|
|
@@ -136,17 +136,17 @@ class A extends z {
|
|
|
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 i = t.tool_calls.map((o) => ({
|
|
140
140
|
role: "tool",
|
|
141
|
-
id:
|
|
142
|
-
name:
|
|
143
|
-
args:
|
|
141
|
+
id: o.id,
|
|
142
|
+
name: o.function.name,
|
|
143
|
+
args: S(o.function.arguments, {}),
|
|
144
144
|
timestamp: t.timestamp
|
|
145
145
|
}));
|
|
146
|
-
r.splice(e, 1, ...
|
|
146
|
+
r.splice(e, 1, ...i), e += i.length - 1;
|
|
147
147
|
} else if (t.role === "tool" && t.content) {
|
|
148
|
-
const
|
|
149
|
-
|
|
148
|
+
const i = r.find((o) => t.tool_call_id == o.id);
|
|
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());
|
|
152
152
|
}
|
|
@@ -167,53 +167,53 @@ class A extends z {
|
|
|
167
167
|
content: t.error || t.content
|
|
168
168
|
});
|
|
169
169
|
else {
|
|
170
|
-
const { timestamp:
|
|
171
|
-
e.push(
|
|
170
|
+
const { timestamp: i, ...o } = t;
|
|
171
|
+
e.push(o);
|
|
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 (
|
|
178
|
+
return Object.assign(new Promise(async (i, o) => {
|
|
179
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
180
|
let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
181
|
-
const
|
|
181
|
+
const m = e.tools || this.ai.options.llm?.tools || [], l = {
|
|
182
182
|
model: e.model || this.model,
|
|
183
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:
|
|
187
|
+
tools: m.map((s) => ({
|
|
188
188
|
type: "function",
|
|
189
189
|
function: {
|
|
190
|
-
name:
|
|
191
|
-
description:
|
|
190
|
+
name: s.name,
|
|
191
|
+
description: s.description,
|
|
192
192
|
parameters: {
|
|
193
193
|
type: "object",
|
|
194
|
-
properties:
|
|
195
|
-
required:
|
|
194
|
+
properties: s.args ? z(s.args, (c, d) => ({ ...d, required: void 0 })) : {},
|
|
195
|
+
required: s.args ? Object.entries(s.args).filter((c) => c[1].required).map((c) => c[0]) : []
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}))
|
|
199
199
|
};
|
|
200
|
-
let
|
|
200
|
+
let a, u = !0;
|
|
201
201
|
do {
|
|
202
|
-
if (
|
|
203
|
-
throw
|
|
202
|
+
if (a = await this.client.chat.completions.create(l).catch((c) => {
|
|
203
|
+
throw c.message += `
|
|
204
204
|
|
|
205
205
|
Messages:
|
|
206
|
-
${JSON.stringify(n, null, 2)}`,
|
|
206
|
+
${JSON.stringify(n, null, 2)}`, c;
|
|
207
207
|
}), e.stream) {
|
|
208
|
-
|
|
208
|
+
u ? u = !1 : e.stream({ text: `
|
|
209
209
|
|
|
210
|
-
` }),
|
|
211
|
-
for await (const
|
|
210
|
+
` }), a.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
|
|
211
|
+
for await (const c of a) {
|
|
212
212
|
if (t.signal.aborted) break;
|
|
213
|
-
if (
|
|
214
|
-
for (const d of
|
|
215
|
-
const p =
|
|
216
|
-
p ? (d.id && (p.id = d.id), d.type && (p.type = d.type), d.function && (p.function || (p.function = {}), d.function.name && (p.function.name = d.function.name), d.function.arguments && (p.function.arguments = (p.function.arguments || "") + d.function.arguments))) :
|
|
213
|
+
if (c.choices[0].delta.content && (a.choices[0].message.content += c.choices[0].delta.content, e.stream({ text: c.choices[0].delta.content })), c.choices[0].delta.tool_calls)
|
|
214
|
+
for (const d of c.choices[0].delta.tool_calls) {
|
|
215
|
+
const p = a.choices[0].message.tool_calls.find((f) => f.index === d.index);
|
|
216
|
+
p ? (d.id && (p.id = d.id), d.type && (p.type = d.type), d.function && (p.function || (p.function = {}), d.function.name && (p.function.name = d.function.name), d.function.arguments && (p.function.arguments = (p.function.arguments || "") + d.function.arguments))) : a.choices[0].message.tool_calls.push({
|
|
217
217
|
index: d.index,
|
|
218
218
|
id: d.id || "",
|
|
219
219
|
type: d.type || "function",
|
|
@@ -225,30 +225,30 @@ ${JSON.stringify(n, null, 2)}`, l;
|
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
230
|
-
n.push(
|
|
231
|
-
const
|
|
232
|
-
const p =
|
|
228
|
+
const s = a.choices[0].message.tool_calls || [];
|
|
229
|
+
if (s.length && !t.signal.aborted) {
|
|
230
|
+
n.push(a.choices[0].message);
|
|
231
|
+
const c = await Promise.all(s.map(async (d) => {
|
|
232
|
+
const p = m?.find(R("name", d.function.name));
|
|
233
233
|
if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
|
|
234
234
|
try {
|
|
235
|
-
const f =
|
|
236
|
-
return { role: "tool", tool_call_id: d.id, content:
|
|
235
|
+
const f = S(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
|
|
236
|
+
return { role: "tool", tool_call_id: d.id, content: _(g) };
|
|
237
237
|
} catch (f) {
|
|
238
|
-
return { role: "tool", tool_call_id: d.id, content:
|
|
238
|
+
return { role: "tool", tool_call_id: d.id, content: _({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
239
239
|
}
|
|
240
240
|
}));
|
|
241
|
-
n.push(...
|
|
241
|
+
n.push(...c), l.messages = n;
|
|
242
242
|
}
|
|
243
|
-
} while (!t.signal.aborted &&
|
|
244
|
-
n.push({ role: "assistant", content:
|
|
243
|
+
} while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
|
|
244
|
+
n.push({ role: "assistant", content: a.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);
|
|
245
245
|
}), { abort: () => t.abort() });
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
class X {
|
|
249
249
|
constructor(r) {
|
|
250
250
|
this.ai = r, r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
|
|
251
|
-
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new Q(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new
|
|
251
|
+
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new Q(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new P(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new P(this.ai, t.host || null, t.token, e));
|
|
252
252
|
});
|
|
253
253
|
}
|
|
254
254
|
defaultModel;
|
|
@@ -270,27 +270,27 @@ class X {
|
|
|
270
270
|
};
|
|
271
271
|
const t = e.model || this.defaultModel;
|
|
272
272
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
273
|
-
let
|
|
273
|
+
let i = () => {
|
|
274
274
|
};
|
|
275
|
-
return Object.assign(new Promise(async (
|
|
275
|
+
return Object.assign(new Promise(async (o) => {
|
|
276
276
|
if (e.history || (e.history = []), e.memory) {
|
|
277
|
-
const
|
|
278
|
-
const [
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
const m = async (a, u, s = 10) => {
|
|
278
|
+
const [c, d] = await Promise.all([
|
|
279
|
+
u ? this.embedding(u) : Promise.resolve(null),
|
|
280
|
+
a ? this.embedding(a) : Promise.resolve(null)
|
|
281
281
|
]);
|
|
282
282
|
return (e.memory || []).map((p) => {
|
|
283
|
-
const f = (
|
|
283
|
+
const f = (c ? this.cosineSimilarity(p.embeddings[0], c[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
|
|
284
284
|
return { ...p, score: f };
|
|
285
|
-
}).toSorted((p, f) => p.score - f.score).slice(0,
|
|
285
|
+
}).toSorted((p, f) => p.score - f.score).slice(0, s).map((p) => `- ${p.owner}: ${p.fact}`).join(`
|
|
286
286
|
`);
|
|
287
287
|
};
|
|
288
288
|
e.system += `
|
|
289
289
|
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.
|
|
290
290
|
`;
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
${
|
|
291
|
+
const l = await m(r);
|
|
292
|
+
l.length && e.history.push({ role: "tool", name: "recall", id: "auto_recall_" + Math.random().toString(), args: {}, content: `Things I remembered:
|
|
293
|
+
${l}` }), e.tools = [{
|
|
294
294
|
name: "recall",
|
|
295
295
|
description: "Recall the closest memories you have regarding a query using RAG",
|
|
296
296
|
args: {
|
|
@@ -298,9 +298,9 @@ ${u}` }), e.tools = [{
|
|
|
298
298
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
299
299
|
topK: { type: "number", description: "Result limit, default 5" }
|
|
300
300
|
},
|
|
301
|
-
fn: (
|
|
302
|
-
if (!
|
|
303
|
-
return
|
|
301
|
+
fn: (a) => {
|
|
302
|
+
if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
|
|
303
|
+
return m(a.query, a.subject, a.topK);
|
|
304
304
|
}
|
|
305
305
|
}, {
|
|
306
306
|
name: "remember",
|
|
@@ -309,31 +309,31 @@ ${u}` }), e.tools = [{
|
|
|
309
309
|
owner: { type: "string", description: "Subject/person this fact is about" },
|
|
310
310
|
fact: { type: "string", description: "The information to remember" }
|
|
311
311
|
},
|
|
312
|
-
fn: async (
|
|
312
|
+
fn: async (a) => {
|
|
313
313
|
if (!e.memory) return;
|
|
314
|
-
const
|
|
315
|
-
this.embedding(
|
|
316
|
-
this.embedding(`${
|
|
317
|
-
]),
|
|
318
|
-
return e.memory.splice(0, e.memory.length, ...e.memory.filter((
|
|
314
|
+
const u = await Promise.all([
|
|
315
|
+
this.embedding(a.owner),
|
|
316
|
+
this.embedding(`${a.owner}: ${a.fact}`)
|
|
317
|
+
]), s = { owner: a.owner, fact: a.fact, embeddings: [u[0][0].embedding, u[1][0].embedding] };
|
|
318
|
+
return e.memory.splice(0, e.memory.length, ...e.memory.filter((c) => !(this.cosineSimilarity(s.embeddings[0], c.embeddings[0]) >= 0.9 && this.cosineSimilarity(s.embeddings[1], c.embeddings[1]) >= 0.8)), s), "Remembered!";
|
|
319
319
|
}
|
|
320
320
|
}, ...e.tools || []];
|
|
321
321
|
}
|
|
322
322
|
const n = await this.models[t].ask(r, e);
|
|
323
|
-
if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((
|
|
324
|
-
const
|
|
325
|
-
e.history.splice(0, e.history.length, ...
|
|
323
|
+
if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((m) => m.role != "tool" || m.name != "recall" && m.name != "remember")), e.compress) {
|
|
324
|
+
const m = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
|
|
325
|
+
e.history.splice(0, e.history.length, ...m);
|
|
326
326
|
}
|
|
327
|
-
return
|
|
328
|
-
}), { abort:
|
|
327
|
+
return o(n);
|
|
328
|
+
}), { abort: i });
|
|
329
329
|
}
|
|
330
330
|
async code(r, e) {
|
|
331
331
|
const t = await this.ask(r, { ...e, system: [
|
|
332
332
|
e?.system,
|
|
333
333
|
"Return your response in a code block"
|
|
334
|
-
].filter((
|
|
335
|
-
`) }),
|
|
336
|
-
return
|
|
334
|
+
].filter((o) => !!o).join(`
|
|
335
|
+
`) }), i = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
|
|
336
|
+
return i ? i[1].trim() : null;
|
|
337
337
|
}
|
|
338
338
|
/**
|
|
339
339
|
* Compress chat history to reduce context size
|
|
@@ -343,17 +343,17 @@ ${u}` }), e.tools = [{
|
|
|
343
343
|
* @param {LLMRequest} options LLM options
|
|
344
344
|
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
345
345
|
*/
|
|
346
|
-
async compressHistory(r, e, t,
|
|
346
|
+
async compressHistory(r, e, t, i) {
|
|
347
347
|
if (this.estimateTokens(r) < e) return r;
|
|
348
|
-
let
|
|
348
|
+
let o = 0, n = 0;
|
|
349
349
|
for (let d of r.toReversed())
|
|
350
|
-
if (n += this.estimateTokens(d.content), n < t)
|
|
350
|
+
if (n += this.estimateTokens(d.content), n < t) o++;
|
|
351
351
|
else break;
|
|
352
|
-
if (r.length <=
|
|
353
|
-
const
|
|
352
|
+
if (r.length <= o) return r;
|
|
353
|
+
const m = r[0].role == "system" ? r[0] : null, l = o == 0 ? [] : r.slice(-o), a = (o == 0 ? r : r.slice(0, -o)).filter((d) => d.role === "assistant" || d.role === "user"), u = await this.summarize(a.map((d) => `[${d.role}]: ${d.content}`).join(`
|
|
354
354
|
|
|
355
|
-
`), 500,
|
|
356
|
-
return
|
|
355
|
+
`), 500, i), s = Date.now(), c = [{ role: "tool", name: "summary", id: "summary_" + s, args: {}, content: `Conversation Summary: ${u?.summary}`, timestamp: s }, ...l];
|
|
356
|
+
return m && c.splice(0, 0, m), c;
|
|
357
357
|
}
|
|
358
358
|
/**
|
|
359
359
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -363,10 +363,10 @@ ${u}` }), e.tools = [{
|
|
|
363
363
|
*/
|
|
364
364
|
cosineSimilarity(r, e) {
|
|
365
365
|
if (r.length !== e.length) throw new Error("Vectors must be same length");
|
|
366
|
-
let t = 0,
|
|
367
|
-
for (let
|
|
368
|
-
t += r[
|
|
369
|
-
const n = Math.sqrt(
|
|
366
|
+
let t = 0, i = 0, o = 0;
|
|
367
|
+
for (let m = 0; m < r.length; m++)
|
|
368
|
+
t += r[m] * e[m], i += r[m] * r[m], o += e[m] * e[m];
|
|
369
|
+
const n = Math.sqrt(i) * Math.sqrt(o);
|
|
370
370
|
return n === 0 ? 0 : t / n;
|
|
371
371
|
}
|
|
372
372
|
/**
|
|
@@ -377,25 +377,25 @@ ${u}` }), e.tools = [{
|
|
|
377
377
|
* @returns {string[]} Chunked strings
|
|
378
378
|
*/
|
|
379
379
|
chunk(r, e = 500, t = 50) {
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
return typeof
|
|
383
|
-
}) : [], n = (typeof r == "object" ?
|
|
384
|
-
`)).flatMap((
|
|
385
|
-
`]),
|
|
386
|
-
for (let
|
|
387
|
-
let
|
|
388
|
-
for (;
|
|
389
|
-
const
|
|
390
|
-
if (this.estimateTokens(
|
|
391
|
-
`)) > e &&
|
|
392
|
-
|
|
380
|
+
const i = (l, a = "") => l ? Object.entries(l).flatMap(([u, s]) => {
|
|
381
|
+
const c = a ? `${a}${isNaN(+u) ? `.${u}` : `[${u}]`}` : u;
|
|
382
|
+
return typeof s == "object" && !Array.isArray(s) ? i(s, c) : `${c}: ${Array.isArray(s) ? s.join(", ") : s}`;
|
|
383
|
+
}) : [], n = (typeof r == "object" ? i(r) : r.toString().split(`
|
|
384
|
+
`)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
|
|
385
|
+
`]), m = [];
|
|
386
|
+
for (let l = 0; l < n.length; ) {
|
|
387
|
+
let a = "", u = l;
|
|
388
|
+
for (; u < n.length; ) {
|
|
389
|
+
const c = a + (a ? " " : "") + n[u];
|
|
390
|
+
if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
|
|
391
|
+
`)) > e && a) break;
|
|
392
|
+
a = c, u++;
|
|
393
393
|
}
|
|
394
|
-
const
|
|
394
|
+
const s = a.replace(/\s*\n\s*/g, `
|
|
395
395
|
`).trim();
|
|
396
|
-
|
|
396
|
+
s && m.push(s), l = Math.max(u - t, u === l ? l + 1 : u);
|
|
397
397
|
}
|
|
398
|
-
return
|
|
398
|
+
return m;
|
|
399
399
|
}
|
|
400
400
|
/**
|
|
401
401
|
* Create a vector representation of a string
|
|
@@ -404,39 +404,39 @@ ${u}` }), e.tools = [{
|
|
|
404
404
|
* @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
|
|
405
405
|
*/
|
|
406
406
|
embedding(r, e = {}) {
|
|
407
|
-
let { maxTokens: t = 500, overlapTokens:
|
|
407
|
+
let { maxTokens: t = 500, overlapTokens: i = 50 } = e, o = !1;
|
|
408
408
|
const n = () => {
|
|
409
|
-
|
|
410
|
-
},
|
|
411
|
-
if (
|
|
412
|
-
const
|
|
413
|
-
|
|
409
|
+
o = !0;
|
|
410
|
+
}, m = (a) => new Promise((u, s) => {
|
|
411
|
+
if (o) return s(new Error("Aborted"));
|
|
412
|
+
const c = [
|
|
413
|
+
H(G(B(import.meta.url)), "embedder.js"),
|
|
414
414
|
this.ai.options.path,
|
|
415
415
|
this.ai.options?.embedder || "bge-small-en-v1.5"
|
|
416
|
-
], d =
|
|
417
|
-
d.stdin.write(
|
|
416
|
+
], d = w("node", c, { stdio: ["pipe", "pipe", "ignore"] });
|
|
417
|
+
d.stdin.write(a), d.stdin.end();
|
|
418
418
|
let p = "";
|
|
419
419
|
d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
|
|
420
|
-
if (
|
|
420
|
+
if (o) return s(new Error("Aborted"));
|
|
421
421
|
if (f === 0)
|
|
422
422
|
try {
|
|
423
423
|
const g = JSON.parse(p);
|
|
424
|
-
|
|
424
|
+
u(g.embedding);
|
|
425
425
|
} catch {
|
|
426
|
-
|
|
426
|
+
s(new Error("Failed to parse embedding output"));
|
|
427
427
|
}
|
|
428
428
|
else
|
|
429
|
-
|
|
430
|
-
}), d.on("error",
|
|
431
|
-
}),
|
|
432
|
-
const
|
|
433
|
-
for (let
|
|
434
|
-
const
|
|
435
|
-
|
|
429
|
+
s(new Error(`Embedder process exited with code ${f}`));
|
|
430
|
+
}), d.on("error", s);
|
|
431
|
+
}), l = (async () => {
|
|
432
|
+
const a = this.chunk(r, t, i), u = [];
|
|
433
|
+
for (let s = 0; s < a.length && !o; s++) {
|
|
434
|
+
const c = a[s], d = await m(c);
|
|
435
|
+
u.push({ index: s, embedding: d, text: c, tokens: this.estimateTokens(c) });
|
|
436
436
|
}
|
|
437
|
-
return
|
|
437
|
+
return u;
|
|
438
438
|
})();
|
|
439
|
-
return Object.assign(
|
|
439
|
+
return Object.assign(l, { abort: n });
|
|
440
440
|
}
|
|
441
441
|
/**
|
|
442
442
|
* Estimate variable as tokens
|
|
@@ -455,8 +455,8 @@ ${u}` }), e.tools = [{
|
|
|
455
455
|
*/
|
|
456
456
|
fuzzyMatch(r, ...e) {
|
|
457
457
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
458
|
-
const t = (n,
|
|
459
|
-
return { avg:
|
|
458
|
+
const t = (n, m = 10) => n.toLowerCase().split("").map((l, a) => l.charCodeAt(0) * (a + 1) % m / m).slice(0, m), i = t(r), o = e.map((n) => t(n)).map((n) => this.cosineSimilarity(i, n));
|
|
459
|
+
return { avg: o.reduce((n, m) => n + m, 0) / o.length, max: Math.max(...o), similarities: o };
|
|
460
460
|
}
|
|
461
461
|
/**
|
|
462
462
|
* Ask a question with JSON response
|
|
@@ -466,15 +466,35 @@ ${u}` }), e.tools = [{
|
|
|
466
466
|
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
467
467
|
*/
|
|
468
468
|
async json(r, e, t) {
|
|
469
|
-
|
|
470
|
-
t?.system,
|
|
471
|
-
`Only respond using JSON matching this schema:
|
|
469
|
+
let i = `Your job is to convert input to JSON. Call \`submit\` exactly once with JSON matching this schema:
|
|
472
470
|
\`\`\`json
|
|
473
471
|
${e}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
472
|
+
\`\`\``;
|
|
473
|
+
return t?.system && (i += `
|
|
474
|
+
|
|
475
|
+
` + t.system), new Promise(async (o, n) => {
|
|
476
|
+
let m = !1;
|
|
477
|
+
const l = await this.ask(r, {
|
|
478
|
+
temperature: 0.3,
|
|
479
|
+
...t,
|
|
480
|
+
system: i,
|
|
481
|
+
tools: [{
|
|
482
|
+
name: "submit",
|
|
483
|
+
description: "Submit JSON",
|
|
484
|
+
args: { json: { type: "string", description: "Javascript parsable JSON string", required: !0 } },
|
|
485
|
+
fn: (a) => {
|
|
486
|
+
try {
|
|
487
|
+
const u = JSON.parse(a.json);
|
|
488
|
+
o(u), m = !0;
|
|
489
|
+
} catch {
|
|
490
|
+
return "Invalid JSON";
|
|
491
|
+
}
|
|
492
|
+
return "Done";
|
|
493
|
+
}
|
|
494
|
+
}, ...t?.tools || []]
|
|
495
|
+
});
|
|
496
|
+
m || n(`AI failed to create summary: ${l}`);
|
|
497
|
+
});
|
|
478
498
|
}
|
|
479
499
|
/**
|
|
480
500
|
* Create a summary of some text
|
|
@@ -483,8 +503,25 @@ ${e}
|
|
|
483
503
|
* @param options LLM request options
|
|
484
504
|
* @returns {Promise<string>} Summary
|
|
485
505
|
*/
|
|
486
|
-
summarize(r, e = 500, t) {
|
|
487
|
-
|
|
506
|
+
async summarize(r, e = 500, t) {
|
|
507
|
+
let i = `Your job is to summarize the users message. Call \`submit\` exactly once with the shortest summary possible that's <= ${e} tokens. Output nothing else`;
|
|
508
|
+
return t?.system && (i += `
|
|
509
|
+
|
|
510
|
+
` + t.system), new Promise(async (o, n) => {
|
|
511
|
+
let m = !1;
|
|
512
|
+
const l = await this.ask(r, {
|
|
513
|
+
temperature: 0.3,
|
|
514
|
+
...t,
|
|
515
|
+
system: i,
|
|
516
|
+
tools: [{
|
|
517
|
+
name: "submit",
|
|
518
|
+
description: "Submit summary",
|
|
519
|
+
args: { summary: { type: "string", description: "Summarization", required: !0 } },
|
|
520
|
+
fn: (a) => (m = !0, o(a.summary || null), "Done")
|
|
521
|
+
}, ...t?.tools || []]
|
|
522
|
+
});
|
|
523
|
+
m || n(`AI failed to create summary: ${l}`);
|
|
524
|
+
});
|
|
488
525
|
}
|
|
489
526
|
}
|
|
490
527
|
class ee {
|
|
@@ -510,21 +547,21 @@ print(json.dumps(segments))
|
|
|
510
547
|
pyannote;
|
|
511
548
|
whisperModel;
|
|
512
549
|
async addPunctuation(r, e, t = 150) {
|
|
513
|
-
const
|
|
550
|
+
const i = (n) => {
|
|
514
551
|
if (n = n.toLowerCase().replace(/[^a-z]/g, ""), n.length <= 3) return 1;
|
|
515
|
-
const
|
|
516
|
-
let
|
|
517
|
-
return n.endsWith("e") &&
|
|
552
|
+
const m = n.match(/[aeiouy]+/g);
|
|
553
|
+
let l = m ? m.length : 1;
|
|
554
|
+
return n.endsWith("e") && l--, Math.max(1, l);
|
|
518
555
|
};
|
|
519
|
-
let
|
|
520
|
-
return r.transcription.filter((n,
|
|
521
|
-
let
|
|
522
|
-
const
|
|
523
|
-
return !n.text &&
|
|
556
|
+
let o = "";
|
|
557
|
+
return r.transcription.filter((n, m) => {
|
|
558
|
+
let l = !1;
|
|
559
|
+
const a = r.transcription[m - 1], u = r.transcription[m + 1];
|
|
560
|
+
return !n.text && u ? (u.offsets.from = n.offsets.from, u.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && a && (a.offsets.to = n.offsets.to, a.timestamps.to = n.timestamps.to, a.text += n.text, l = !0), !!n.text && !l;
|
|
524
561
|
}).forEach((n) => {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
}), e ? this.ai.language.ask(
|
|
562
|
+
const m = /^[A-Z]/.test(n.text.trim()), l = n.offsets.to - n.offsets.from, u = i(n.text.trim()) * t;
|
|
563
|
+
m && l > u * 2 && n.text[0] == " " && (o += "."), o += n.text;
|
|
564
|
+
}), e ? this.ai.language.ask(o, {
|
|
528
565
|
system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
|
|
529
566
|
temperature: 0.1,
|
|
530
567
|
tools: [{
|
|
@@ -534,138 +571,138 @@ print(json.dumps(segments))
|
|
|
534
571
|
find: { type: "string", description: "Text to find", required: !0 },
|
|
535
572
|
replace: { type: "string", description: "Text to replace", required: !0 }
|
|
536
573
|
},
|
|
537
|
-
fn: (n) =>
|
|
574
|
+
fn: (n) => o = o.replace(n.find, n.replace)
|
|
538
575
|
}]
|
|
539
|
-
}).then(() =>
|
|
576
|
+
}).then(() => o) : o.trim();
|
|
540
577
|
}
|
|
541
578
|
async diarizeTranscript(r, e, t) {
|
|
542
|
-
const
|
|
543
|
-
let
|
|
579
|
+
const i = /* @__PURE__ */ new Map();
|
|
580
|
+
let o = 0;
|
|
544
581
|
e.forEach((p) => {
|
|
545
|
-
|
|
582
|
+
i.has(p.speaker) || i.set(p.speaker, ++o);
|
|
546
583
|
});
|
|
547
|
-
const n = await this.addPunctuation(r, t),
|
|
584
|
+
const n = await this.addPunctuation(r, t), m = n.match(/[^.!?]+[.!?]+/g) || [n], l = r.transcription.filter((p) => p.text.trim()), a = m.map((p) => {
|
|
548
585
|
if (p = p.trim(), !p) return null;
|
|
549
586
|
const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
|
|
550
|
-
f.forEach((
|
|
551
|
-
const
|
|
552
|
-
if (!
|
|
553
|
-
const
|
|
554
|
-
if (
|
|
555
|
-
const y =
|
|
587
|
+
f.forEach((x) => {
|
|
588
|
+
const k = l.find((y) => x === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
|
|
589
|
+
if (!k) return;
|
|
590
|
+
const v = k.offsets.from / 1e3, $ = e.find((y) => v >= y.start && v <= y.end);
|
|
591
|
+
if ($) {
|
|
592
|
+
const y = i.get($.speaker);
|
|
556
593
|
g.set(y, (g.get(y) || 0) + 1);
|
|
557
594
|
}
|
|
558
595
|
});
|
|
559
596
|
let T = 1, E = 0;
|
|
560
|
-
return g.forEach((
|
|
561
|
-
|
|
597
|
+
return g.forEach((x, k) => {
|
|
598
|
+
x > E && (E = x, T = k);
|
|
562
599
|
}), { speaker: T, text: p };
|
|
563
|
-
}).filter((p) => p !== null),
|
|
564
|
-
|
|
565
|
-
const f =
|
|
566
|
-
f && f.speaker === p.speaker ? f.text += " " + p.text :
|
|
600
|
+
}).filter((p) => p !== null), u = [];
|
|
601
|
+
a.forEach((p) => {
|
|
602
|
+
const f = u[u.length - 1];
|
|
603
|
+
f && f.speaker === p.speaker ? f.text += " " + p.text : u.push({ ...p });
|
|
567
604
|
});
|
|
568
|
-
let
|
|
605
|
+
let s = u.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
|
|
569
606
|
`).trim();
|
|
570
|
-
if (!t) return
|
|
571
|
-
let
|
|
572
|
-
|
|
573
|
-
const d = await this.ai.language.json(
|
|
607
|
+
if (!t) return s;
|
|
608
|
+
let c = this.ai.language.chunk(s, 500, 0);
|
|
609
|
+
c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
|
|
610
|
+
const d = await this.ai.language.json(c.join(`
|
|
574
611
|
`), '{1: "Detected Name", 2: "Second Name"}', {
|
|
575
612
|
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",
|
|
576
613
|
temperature: 0.1
|
|
577
614
|
});
|
|
578
|
-
return Object.entries(d).forEach(([p, f]) =>
|
|
615
|
+
return Object.entries(d).forEach(([p, f]) => s = s.replaceAll(`[Speaker ${p}]`, `[${f}]`)), s;
|
|
579
616
|
}
|
|
580
617
|
runAsr(r, e = {}) {
|
|
581
618
|
let t;
|
|
582
|
-
const
|
|
583
|
-
this.downloadAsrModel(e.model).then((
|
|
619
|
+
const i = new Promise((o, n) => {
|
|
620
|
+
this.downloadAsrModel(e.model).then((m) => {
|
|
584
621
|
if (e.diarization) {
|
|
585
|
-
let
|
|
586
|
-
t =
|
|
622
|
+
let l = M.join(M.dirname(r), "transcript");
|
|
623
|
+
t = w(
|
|
587
624
|
this.ai.options.whisper,
|
|
588
|
-
["-m",
|
|
625
|
+
["-m", m, "-f", r, "-np", "-ml", "1", "-oj", "-of", l],
|
|
589
626
|
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
590
|
-
), t.on("error", (
|
|
591
|
-
if (
|
|
592
|
-
|
|
627
|
+
), t.on("error", (a) => n(a)), t.on("close", async (a) => {
|
|
628
|
+
if (a === 0) {
|
|
629
|
+
l = await b.readFile(l + ".json", "utf-8"), b.rm(l + ".json").catch(() => {
|
|
593
630
|
});
|
|
594
631
|
try {
|
|
595
|
-
|
|
632
|
+
o(JSON.parse(l));
|
|
596
633
|
} catch {
|
|
597
634
|
n(new Error("Failed to parse whisper JSON"));
|
|
598
635
|
}
|
|
599
636
|
} else
|
|
600
|
-
n(new Error(`Exit code ${
|
|
637
|
+
n(new Error(`Exit code ${a}`));
|
|
601
638
|
});
|
|
602
639
|
} else {
|
|
603
|
-
let
|
|
604
|
-
t =
|
|
605
|
-
|
|
640
|
+
let l = "";
|
|
641
|
+
t = w(this.ai.options.whisper, ["-m", m, "-f", r, "-np", "-nt"]), t.on("error", (a) => n(a)), t.stdout.on("data", (a) => l += a.toString()), t.on("close", async (a) => {
|
|
642
|
+
a === 0 ? o(l.trim() || null) : n(new Error(`Exit code ${a}`));
|
|
606
643
|
});
|
|
607
644
|
}
|
|
608
645
|
});
|
|
609
646
|
});
|
|
610
|
-
return Object.assign(
|
|
647
|
+
return Object.assign(i, { abort: () => t?.kill("SIGTERM") });
|
|
611
648
|
}
|
|
612
649
|
runDiarization(r) {
|
|
613
650
|
let e = !1, t = () => {
|
|
614
651
|
e = !0;
|
|
615
652
|
};
|
|
616
|
-
const
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
}),
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
]).then((async ([n,
|
|
653
|
+
const i = (n) => new Promise((m) => {
|
|
654
|
+
const l = w(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
|
|
655
|
+
l.on("close", (a) => m(a === 0)), l.on("error", () => m(!1));
|
|
656
|
+
}), o = Promise.all([
|
|
657
|
+
i("python"),
|
|
658
|
+
i("python3")
|
|
659
|
+
]).then((async ([n, m]) => {
|
|
623
660
|
if (e) return;
|
|
624
|
-
if (!n && !
|
|
625
|
-
const
|
|
626
|
-
return new Promise((
|
|
661
|
+
if (!n && !m) throw new Error("Pyannote is not installed: pip install pyannote.audio");
|
|
662
|
+
const l = m ? "python3" : "python";
|
|
663
|
+
return new Promise((a, u) => {
|
|
627
664
|
if (e) return;
|
|
628
|
-
let
|
|
629
|
-
const
|
|
630
|
-
|
|
665
|
+
let s = "";
|
|
666
|
+
const c = w(l, ["-W", "ignore", "-c", this.pyannote, r]);
|
|
667
|
+
c.stdout.on("data", (d) => s += d.toString()), c.stderr.on("data", (d) => console.error(d.toString())), c.on("close", (d) => {
|
|
631
668
|
if (d === 0)
|
|
632
669
|
try {
|
|
633
|
-
|
|
670
|
+
a(JSON.parse(s));
|
|
634
671
|
} catch {
|
|
635
|
-
|
|
672
|
+
u(new Error("Failed to parse diarization output"));
|
|
636
673
|
}
|
|
637
674
|
else
|
|
638
|
-
|
|
639
|
-
}),
|
|
675
|
+
u(new Error(`Python process exited with code ${d}`));
|
|
676
|
+
}), c.on("error", u), t = () => c.kill("SIGTERM");
|
|
640
677
|
});
|
|
641
678
|
}));
|
|
642
|
-
return Object.assign(
|
|
679
|
+
return Object.assign(o, { abort: t });
|
|
643
680
|
}
|
|
644
681
|
asr(r, e = {}) {
|
|
645
682
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
646
|
-
const t =
|
|
683
|
+
const t = A(Y(A(U(), "audio-")), "converted.wav");
|
|
647
684
|
K(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
|
|
648
|
-
const
|
|
685
|
+
const i = () => b.rm(q.dirname(t), { recursive: !0, force: !0 }).catch(() => {
|
|
649
686
|
});
|
|
650
687
|
if (!e.diarization) return this.runAsr(t, { model: e.model });
|
|
651
|
-
const
|
|
652
|
-
let
|
|
653
|
-
|
|
688
|
+
const o = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
|
|
689
|
+
let m = !1, l = () => {
|
|
690
|
+
m = !0, o.abort(), n.abort(), i();
|
|
654
691
|
};
|
|
655
|
-
const
|
|
656
|
-
if (
|
|
657
|
-
` +
|
|
658
|
-
if (
|
|
659
|
-
` +
|
|
660
|
-
return
|
|
661
|
-
}).finally(() =>
|
|
662
|
-
return Object.assign(
|
|
692
|
+
const a = Promise.allSettled([o, n]).then(async ([u, s]) => {
|
|
693
|
+
if (u.status == "rejected") throw new Error(`Whisper.cpp timestamps:
|
|
694
|
+
` + u.reason);
|
|
695
|
+
if (s.status == "rejected") throw new Error(`Pyannote:
|
|
696
|
+
` + s.reason);
|
|
697
|
+
return m || !e.diarization ? u.value : this.diarizeTranscript(u.value, s.value, e.diarization == "llm");
|
|
698
|
+
}).finally(() => i());
|
|
699
|
+
return Object.assign(a, { abort: l });
|
|
663
700
|
}
|
|
664
701
|
async downloadAsrModel(r = this.whisperModel) {
|
|
665
702
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
666
703
|
r.endsWith(".bin") || (r += ".bin");
|
|
667
|
-
const e =
|
|
668
|
-
return await
|
|
704
|
+
const e = q.join(this.ai.options.path, r);
|
|
705
|
+
return await b.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 b.writeFile(e, t), delete this.downloads[r], e)), this.downloads[r]);
|
|
669
706
|
}
|
|
670
707
|
}
|
|
671
708
|
class te {
|
|
@@ -679,15 +716,15 @@ class te {
|
|
|
679
716
|
*/
|
|
680
717
|
ocr(r) {
|
|
681
718
|
let e;
|
|
682
|
-
const t = new Promise(async (
|
|
683
|
-
e = await
|
|
684
|
-
const { data:
|
|
685
|
-
await e.terminate(),
|
|
719
|
+
const t = new Promise(async (i) => {
|
|
720
|
+
e = await V(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
721
|
+
const { data: o } = await e.recognize(r);
|
|
722
|
+
await e.terminate(), i(o.text.trim() || null);
|
|
686
723
|
});
|
|
687
724
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
688
725
|
}
|
|
689
726
|
}
|
|
690
|
-
class
|
|
727
|
+
class be {
|
|
691
728
|
constructor(r) {
|
|
692
729
|
this.options = r, r.path || (r.path = O.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new ee(this), this.language = new X(this), this.vision = new te(this);
|
|
693
730
|
}
|
|
@@ -703,17 +740,17 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
703
740
|
description: "Use the command line interface, returns any output",
|
|
704
741
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
705
742
|
fn: (h) => j`${h.command}`
|
|
706
|
-
},
|
|
743
|
+
}, we = {
|
|
707
744
|
name: "get_datetime",
|
|
708
745
|
description: "Get local date / time",
|
|
709
746
|
args: {},
|
|
710
747
|
fn: async () => (/* @__PURE__ */ new Date()).toString()
|
|
711
|
-
},
|
|
748
|
+
}, xe = {
|
|
712
749
|
name: "get_datetime_utc",
|
|
713
750
|
description: "Get current UTC date / time",
|
|
714
751
|
args: {},
|
|
715
752
|
fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
|
|
716
|
-
},
|
|
753
|
+
}, ke = {
|
|
717
754
|
name: "exec",
|
|
718
755
|
description: "Run code/scripts",
|
|
719
756
|
args: {
|
|
@@ -736,7 +773,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
736
773
|
return { error: t?.message || t.toString() };
|
|
737
774
|
}
|
|
738
775
|
}
|
|
739
|
-
},
|
|
776
|
+
}, Se = {
|
|
740
777
|
name: "fetch",
|
|
741
778
|
description: "Make HTTP request to URL",
|
|
742
779
|
args: {
|
|
@@ -745,7 +782,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
745
782
|
headers: { type: "object", description: "HTTP headers to send", default: {} },
|
|
746
783
|
body: { type: "object", description: "HTTP body to send" }
|
|
747
784
|
},
|
|
748
|
-
fn: (h) => new
|
|
785
|
+
fn: (h) => new C({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
|
|
749
786
|
}, se = {
|
|
750
787
|
name: "exec_javascript",
|
|
751
788
|
description: "Execute commonjs javascript",
|
|
@@ -753,7 +790,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
753
790
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
754
791
|
},
|
|
755
792
|
fn: async (h) => {
|
|
756
|
-
const r =
|
|
793
|
+
const r = J(null), e = await I({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
|
|
757
794
|
return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
|
|
758
795
|
}
|
|
759
796
|
}, oe = {
|
|
@@ -763,32 +800,80 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
763
800
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
764
801
|
},
|
|
765
802
|
fn: async (h) => ({ result: j`python -c "${h.code}"` })
|
|
766
|
-
},
|
|
803
|
+
}, _e = {
|
|
767
804
|
name: "read_webpage",
|
|
768
|
-
description: "Extract clean, structured content from a webpage
|
|
805
|
+
description: "Extract clean, structured content from a webpage or convert media/documents to accessible formats",
|
|
769
806
|
args: {
|
|
770
807
|
url: { type: "string", description: "URL to extract content from", required: !0 },
|
|
771
|
-
|
|
808
|
+
mimeRegex: { type: "string", description: 'Optional: Regex pattern to filter MIME types (e.g., "^image/", "text/", "application/pdf")' },
|
|
809
|
+
maxSize: { type: "number", description: "Optional: Max file size in bytes for binary content (default: 10MB)" }
|
|
772
810
|
},
|
|
773
811
|
fn: async (h) => {
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
812
|
+
const e = await fetch(h.url, {
|
|
813
|
+
headers: {
|
|
814
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
815
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
|
816
|
+
"Accept-Language": "en-US,en;q=0.5"
|
|
817
|
+
},
|
|
818
|
+
redirect: "follow"
|
|
819
|
+
}).catch((s) => {
|
|
820
|
+
throw new Error(`Failed to fetch: ${s.message}`);
|
|
821
|
+
}), t = e.headers.get("content-type") || "", i = t.split(";")[0].trim().toLowerCase();
|
|
822
|
+
if (t.match(/charset=([^;]+)/)?.[1], h.mimeRegex && !new RegExp(h.mimeRegex, "i").test(i))
|
|
823
|
+
return { url: h.url, error: "MIME type rejected", mimeType: i, filter: h.mimeRegex };
|
|
824
|
+
if (i.startsWith("image/") || i.startsWith("audio/") || i.startsWith("video/")) {
|
|
825
|
+
const s = await e.arrayBuffer();
|
|
826
|
+
if (s.byteLength > 10485760)
|
|
827
|
+
return { url: h.url, type: "media", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
|
|
828
|
+
const c = Buffer.from(s).toString("base64");
|
|
829
|
+
return { url: h.url, type: "media", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
|
|
830
|
+
}
|
|
831
|
+
if (i.match(/^(text\/(plain|csv|xml)|application\/(json|xml|csv|x-yaml))/) || h.url.match(/\.(txt|json|xml|csv|yaml|yml|md)$/i)) {
|
|
832
|
+
const s = await e.text();
|
|
833
|
+
return { url: h.url, type: "text", mimeType: i, content: s.slice(0, 1e5) };
|
|
834
|
+
}
|
|
835
|
+
if (i === "application/pdf" || i.startsWith("application/") && !i.includes("html")) {
|
|
836
|
+
const s = await e.arrayBuffer();
|
|
837
|
+
if (s.byteLength > 10485760)
|
|
838
|
+
return { url: h.url, type: "binary", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
|
|
839
|
+
const c = Buffer.from(s).toString("base64");
|
|
840
|
+
return { url: h.url, type: "binary", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
|
|
841
|
+
}
|
|
842
|
+
const o = await e.text(), n = Z.load(o);
|
|
843
|
+
n('script, style, nav, footer, header, aside, iframe, noscript, svg, [role="navigation"], [role="banner"], [role="complementary"], .ad, .ads, .advertisement, .cookie, .popup, .modal, .sidebar, .related, .comments, .social-share').remove();
|
|
844
|
+
const m = {
|
|
845
|
+
title: n('meta[property="og:title"]').attr("content") || n("title").text() || "",
|
|
846
|
+
description: n('meta[name="description"]').attr("content") || n('meta[property="og:description"]').attr("content") || "",
|
|
847
|
+
author: n('meta[name="author"]').attr("content") || "",
|
|
848
|
+
published: n('meta[property="article:published_time"]').attr("content") || n("time").attr("datetime") || "",
|
|
849
|
+
image: n('meta[property="og:image"]').attr("content") || ""
|
|
781
850
|
};
|
|
782
|
-
let
|
|
783
|
-
const
|
|
784
|
-
for (const
|
|
785
|
-
const c =
|
|
851
|
+
let l = "";
|
|
852
|
+
const a = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content", "body"];
|
|
853
|
+
for (const s of a) {
|
|
854
|
+
const c = n(s).first();
|
|
786
855
|
if (c.length && c.text().trim().length > 200) {
|
|
787
|
-
|
|
856
|
+
l = c.text();
|
|
788
857
|
break;
|
|
789
858
|
}
|
|
790
859
|
}
|
|
791
|
-
|
|
860
|
+
l || (l = n("body").text()), l = l.replace(/\n\s*\n\s*\n/g, `
|
|
861
|
+
|
|
862
|
+
`).replace(/[ \t]+/g, " ").trim().slice(0, 5e4);
|
|
863
|
+
let u = [];
|
|
864
|
+
return l.length < 500 && (n("a[href]").each((s, c) => {
|
|
865
|
+
const d = n(c).attr("href"), p = n(c).text().trim();
|
|
866
|
+
d && p && !d.startsWith("#") && u.push({ text: p, href: d });
|
|
867
|
+
}), u = u.slice(0, 50)), {
|
|
868
|
+
url: h.url,
|
|
869
|
+
type: "html",
|
|
870
|
+
title: m.title.trim(),
|
|
871
|
+
description: m.description.trim(),
|
|
872
|
+
author: m.author.trim(),
|
|
873
|
+
published: m.published,
|
|
874
|
+
content: l,
|
|
875
|
+
links: u.length ? u : void 0
|
|
876
|
+
};
|
|
792
877
|
}
|
|
793
878
|
}, je = {
|
|
794
879
|
name: "web_search",
|
|
@@ -800,30 +885,30 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
800
885
|
fn: async (h) => {
|
|
801
886
|
const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
|
|
802
887
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
803
|
-
}).then((
|
|
888
|
+
}).then((o) => o.text());
|
|
804
889
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
805
|
-
const
|
|
890
|
+
const i = new D();
|
|
806
891
|
for (; (e = t.exec(r)) !== null; ) {
|
|
807
|
-
let
|
|
808
|
-
if (
|
|
892
|
+
let o = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
893
|
+
if (o && (o = decodeURIComponent(o)), o && i.add(o), i.size >= (h.length || 5)) break;
|
|
809
894
|
}
|
|
810
|
-
return
|
|
895
|
+
return i;
|
|
811
896
|
}
|
|
812
897
|
};
|
|
813
898
|
export {
|
|
814
|
-
|
|
899
|
+
be as Ai,
|
|
815
900
|
Q as Anthropic,
|
|
816
901
|
ee as Audio,
|
|
817
902
|
ne as CliTool,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
903
|
+
we as DateTimeTool,
|
|
904
|
+
xe as DateTimeUTCTool,
|
|
905
|
+
ke as ExecTool,
|
|
906
|
+
Se as FetchTool,
|
|
822
907
|
se as JSTool,
|
|
823
|
-
|
|
824
|
-
|
|
908
|
+
L as LLMProvider,
|
|
909
|
+
P as OpenAi,
|
|
825
910
|
oe as PythonTool,
|
|
826
|
-
|
|
911
|
+
_e as ReadWebpageTool,
|
|
827
912
|
te as Vision,
|
|
828
913
|
je as WebSearchTool
|
|
829
914
|
};
|