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