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