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