@ztimson/ai-utils 0.7.6 → 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 -2
- 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 +33 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -257
- package/dist/index.mjs.map +1 -1
- package/dist/llm.d.ts +1 -1
- 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 -101
- package/dist/asr.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,109 +1,112 @@
|
|
|
1
1
|
import * as $ from "node:os";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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(
|
|
23
|
+
toStandard(r) {
|
|
21
24
|
const e = Date.now(), t = [];
|
|
22
|
-
for (let
|
|
23
|
-
if (typeof
|
|
24
|
-
t.push({ timestamp: e, ...
|
|
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
|
-
t.push({ timestamp: e, role: "tool", id:
|
|
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
41
|
return t;
|
|
39
42
|
}
|
|
40
|
-
fromStandard(
|
|
41
|
-
for (let e = 0; e <
|
|
42
|
-
if (
|
|
43
|
-
const t =
|
|
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
50
|
{ role: "assistant", content: [{ type: "tool_use", id: t.id, name: t.name, input: t.args }] },
|
|
48
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(
|
|
56
|
+
ask(r, e = {}) {
|
|
54
57
|
const t = new AbortController();
|
|
55
|
-
return Object.assign(new Promise(async (
|
|
56
|
-
let
|
|
57
|
-
const
|
|
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 ?
|
|
70
|
+
properties: m.args ? j(m.args, (i, d) => ({ ...d, required: void 0 })) : {},
|
|
68
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 (
|
|
80
|
+
if (c = await this.client.messages.create(o).catch((i) => {
|
|
78
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 i of
|
|
88
|
+
` }), c.content = [];
|
|
89
|
+
for await (const i of c) {
|
|
87
90
|
if (t.signal.aborted) break;
|
|
88
91
|
if (i.type === "content_block_start")
|
|
89
|
-
i.content_block.type === "text" ?
|
|
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: "" });
|
|
90
93
|
else if (i.type === "content_block_delta")
|
|
91
94
|
if (i.delta.type === "text_delta") {
|
|
92
95
|
const d = i.delta.text;
|
|
93
|
-
|
|
94
|
-
} else i.delta.type === "input_json_delta" && (
|
|
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);
|
|
95
98
|
else if (i.type === "content_block_stop") {
|
|
96
|
-
const d =
|
|
97
|
-
d.input != null && (d.input = d.input ?
|
|
99
|
+
const d = c.content.at(-1);
|
|
100
|
+
d.input != null && (d.input = d.input ? w(d.input, {}) : {});
|
|
98
101
|
} else if (i.type === "message_stop")
|
|
99
102
|
break;
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
|
-
const m =
|
|
105
|
+
const m = c.content.filter((i) => i.type === "tool_use");
|
|
103
106
|
if (m.length && !t.signal.aborted) {
|
|
104
|
-
|
|
107
|
+
n.push({ role: "assistant", content: c.content });
|
|
105
108
|
const i = await Promise.all(m.map(async (d) => {
|
|
106
|
-
const p =
|
|
109
|
+
const p = s.find(T("name", d.name));
|
|
107
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
112
|
const u = await p.fn(d.input, e?.stream, this.ai);
|
|
@@ -112,45 +115,45 @@ ${JSON.stringify(r, null, 2)}`, i;
|
|
|
112
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 (!t.signal.aborted &&
|
|
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
|
-
`) }),
|
|
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);
|
|
121
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
131
|
apiKey: t
|
|
129
132
|
}));
|
|
130
133
|
}
|
|
131
134
|
client;
|
|
132
|
-
toStandard(
|
|
133
|
-
for (let e = 0; e <
|
|
134
|
-
const t =
|
|
135
|
+
toStandard(r) {
|
|
136
|
+
for (let e = 0; e < r.length; e++) {
|
|
137
|
+
const t = r[e];
|
|
135
138
|
if (t.role === "assistant" && t.tool_calls) {
|
|
136
|
-
const
|
|
139
|
+
const l = t.tool_calls.map((n) => ({
|
|
137
140
|
role: "tool",
|
|
138
|
-
id:
|
|
139
|
-
name:
|
|
140
|
-
args:
|
|
141
|
+
id: n.id,
|
|
142
|
+
name: n.function.name,
|
|
143
|
+
args: w(n.function.arguments, {}),
|
|
141
144
|
timestamp: t.timestamp
|
|
142
145
|
}));
|
|
143
|
-
|
|
146
|
+
r.splice(e, 1, ...l), e += l.length - 1;
|
|
144
147
|
} else if (t.role === "tool" && t.content) {
|
|
145
|
-
const
|
|
146
|
-
|
|
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
|
|
155
|
+
fromStandard(r) {
|
|
156
|
+
return r.reduce((e, t) => {
|
|
154
157
|
if (t.role === "tool")
|
|
155
158
|
e.push({
|
|
156
159
|
role: "assistant",
|
|
@@ -164,31 +167,31 @@ class w extends q {
|
|
|
164
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(
|
|
176
|
+
ask(r, e = {}) {
|
|
174
177
|
const t = new AbortController();
|
|
175
|
-
return Object.assign(new Promise(async (
|
|
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
190
|
name: i.name,
|
|
188
191
|
description: i.description,
|
|
189
192
|
parameters: {
|
|
190
193
|
type: "object",
|
|
191
|
-
properties: i.args ?
|
|
194
|
+
properties: i.args ? j(i.args, (d, p) => ({ ...p, required: void 0 })) : {},
|
|
192
195
|
required: i.args ? Object.entries(i.args).filter((d) => d[1].required).map((d) => d[0]) : []
|
|
193
196
|
}
|
|
194
197
|
}
|
|
@@ -196,11 +199,11 @@ class w extends q {
|
|
|
196
199
|
};
|
|
197
200
|
let a, m = !0;
|
|
198
201
|
do {
|
|
199
|
-
if (a = await this.client.chat.completions.create(
|
|
202
|
+
if (a = await this.client.chat.completions.create(c).catch((d) => {
|
|
200
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
|
|
|
@@ -212,28 +215,28 @@ ${JSON.stringify(n, null, 2)}`, d;
|
|
|
212
215
|
}
|
|
213
216
|
const i = a.choices[0].message.tool_calls || [];
|
|
214
217
|
if (i.length && !t.signal.aborted) {
|
|
215
|
-
|
|
218
|
+
s.push(a.choices[0].message);
|
|
216
219
|
const d = await Promise.all(i.map(async (p) => {
|
|
217
|
-
const u =
|
|
220
|
+
const u = o?.find(T("name", p.function.name));
|
|
218
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
231
|
} while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
|
|
229
|
-
|
|
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);
|
|
230
233
|
}), { abort: () => t.abort() });
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
|
-
class
|
|
234
|
-
constructor(
|
|
235
|
-
this.ai =
|
|
236
|
-
this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new
|
|
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 z {
|
|
|
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(
|
|
250
|
+
ask(r, e = {}) {
|
|
248
251
|
const t = e.model || this.defaultModel;
|
|
249
252
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
250
|
-
let
|
|
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
|
|
260
|
+
const o = async (a, m, i = 50) => {
|
|
258
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
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);
|
|
263
|
-
},
|
|
264
|
-
|
|
265
|
-
` +
|
|
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",
|
|
@@ -273,30 +276,30 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
273
276
|
},
|
|
274
277
|
fn: (a) => {
|
|
275
278
|
if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
|
|
276
|
-
return
|
|
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 u of
|
|
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:
|
|
323
|
+
model: l?.model,
|
|
324
|
+
temperature: l?.temperature || 0.3
|
|
322
325
|
}), i = /* @__PURE__ */ new Date(), d = await Promise.all((m?.facts || [])?.map(async ([u, f]) => {
|
|
323
326
|
const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
|
|
324
327
|
return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: i };
|
|
325
|
-
})), p = [{ role: "assistant", content: `Conversation Summary: ${m?.summary}`, timestamp: Date.now() }, ...
|
|
326
|
-
return
|
|
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 t = 0,
|
|
337
|
-
for (let
|
|
338
|
-
t +=
|
|
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
|
|
352
|
+
chunk(r, e = 500, t = 50) {
|
|
353
|
+
const l = (c, a = "") => c ? Object.entries(c).flatMap(([m, i]) => {
|
|
351
354
|
const d = a ? `${a}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
|
|
352
|
-
return typeof i == "object" && !Array.isArray(i) ?
|
|
353
|
-
}) : [],
|
|
354
|
-
`)).flatMap((
|
|
355
|
-
`]),
|
|
356
|
-
for (let
|
|
357
|
-
let a = "", m =
|
|
358
|
-
for (; m <
|
|
359
|
-
const d = a + (a ? " " : "") +
|
|
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];
|
|
360
363
|
if (this.estimateTokens(d.replace(/\s*\n\s*/g, `
|
|
361
364
|
`)) > e && a) break;
|
|
362
365
|
a = d, m++;
|
|
363
366
|
}
|
|
364
367
|
const i = a.replace(/\s*\n\s*/g, `
|
|
365
368
|
`).trim();
|
|
366
|
-
i &&
|
|
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: t = 500, overlapTokens:
|
|
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 t = (
|
|
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,34 +455,112 @@ ${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 =
|
|
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
|
+
`;
|
|
480
|
+
}
|
|
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") });
|
|
445
506
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const n = () => {
|
|
450
|
-
r = !0;
|
|
507
|
+
runDiarization(r) {
|
|
508
|
+
let e = !1, t = () => {
|
|
509
|
+
e = !0;
|
|
451
510
|
};
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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);
|
|
461
547
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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");
|
|
466
564
|
let a = this.ai.language.chunk(o, 500, 0);
|
|
467
565
|
a.length > 4 && (a = [...a.slice(0, 3), a.at(-1)]);
|
|
468
566
|
const m = await this.ai.language.json(a.join(`
|
|
@@ -470,37 +568,41 @@ class I {
|
|
|
470
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",
|
|
471
569
|
temperature: 0.1
|
|
472
570
|
});
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
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 });
|
|
576
|
+
}
|
|
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]);
|
|
479
582
|
}
|
|
480
|
-
canDiarization = () => R().then((s) => !!s);
|
|
481
583
|
}
|
|
482
|
-
class
|
|
483
|
-
constructor(
|
|
484
|
-
this.ai =
|
|
584
|
+
class V {
|
|
585
|
+
constructor(r) {
|
|
586
|
+
this.ai = r;
|
|
485
587
|
}
|
|
486
588
|
/**
|
|
487
589
|
* Convert image to text using Optical Character Recognition
|
|
488
590
|
* @param {string} path Path to image
|
|
489
591
|
* @returns {AbortablePromise<string | null>} Promise of extracted text with abort method
|
|
490
592
|
*/
|
|
491
|
-
ocr(
|
|
593
|
+
ocr(r) {
|
|
492
594
|
let e;
|
|
493
|
-
const t = new Promise(async (
|
|
494
|
-
e = await
|
|
495
|
-
const { data:
|
|
496
|
-
await e.terminate(),
|
|
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);
|
|
497
599
|
});
|
|
498
600
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
499
601
|
}
|
|
500
602
|
}
|
|
501
|
-
class
|
|
502
|
-
constructor(
|
|
503
|
-
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);
|
|
504
606
|
}
|
|
505
607
|
/** Audio processing AI */
|
|
506
608
|
audio;
|
|
@@ -509,38 +611,38 @@ class re {
|
|
|
509
611
|
/** Vision processing AI */
|
|
510
612
|
vision;
|
|
511
613
|
}
|
|
512
|
-
const
|
|
614
|
+
const Y = {
|
|
513
615
|
name: "cli",
|
|
514
616
|
description: "Use the command line interface, returns any output",
|
|
515
617
|
args: { command: { type: "string", description: "Command to run", required: !0 } },
|
|
516
|
-
fn: (h) =>
|
|
517
|
-
},
|
|
618
|
+
fn: (h) => F`${h.command}`
|
|
619
|
+
}, pe = {
|
|
518
620
|
name: "get_datetime",
|
|
519
621
|
description: "Get current UTC date / time",
|
|
520
622
|
args: {},
|
|
521
623
|
fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
|
|
522
|
-
},
|
|
624
|
+
}, he = {
|
|
523
625
|
name: "exec",
|
|
524
626
|
description: "Run code/scripts",
|
|
525
627
|
args: {
|
|
526
628
|
language: { type: "string", description: "Execution language", enum: ["cli", "node", "python"], required: !0 },
|
|
527
629
|
code: { type: "string", description: "Code to execute", required: !0 }
|
|
528
630
|
},
|
|
529
|
-
fn: async (h,
|
|
631
|
+
fn: async (h, r, e) => {
|
|
530
632
|
try {
|
|
531
633
|
switch (h.type) {
|
|
532
634
|
case "bash":
|
|
533
|
-
return await
|
|
635
|
+
return await Y.fn({ command: h.code }, r, e);
|
|
534
636
|
case "node":
|
|
535
|
-
return await
|
|
637
|
+
return await Q.fn({ code: h.code }, r, e);
|
|
536
638
|
case "python":
|
|
537
|
-
return await
|
|
639
|
+
return await X.fn({ code: h.code }, r, e);
|
|
538
640
|
}
|
|
539
641
|
} catch (t) {
|
|
540
642
|
return { error: t?.message || t.toString() };
|
|
541
643
|
}
|
|
542
644
|
}
|
|
543
|
-
},
|
|
645
|
+
}, fe = {
|
|
544
646
|
name: "fetch",
|
|
545
647
|
description: "Make HTTP request to URL",
|
|
546
648
|
args: {
|
|
@@ -549,25 +651,25 @@ const H = {
|
|
|
549
651
|
headers: { type: "object", description: "HTTP headers to send", default: {} },
|
|
550
652
|
body: { type: "object", description: "HTTP body to send" }
|
|
551
653
|
},
|
|
552
|
-
fn: (h) => new
|
|
553
|
-
},
|
|
654
|
+
fn: (h) => new O({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
|
|
655
|
+
}, Q = {
|
|
554
656
|
name: "exec_javascript",
|
|
555
657
|
description: "Execute commonjs javascript",
|
|
556
658
|
args: {
|
|
557
659
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
558
660
|
},
|
|
559
661
|
fn: async (h) => {
|
|
560
|
-
const
|
|
561
|
-
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 };
|
|
562
664
|
}
|
|
563
|
-
},
|
|
665
|
+
}, X = {
|
|
564
666
|
name: "exec_javascript",
|
|
565
667
|
description: "Execute commonjs javascript",
|
|
566
668
|
args: {
|
|
567
669
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
568
670
|
},
|
|
569
|
-
fn: async (h) => ({ result:
|
|
570
|
-
},
|
|
671
|
+
fn: async (h) => ({ result: H`python -c "${h.code}"` })
|
|
672
|
+
}, ye = {
|
|
571
673
|
name: "read_webpage",
|
|
572
674
|
description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
|
|
573
675
|
args: {
|
|
@@ -575,26 +677,26 @@ const H = {
|
|
|
575
677
|
focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
|
|
576
678
|
},
|
|
577
679
|
fn: async (h) => {
|
|
578
|
-
const
|
|
579
|
-
throw new Error(`Failed to fetch: ${
|
|
580
|
-
}), 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);
|
|
581
683
|
e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
|
|
582
684
|
const t = {
|
|
583
685
|
title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
|
|
584
686
|
description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
|
|
585
687
|
};
|
|
586
|
-
let
|
|
587
|
-
const
|
|
588
|
-
for (const
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
591
|
-
|
|
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();
|
|
592
694
|
break;
|
|
593
695
|
}
|
|
594
696
|
}
|
|
595
|
-
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 };
|
|
596
698
|
}
|
|
597
|
-
},
|
|
699
|
+
}, ge = {
|
|
598
700
|
name: "web_search",
|
|
599
701
|
description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
|
|
600
702
|
args: {
|
|
@@ -602,33 +704,32 @@ const H = {
|
|
|
602
704
|
length: { type: "string", description: "Number of results to return", default: 5 }
|
|
603
705
|
},
|
|
604
706
|
fn: async (h) => {
|
|
605
|
-
const
|
|
707
|
+
const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
|
|
606
708
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
607
|
-
}).then((
|
|
709
|
+
}).then((n) => n.text());
|
|
608
710
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
609
|
-
const
|
|
610
|
-
for (; (e = t.exec(
|
|
611
|
-
let
|
|
612
|
-
if (
|
|
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;
|
|
613
715
|
}
|
|
614
|
-
return
|
|
716
|
+
return l;
|
|
615
717
|
}
|
|
616
718
|
};
|
|
617
719
|
export {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
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
|
|
633
734
|
};
|
|
634
735
|
//# sourceMappingURL=index.mjs.map
|