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