@ztimson/ai-utils 0.8.11 → 0.8.13
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/index.js +27 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -192
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as O from "node:os";
|
|
2
2
|
import { tmpdir as U } from "node:os";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { OpenAI as
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { join as
|
|
3
|
+
import { Anthropic as W } from "@anthropic-ai/sdk";
|
|
4
|
+
import { objectMap as z, JSONAttemptParse as S, findByProp as R, JSONSanitize as _, clean as N, Http as C, consoleInterceptor as J, fn as I, ASet as D } from "@ztimson/utils";
|
|
5
|
+
import { OpenAI as F } from "openai";
|
|
6
|
+
import { fileURLToPath as B } from "url";
|
|
7
|
+
import { join as H, dirname as G } from "path";
|
|
8
8
|
import { spawn as w, execSync as K } from "node:child_process";
|
|
9
|
-
import { mkdtempSync as
|
|
9
|
+
import { mkdtempSync as Y } from "node:fs";
|
|
10
10
|
import b from "node:fs/promises";
|
|
11
11
|
import * as M from "node:path";
|
|
12
12
|
import q, { join as A } from "node:path";
|
|
13
|
-
import { createWorker as
|
|
13
|
+
import { createWorker as V } from "tesseract.js";
|
|
14
14
|
import * as Z from "cheerio";
|
|
15
15
|
import { $Sync as j } from "@ztimson/node-utils";
|
|
16
16
|
class L {
|
|
17
17
|
}
|
|
18
18
|
class Q extends L {
|
|
19
19
|
constructor(r, e, t) {
|
|
20
|
-
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 W({ apiKey: e });
|
|
21
21
|
}
|
|
22
22
|
client;
|
|
23
23
|
toStandard(r) {
|
|
@@ -33,8 +33,8 @@ class Q extends L {
|
|
|
33
33
|
if (n.type == "tool_use")
|
|
34
34
|
t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
|
|
35
35
|
else if (n.type == "tool_result") {
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
const m = t.findLast((l) => l.id == n.tool_use_id);
|
|
37
|
+
m && (m[n.is_error ? "error" : "content"] = n.content);
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -57,33 +57,33 @@ class Q extends L {
|
|
|
57
57
|
const t = new AbortController();
|
|
58
58
|
return Object.assign(new Promise(async (i) => {
|
|
59
59
|
let o = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
60
|
-
const n = e.tools || this.ai.options.llm?.tools || [],
|
|
60
|
+
const n = e.tools || this.ai.options.llm?.tools || [], m = {
|
|
61
61
|
model: e.model || this.model,
|
|
62
62
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
63
63
|
system: e.system || this.ai.options.llm?.system || "",
|
|
64
64
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
65
|
-
tools: n.map((
|
|
66
|
-
name:
|
|
67
|
-
description:
|
|
65
|
+
tools: n.map((u) => ({
|
|
66
|
+
name: u.name,
|
|
67
|
+
description: u.description,
|
|
68
68
|
input_schema: {
|
|
69
69
|
type: "object",
|
|
70
|
-
properties:
|
|
71
|
-
required:
|
|
70
|
+
properties: u.args ? z(u.args, (s, c) => ({ ...c, required: void 0 })) : {},
|
|
71
|
+
required: u.args ? Object.entries(u.args).filter((s) => s[1].required).map((s) => s[0]) : []
|
|
72
72
|
},
|
|
73
73
|
fn: void 0
|
|
74
74
|
})),
|
|
75
75
|
messages: o,
|
|
76
76
|
stream: !!e.stream
|
|
77
77
|
};
|
|
78
|
-
let l,
|
|
78
|
+
let l, a = !0;
|
|
79
79
|
do {
|
|
80
|
-
if (l = await this.client.messages.create(
|
|
80
|
+
if (l = await this.client.messages.create(m).catch((s) => {
|
|
81
81
|
throw s.message += `
|
|
82
82
|
|
|
83
83
|
Messages:
|
|
84
84
|
${JSON.stringify(o, null, 2)}`, s;
|
|
85
85
|
}), e.stream) {
|
|
86
|
-
|
|
86
|
+
a ? a = !1 : e.stream({ text: `
|
|
87
87
|
|
|
88
88
|
` }), l.content = [];
|
|
89
89
|
for await (const s of l) {
|
|
@@ -92,33 +92,33 @@ ${JSON.stringify(o, null, 2)}`, s;
|
|
|
92
92
|
s.content_block.type === "text" ? l.content.push({ type: "text", text: "" }) : s.content_block.type === "tool_use" && l.content.push({ type: "tool_use", id: s.content_block.id, name: s.content_block.name, input: "" });
|
|
93
93
|
else if (s.type === "content_block_delta")
|
|
94
94
|
if (s.delta.type === "text_delta") {
|
|
95
|
-
const
|
|
96
|
-
l.content.at(-1).text +=
|
|
95
|
+
const c = s.delta.text;
|
|
96
|
+
l.content.at(-1).text += c, e.stream({ text: c });
|
|
97
97
|
} else s.delta.type === "input_json_delta" && (l.content.at(-1).input += s.delta.partial_json);
|
|
98
98
|
else if (s.type === "content_block_stop") {
|
|
99
|
-
const
|
|
100
|
-
|
|
99
|
+
const c = l.content.at(-1);
|
|
100
|
+
c.input != null && (c.input = c.input ? S(c.input, {}) : {});
|
|
101
101
|
} else if (s.type === "message_stop")
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
105
|
+
const u = l.content.filter((s) => s.type === "tool_use");
|
|
106
|
+
if (u.length && !t.signal.aborted) {
|
|
107
107
|
o.push({ role: "assistant", content: l.content });
|
|
108
|
-
const s = await Promise.all(
|
|
109
|
-
const d = n.find(R("name",
|
|
110
|
-
if (e.stream && e.stream({ tool:
|
|
108
|
+
const s = await Promise.all(u.map(async (c) => {
|
|
109
|
+
const d = n.find(R("name", c.name));
|
|
110
|
+
if (e.stream && e.stream({ tool: c.name }), !d) return { tool_use_id: c.id, is_error: !0, content: "Tool not found" };
|
|
111
111
|
try {
|
|
112
|
-
const p = await d.fn(
|
|
113
|
-
return { type: "tool_result", tool_use_id:
|
|
112
|
+
const p = await d.fn(c.input, e?.stream, this.ai);
|
|
113
|
+
return { type: "tool_result", tool_use_id: c.id, content: _(p) };
|
|
114
114
|
} catch (p) {
|
|
115
|
-
return { type: "tool_result", tool_use_id:
|
|
115
|
+
return { type: "tool_result", tool_use_id: c.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
|
|
116
116
|
}
|
|
117
117
|
}));
|
|
118
|
-
o.push({ role: "user", content: s }),
|
|
118
|
+
o.push({ role: "user", content: s }), m.messages = o;
|
|
119
119
|
}
|
|
120
|
-
} while (!t.signal.aborted && l.content.some((
|
|
121
|
-
o.push({ role: "assistant", content: l.content.filter((
|
|
120
|
+
} while (!t.signal.aborted && l.content.some((u) => u.type === "tool_use"));
|
|
121
|
+
o.push({ role: "assistant", content: l.content.filter((u) => u.type == "text").map((u) => u.text).join(`
|
|
122
122
|
|
|
123
123
|
`) }), o = this.toStandard(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), i(o.at(-1)?.content);
|
|
124
124
|
}), { abort: () => t.abort() });
|
|
@@ -126,7 +126,7 @@ ${JSON.stringify(o, null, 2)}`, s;
|
|
|
126
126
|
}
|
|
127
127
|
class P extends L {
|
|
128
128
|
constructor(r, e, t, i) {
|
|
129
|
-
super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new
|
|
129
|
+
super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new F(N({
|
|
130
130
|
baseURL: e,
|
|
131
131
|
apiKey: t || e ? "ignored" : void 0
|
|
132
132
|
}));
|
|
@@ -140,7 +140,7 @@ class P extends L {
|
|
|
140
140
|
role: "tool",
|
|
141
141
|
id: o.id,
|
|
142
142
|
name: o.function.name,
|
|
143
|
-
args:
|
|
143
|
+
args: S(o.function.arguments, {}),
|
|
144
144
|
timestamp: t.timestamp
|
|
145
145
|
}));
|
|
146
146
|
r.splice(e, 1, ...i), e += i.length - 1;
|
|
@@ -178,42 +178,42 @@ class P extends L {
|
|
|
178
178
|
return Object.assign(new Promise(async (i, o) => {
|
|
179
179
|
e.system && (e.history?.[0]?.role != "system" ? e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() }) : e.history[0].content = e.system);
|
|
180
180
|
let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
|
|
181
|
-
const
|
|
181
|
+
const m = e.tools || this.ai.options.llm?.tools || [], l = {
|
|
182
182
|
model: e.model || this.model,
|
|
183
183
|
messages: n,
|
|
184
184
|
stream: !!e.stream,
|
|
185
185
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
186
186
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
187
|
-
tools:
|
|
187
|
+
tools: m.map((s) => ({
|
|
188
188
|
type: "function",
|
|
189
189
|
function: {
|
|
190
190
|
name: s.name,
|
|
191
191
|
description: s.description,
|
|
192
192
|
parameters: {
|
|
193
193
|
type: "object",
|
|
194
|
-
properties: s.args ? z(s.args, (
|
|
195
|
-
required: s.args ? Object.entries(s.args).filter((
|
|
194
|
+
properties: s.args ? z(s.args, (c, d) => ({ ...d, required: void 0 })) : {},
|
|
195
|
+
required: s.args ? Object.entries(s.args).filter((c) => c[1].required).map((c) => c[0]) : []
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}))
|
|
199
199
|
};
|
|
200
|
-
let
|
|
200
|
+
let a, u = !0;
|
|
201
201
|
do {
|
|
202
|
-
if (
|
|
203
|
-
throw
|
|
202
|
+
if (a = await this.client.chat.completions.create(l).catch((c) => {
|
|
203
|
+
throw c.message += `
|
|
204
204
|
|
|
205
205
|
Messages:
|
|
206
|
-
${JSON.stringify(n, null, 2)}`,
|
|
206
|
+
${JSON.stringify(n, null, 2)}`, c;
|
|
207
207
|
}), e.stream) {
|
|
208
|
-
|
|
208
|
+
u ? u = !1 : e.stream({ text: `
|
|
209
209
|
|
|
210
|
-
` }),
|
|
211
|
-
for await (const
|
|
210
|
+
` }), a.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
|
|
211
|
+
for await (const c of a) {
|
|
212
212
|
if (t.signal.aborted) break;
|
|
213
|
-
if (
|
|
214
|
-
for (const d of
|
|
215
|
-
const p =
|
|
216
|
-
p ? (d.id && (p.id = d.id), d.type && (p.type = d.type), d.function && (p.function || (p.function = {}), d.function.name && (p.function.name = d.function.name), d.function.arguments && (p.function.arguments = (p.function.arguments || "") + d.function.arguments))) :
|
|
213
|
+
if (c.choices[0].delta.content && (a.choices[0].message.content += c.choices[0].delta.content, e.stream({ text: c.choices[0].delta.content })), c.choices[0].delta.tool_calls)
|
|
214
|
+
for (const d of c.choices[0].delta.tool_calls) {
|
|
215
|
+
const p = a.choices[0].message.tool_calls.find((f) => f.index === d.index);
|
|
216
|
+
p ? (d.id && (p.id = d.id), d.type && (p.type = d.type), d.function && (p.function || (p.function = {}), d.function.name && (p.function.name = d.function.name), d.function.arguments && (p.function.arguments = (p.function.arguments || "") + d.function.arguments))) : a.choices[0].message.tool_calls.push({
|
|
217
217
|
index: d.index,
|
|
218
218
|
id: d.id || "",
|
|
219
219
|
type: d.type || "function",
|
|
@@ -225,23 +225,23 @@ ${JSON.stringify(n, null, 2)}`, a;
|
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
const s =
|
|
228
|
+
const s = a.choices[0].message.tool_calls || [];
|
|
229
229
|
if (s.length && !t.signal.aborted) {
|
|
230
|
-
n.push(
|
|
231
|
-
const
|
|
232
|
-
const p =
|
|
230
|
+
n.push(a.choices[0].message);
|
|
231
|
+
const c = await Promise.all(s.map(async (d) => {
|
|
232
|
+
const p = m?.find(R("name", d.function.name));
|
|
233
233
|
if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
|
|
234
234
|
try {
|
|
235
|
-
const f =
|
|
236
|
-
return { role: "tool", tool_call_id: d.id, content:
|
|
235
|
+
const f = S(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
|
|
236
|
+
return { role: "tool", tool_call_id: d.id, content: _(g) };
|
|
237
237
|
} catch (f) {
|
|
238
|
-
return { role: "tool", tool_call_id: d.id, content:
|
|
238
|
+
return { role: "tool", tool_call_id: d.id, content: _({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
239
239
|
}
|
|
240
240
|
}));
|
|
241
|
-
n.push(...
|
|
241
|
+
n.push(...c), l.messages = n;
|
|
242
242
|
}
|
|
243
|
-
} while (!t.signal.aborted &&
|
|
244
|
-
n.push({ role: "assistant", content:
|
|
243
|
+
} while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
|
|
244
|
+
n.push({ role: "assistant", content: a.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), i(n.at(-1)?.content);
|
|
245
245
|
}), { abort: () => t.abort() });
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -274,13 +274,13 @@ class X {
|
|
|
274
274
|
};
|
|
275
275
|
return Object.assign(new Promise(async (o) => {
|
|
276
276
|
if (e.history || (e.history = []), e.memory) {
|
|
277
|
-
const
|
|
278
|
-
const [
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
const m = async (a, u, s = 10) => {
|
|
278
|
+
const [c, d] = await Promise.all([
|
|
279
|
+
u ? this.embedding(u) : Promise.resolve(null),
|
|
280
|
+
a ? this.embedding(a) : Promise.resolve(null)
|
|
281
281
|
]);
|
|
282
282
|
return (e.memory || []).map((p) => {
|
|
283
|
-
const f = (
|
|
283
|
+
const f = (c ? this.cosineSimilarity(p.embeddings[0], c[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
|
|
284
284
|
return { ...p, score: f };
|
|
285
285
|
}).toSorted((p, f) => p.score - f.score).slice(0, s).map((p) => `- ${p.owner}: ${p.fact}`).join(`
|
|
286
286
|
`);
|
|
@@ -288,7 +288,7 @@ class X {
|
|
|
288
288
|
e.system += `
|
|
289
289
|
You have RAG memory and will be given the top_k closest memories regarding the users query. Save anything new you have learned worth remembering from the user message using the remember tool and feel free to recall memories manually.
|
|
290
290
|
`;
|
|
291
|
-
const l = await
|
|
291
|
+
const l = await m(r);
|
|
292
292
|
l.length && e.history.push({ role: "tool", name: "recall", id: "auto_recall_" + Math.random().toString(), args: {}, content: `Things I remembered:
|
|
293
293
|
${l}` }), e.tools = [{
|
|
294
294
|
name: "recall",
|
|
@@ -298,9 +298,9 @@ ${l}` }), e.tools = [{
|
|
|
298
298
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
299
299
|
topK: { type: "number", description: "Result limit, default 5" }
|
|
300
300
|
},
|
|
301
|
-
fn: (
|
|
302
|
-
if (!
|
|
303
|
-
return
|
|
301
|
+
fn: (a) => {
|
|
302
|
+
if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
|
|
303
|
+
return m(a.query, a.subject, a.topK);
|
|
304
304
|
}
|
|
305
305
|
}, {
|
|
306
306
|
name: "remember",
|
|
@@ -309,20 +309,20 @@ ${l}` }), e.tools = [{
|
|
|
309
309
|
owner: { type: "string", description: "Subject/person this fact is about" },
|
|
310
310
|
fact: { type: "string", description: "The information to remember" }
|
|
311
311
|
},
|
|
312
|
-
fn: async (
|
|
312
|
+
fn: async (a) => {
|
|
313
313
|
if (!e.memory) return;
|
|
314
|
-
const
|
|
315
|
-
this.embedding(
|
|
316
|
-
this.embedding(`${
|
|
317
|
-
]), s = { owner:
|
|
318
|
-
return e.memory.splice(0, e.memory.length, ...e.memory.filter((
|
|
314
|
+
const u = await Promise.all([
|
|
315
|
+
this.embedding(a.owner),
|
|
316
|
+
this.embedding(`${a.owner}: ${a.fact}`)
|
|
317
|
+
]), s = { owner: a.owner, fact: a.fact, embeddings: [u[0][0].embedding, u[1][0].embedding] };
|
|
318
|
+
return e.memory.splice(0, e.memory.length, ...e.memory.filter((c) => !(this.cosineSimilarity(s.embeddings[0], c.embeddings[0]) >= 0.9 && this.cosineSimilarity(s.embeddings[1], c.embeddings[1]) >= 0.8)), s), "Remembered!";
|
|
319
319
|
}
|
|
320
320
|
}, ...e.tools || []];
|
|
321
321
|
}
|
|
322
322
|
const n = await this.models[t].ask(r, e);
|
|
323
|
-
if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((
|
|
324
|
-
const
|
|
325
|
-
e.history.splice(0, e.history.length, ...
|
|
323
|
+
if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((m) => m.role != "tool" || m.name != "recall" && m.name != "remember")), e.compress) {
|
|
324
|
+
const m = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
|
|
325
|
+
e.history.splice(0, e.history.length, ...m);
|
|
326
326
|
}
|
|
327
327
|
return o(n);
|
|
328
328
|
}), { abort: i });
|
|
@@ -350,10 +350,10 @@ ${l}` }), e.tools = [{
|
|
|
350
350
|
if (n += this.estimateTokens(d.content), n < t) o++;
|
|
351
351
|
else break;
|
|
352
352
|
if (r.length <= o) return r;
|
|
353
|
-
const
|
|
353
|
+
const m = r[0].role == "system" ? r[0] : null, l = o == 0 ? [] : r.slice(-o), a = (o == 0 ? r : r.slice(0, -o)).filter((d) => d.role === "assistant" || d.role === "user"), u = await this.summarize(a.map((d) => `[${d.role}]: ${d.content}`).join(`
|
|
354
354
|
|
|
355
|
-
`), 500, i), s = Date.now(),
|
|
356
|
-
return
|
|
355
|
+
`), 500, i), s = Date.now(), c = [{ role: "tool", name: "summary", id: "summary_" + s, args: {}, content: `Conversation Summary: ${u?.summary}`, timestamp: s }, ...l];
|
|
356
|
+
return m && c.splice(0, 0, m), c;
|
|
357
357
|
}
|
|
358
358
|
/**
|
|
359
359
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -364,8 +364,8 @@ ${l}` }), e.tools = [{
|
|
|
364
364
|
cosineSimilarity(r, e) {
|
|
365
365
|
if (r.length !== e.length) throw new Error("Vectors must be same length");
|
|
366
366
|
let t = 0, i = 0, o = 0;
|
|
367
|
-
for (let
|
|
368
|
-
t += r[
|
|
367
|
+
for (let m = 0; m < r.length; m++)
|
|
368
|
+
t += r[m] * e[m], i += r[m] * r[m], o += e[m] * e[m];
|
|
369
369
|
const n = Math.sqrt(i) * Math.sqrt(o);
|
|
370
370
|
return n === 0 ? 0 : t / n;
|
|
371
371
|
}
|
|
@@ -377,25 +377,25 @@ ${l}` }), e.tools = [{
|
|
|
377
377
|
* @returns {string[]} Chunked strings
|
|
378
378
|
*/
|
|
379
379
|
chunk(r, e = 500, t = 50) {
|
|
380
|
-
const i = (l,
|
|
381
|
-
const
|
|
382
|
-
return typeof s == "object" && !Array.isArray(s) ? i(s,
|
|
380
|
+
const i = (l, a = "") => l ? Object.entries(l).flatMap(([u, s]) => {
|
|
381
|
+
const c = a ? `${a}${isNaN(+u) ? `.${u}` : `[${u}]`}` : u;
|
|
382
|
+
return typeof s == "object" && !Array.isArray(s) ? i(s, c) : `${c}: ${Array.isArray(s) ? s.join(", ") : s}`;
|
|
383
383
|
}) : [], n = (typeof r == "object" ? i(r) : r.toString().split(`
|
|
384
384
|
`)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
|
|
385
|
-
`]),
|
|
385
|
+
`]), m = [];
|
|
386
386
|
for (let l = 0; l < n.length; ) {
|
|
387
|
-
let
|
|
388
|
-
for (;
|
|
389
|
-
const
|
|
390
|
-
if (this.estimateTokens(
|
|
391
|
-
`)) > e &&
|
|
392
|
-
|
|
387
|
+
let a = "", u = l;
|
|
388
|
+
for (; u < n.length; ) {
|
|
389
|
+
const c = a + (a ? " " : "") + n[u];
|
|
390
|
+
if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
|
|
391
|
+
`)) > e && a) break;
|
|
392
|
+
a = c, u++;
|
|
393
393
|
}
|
|
394
|
-
const s =
|
|
394
|
+
const s = a.replace(/\s*\n\s*/g, `
|
|
395
395
|
`).trim();
|
|
396
|
-
s &&
|
|
396
|
+
s && m.push(s), l = Math.max(u - t, u === l ? l + 1 : u);
|
|
397
397
|
}
|
|
398
|
-
return
|
|
398
|
+
return m;
|
|
399
399
|
}
|
|
400
400
|
/**
|
|
401
401
|
* Create a vector representation of a string
|
|
@@ -407,21 +407,21 @@ ${l}` }), e.tools = [{
|
|
|
407
407
|
let { maxTokens: t = 500, overlapTokens: i = 50 } = e, o = !1;
|
|
408
408
|
const n = () => {
|
|
409
409
|
o = !0;
|
|
410
|
-
},
|
|
410
|
+
}, m = (a) => new Promise((u, s) => {
|
|
411
411
|
if (o) return s(new Error("Aborted"));
|
|
412
|
-
const
|
|
413
|
-
|
|
412
|
+
const c = [
|
|
413
|
+
H(G(B(import.meta.url)), "embedder.js"),
|
|
414
414
|
this.ai.options.path,
|
|
415
415
|
this.ai.options?.embedder || "bge-small-en-v1.5"
|
|
416
|
-
], d = w("node",
|
|
417
|
-
d.stdin.write(
|
|
416
|
+
], d = w("node", c, { stdio: ["pipe", "pipe", "ignore"] });
|
|
417
|
+
d.stdin.write(a), d.stdin.end();
|
|
418
418
|
let p = "";
|
|
419
419
|
d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
|
|
420
420
|
if (o) return s(new Error("Aborted"));
|
|
421
421
|
if (f === 0)
|
|
422
422
|
try {
|
|
423
423
|
const g = JSON.parse(p);
|
|
424
|
-
|
|
424
|
+
u(g.embedding);
|
|
425
425
|
} catch {
|
|
426
426
|
s(new Error("Failed to parse embedding output"));
|
|
427
427
|
}
|
|
@@ -429,12 +429,12 @@ ${l}` }), e.tools = [{
|
|
|
429
429
|
s(new Error(`Embedder process exited with code ${f}`));
|
|
430
430
|
}), d.on("error", s);
|
|
431
431
|
}), l = (async () => {
|
|
432
|
-
const
|
|
433
|
-
for (let s = 0; s <
|
|
434
|
-
const
|
|
435
|
-
|
|
432
|
+
const a = this.chunk(r, t, i), u = [];
|
|
433
|
+
for (let s = 0; s < a.length && !o; s++) {
|
|
434
|
+
const c = a[s], d = await m(c);
|
|
435
|
+
u.push({ index: s, embedding: d, text: c, tokens: this.estimateTokens(c) });
|
|
436
436
|
}
|
|
437
|
-
return
|
|
437
|
+
return u;
|
|
438
438
|
})();
|
|
439
439
|
return Object.assign(l, { abort: n });
|
|
440
440
|
}
|
|
@@ -455,8 +455,8 @@ ${l}` }), e.tools = [{
|
|
|
455
455
|
*/
|
|
456
456
|
fuzzyMatch(r, ...e) {
|
|
457
457
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
458
|
-
const t = (n,
|
|
459
|
-
return { avg: o.reduce((n,
|
|
458
|
+
const t = (n, m = 10) => n.toLowerCase().split("").map((l, a) => l.charCodeAt(0) * (a + 1) % m / m).slice(0, m), i = t(r), o = e.map((n) => t(n)).map((n) => this.cosineSimilarity(i, n));
|
|
459
|
+
return { avg: o.reduce((n, m) => n + m, 0) / o.length, max: Math.max(...o), similarities: o };
|
|
460
460
|
}
|
|
461
461
|
/**
|
|
462
462
|
* Ask a question with JSON response
|
|
@@ -466,15 +466,35 @@ ${l}` }), e.tools = [{
|
|
|
466
466
|
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
467
467
|
*/
|
|
468
468
|
async json(r, e, t) {
|
|
469
|
-
|
|
470
|
-
t?.system,
|
|
471
|
-
`Only respond using JSON matching this schema:
|
|
469
|
+
let i = `Your job is to convert input to JSON. Call the \`submit\` tool exactly once with JSON matching this schema:
|
|
472
470
|
\`\`\`json
|
|
473
471
|
${e}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
472
|
+
\`\`\``;
|
|
473
|
+
return t?.system && (i += `
|
|
474
|
+
|
|
475
|
+
` + t.system), new Promise(async (o, n) => {
|
|
476
|
+
let m = !1;
|
|
477
|
+
const l = await this.ask(r, {
|
|
478
|
+
temperature: 0.3,
|
|
479
|
+
...t,
|
|
480
|
+
system: i,
|
|
481
|
+
tools: [{
|
|
482
|
+
name: "submit",
|
|
483
|
+
description: "Submit JSON",
|
|
484
|
+
args: { json: { type: "string", description: "Javascript parsable JSON string", required: !0 } },
|
|
485
|
+
fn: (a) => {
|
|
486
|
+
try {
|
|
487
|
+
const u = JSON.parse(a.json);
|
|
488
|
+
o(u), m = !0;
|
|
489
|
+
} catch {
|
|
490
|
+
return "Invalid JSON";
|
|
491
|
+
}
|
|
492
|
+
return "Done";
|
|
493
|
+
}
|
|
494
|
+
}, ...t?.tools || []]
|
|
495
|
+
});
|
|
496
|
+
m || n(`AI failed to create summary: ${l}`);
|
|
497
|
+
});
|
|
478
498
|
}
|
|
479
499
|
/**
|
|
480
500
|
* Create a summary of some text
|
|
@@ -483,8 +503,25 @@ ${e}
|
|
|
483
503
|
* @param options LLM request options
|
|
484
504
|
* @returns {Promise<string>} Summary
|
|
485
505
|
*/
|
|
486
|
-
summarize(r, e = 500, t) {
|
|
487
|
-
|
|
506
|
+
async summarize(r, e = 500, t) {
|
|
507
|
+
let i = `Your job is to summarize the users message. Call the \`submit\` tool exactly once with the shortest summary possible that's <= ${e} tokens. Output nothing else`;
|
|
508
|
+
return t?.system && (i += `
|
|
509
|
+
|
|
510
|
+
` + t.system), new Promise(async (o, n) => {
|
|
511
|
+
let m = !1;
|
|
512
|
+
const l = await this.ask(r, {
|
|
513
|
+
temperature: 0.3,
|
|
514
|
+
...t,
|
|
515
|
+
system: i,
|
|
516
|
+
tools: [{
|
|
517
|
+
name: "submit",
|
|
518
|
+
description: "Submit summary",
|
|
519
|
+
args: { summary: { type: "string", description: "Text summarization", required: !0 } },
|
|
520
|
+
fn: (a) => (m = !0, o(a.summary || null), "Done")
|
|
521
|
+
}, ...t?.tools || []]
|
|
522
|
+
});
|
|
523
|
+
m || n(`AI failed to create summary: ${l}`);
|
|
524
|
+
});
|
|
488
525
|
}
|
|
489
526
|
}
|
|
490
527
|
class ee {
|
|
@@ -512,18 +549,18 @@ print(json.dumps(segments))
|
|
|
512
549
|
async addPunctuation(r, e, t = 150) {
|
|
513
550
|
const i = (n) => {
|
|
514
551
|
if (n = n.toLowerCase().replace(/[^a-z]/g, ""), n.length <= 3) return 1;
|
|
515
|
-
const
|
|
516
|
-
let l =
|
|
552
|
+
const m = n.match(/[aeiouy]+/g);
|
|
553
|
+
let l = m ? m.length : 1;
|
|
517
554
|
return n.endsWith("e") && l--, Math.max(1, l);
|
|
518
555
|
};
|
|
519
556
|
let o = "";
|
|
520
|
-
return r.transcription.filter((n,
|
|
557
|
+
return r.transcription.filter((n, m) => {
|
|
521
558
|
let l = !1;
|
|
522
|
-
const
|
|
523
|
-
return !n.text &&
|
|
559
|
+
const a = r.transcription[m - 1], u = r.transcription[m + 1];
|
|
560
|
+
return !n.text && u ? (u.offsets.from = n.offsets.from, u.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && a && (a.offsets.to = n.offsets.to, a.timestamps.to = n.timestamps.to, a.text += n.text, l = !0), !!n.text && !l;
|
|
524
561
|
}).forEach((n) => {
|
|
525
|
-
const
|
|
526
|
-
|
|
562
|
+
const m = /^[A-Z]/.test(n.text.trim()), l = n.offsets.to - n.offsets.from, u = i(n.text.trim()) * t;
|
|
563
|
+
m && l > u * 2 && n.text[0] == " " && (o += "."), o += n.text;
|
|
527
564
|
}), e ? this.ai.language.ask(o, {
|
|
528
565
|
system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
|
|
529
566
|
temperature: 0.1,
|
|
@@ -544,15 +581,15 @@ print(json.dumps(segments))
|
|
|
544
581
|
e.forEach((p) => {
|
|
545
582
|
i.has(p.speaker) || i.set(p.speaker, ++o);
|
|
546
583
|
});
|
|
547
|
-
const n = await this.addPunctuation(r, t),
|
|
584
|
+
const n = await this.addPunctuation(r, t), m = n.match(/[^.!?]+[.!?]+/g) || [n], l = r.transcription.filter((p) => p.text.trim()), a = m.map((p) => {
|
|
548
585
|
if (p = p.trim(), !p) return null;
|
|
549
586
|
const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
|
|
550
587
|
f.forEach((x) => {
|
|
551
588
|
const k = l.find((y) => x === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
|
|
552
589
|
if (!k) return;
|
|
553
|
-
const
|
|
554
|
-
if (
|
|
555
|
-
const y = i.get(
|
|
590
|
+
const v = k.offsets.from / 1e3, $ = e.find((y) => v >= y.start && v <= y.end);
|
|
591
|
+
if ($) {
|
|
592
|
+
const y = i.get($.speaker);
|
|
556
593
|
g.set(y, (g.get(y) || 0) + 1);
|
|
557
594
|
}
|
|
558
595
|
});
|
|
@@ -560,17 +597,17 @@ print(json.dumps(segments))
|
|
|
560
597
|
return g.forEach((x, k) => {
|
|
561
598
|
x > E && (E = x, T = k);
|
|
562
599
|
}), { speaker: T, text: p };
|
|
563
|
-
}).filter((p) => p !== null),
|
|
564
|
-
|
|
565
|
-
const f =
|
|
566
|
-
f && f.speaker === p.speaker ? f.text += " " + p.text :
|
|
600
|
+
}).filter((p) => p !== null), u = [];
|
|
601
|
+
a.forEach((p) => {
|
|
602
|
+
const f = u[u.length - 1];
|
|
603
|
+
f && f.speaker === p.speaker ? f.text += " " + p.text : u.push({ ...p });
|
|
567
604
|
});
|
|
568
|
-
let s =
|
|
605
|
+
let s = u.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
|
|
569
606
|
`).trim();
|
|
570
607
|
if (!t) return s;
|
|
571
|
-
let
|
|
572
|
-
|
|
573
|
-
const d = await this.ai.language.json(
|
|
608
|
+
let c = this.ai.language.chunk(s, 500, 0);
|
|
609
|
+
c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
|
|
610
|
+
const d = await this.ai.language.json(c.join(`
|
|
574
611
|
`), '{1: "Detected Name", 2: "Second Name"}', {
|
|
575
612
|
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",
|
|
576
613
|
temperature: 0.1
|
|
@@ -580,15 +617,15 @@ print(json.dumps(segments))
|
|
|
580
617
|
runAsr(r, e = {}) {
|
|
581
618
|
let t;
|
|
582
619
|
const i = new Promise((o, n) => {
|
|
583
|
-
this.downloadAsrModel(e.model).then((
|
|
620
|
+
this.downloadAsrModel(e.model).then((m) => {
|
|
584
621
|
if (e.diarization) {
|
|
585
622
|
let l = M.join(M.dirname(r), "transcript");
|
|
586
623
|
t = w(
|
|
587
624
|
this.ai.options.whisper,
|
|
588
|
-
["-m",
|
|
625
|
+
["-m", m, "-f", r, "-np", "-ml", "1", "-oj", "-of", l],
|
|
589
626
|
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
590
|
-
), t.on("error", (
|
|
591
|
-
if (
|
|
627
|
+
), t.on("error", (a) => n(a)), t.on("close", async (a) => {
|
|
628
|
+
if (a === 0) {
|
|
592
629
|
l = await b.readFile(l + ".json", "utf-8"), b.rm(l + ".json").catch(() => {
|
|
593
630
|
});
|
|
594
631
|
try {
|
|
@@ -597,12 +634,12 @@ print(json.dumps(segments))
|
|
|
597
634
|
n(new Error("Failed to parse whisper JSON"));
|
|
598
635
|
}
|
|
599
636
|
} else
|
|
600
|
-
n(new Error(`Exit code ${
|
|
637
|
+
n(new Error(`Exit code ${a}`));
|
|
601
638
|
});
|
|
602
639
|
} else {
|
|
603
640
|
let l = "";
|
|
604
|
-
t = w(this.ai.options.whisper, ["-m",
|
|
605
|
-
|
|
641
|
+
t = w(this.ai.options.whisper, ["-m", m, "-f", r, "-np", "-nt"]), t.on("error", (a) => n(a)), t.stdout.on("data", (a) => l += a.toString()), t.on("close", async (a) => {
|
|
642
|
+
a === 0 ? o(l.trim() || null) : n(new Error(`Exit code ${a}`));
|
|
606
643
|
});
|
|
607
644
|
}
|
|
608
645
|
});
|
|
@@ -613,53 +650,53 @@ print(json.dumps(segments))
|
|
|
613
650
|
let e = !1, t = () => {
|
|
614
651
|
e = !0;
|
|
615
652
|
};
|
|
616
|
-
const i = (n) => new Promise((
|
|
653
|
+
const i = (n) => new Promise((m) => {
|
|
617
654
|
const l = w(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
|
|
618
|
-
l.on("close", (
|
|
655
|
+
l.on("close", (a) => m(a === 0)), l.on("error", () => m(!1));
|
|
619
656
|
}), o = Promise.all([
|
|
620
657
|
i("python"),
|
|
621
658
|
i("python3")
|
|
622
|
-
]).then((async ([n,
|
|
659
|
+
]).then((async ([n, m]) => {
|
|
623
660
|
if (e) return;
|
|
624
|
-
if (!n && !
|
|
625
|
-
const l =
|
|
626
|
-
return new Promise((
|
|
661
|
+
if (!n && !m) throw new Error("Pyannote is not installed: pip install pyannote.audio");
|
|
662
|
+
const l = m ? "python3" : "python";
|
|
663
|
+
return new Promise((a, u) => {
|
|
627
664
|
if (e) return;
|
|
628
665
|
let s = "";
|
|
629
|
-
const
|
|
630
|
-
|
|
666
|
+
const c = w(l, ["-W", "ignore", "-c", this.pyannote, r]);
|
|
667
|
+
c.stdout.on("data", (d) => s += d.toString()), c.stderr.on("data", (d) => console.error(d.toString())), c.on("close", (d) => {
|
|
631
668
|
if (d === 0)
|
|
632
669
|
try {
|
|
633
|
-
|
|
670
|
+
a(JSON.parse(s));
|
|
634
671
|
} catch {
|
|
635
|
-
|
|
672
|
+
u(new Error("Failed to parse diarization output"));
|
|
636
673
|
}
|
|
637
674
|
else
|
|
638
|
-
|
|
639
|
-
}),
|
|
675
|
+
u(new Error(`Python process exited with code ${d}`));
|
|
676
|
+
}), c.on("error", u), t = () => c.kill("SIGTERM");
|
|
640
677
|
});
|
|
641
678
|
}));
|
|
642
679
|
return Object.assign(o, { abort: t });
|
|
643
680
|
}
|
|
644
681
|
asr(r, e = {}) {
|
|
645
682
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
646
|
-
const t = A(
|
|
683
|
+
const t = A(Y(A(U(), "audio-")), "converted.wav");
|
|
647
684
|
K(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
|
|
648
685
|
const i = () => b.rm(q.dirname(t), { recursive: !0, force: !0 }).catch(() => {
|
|
649
686
|
});
|
|
650
687
|
if (!e.diarization) return this.runAsr(t, { model: e.model });
|
|
651
688
|
const o = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
|
|
652
|
-
let
|
|
653
|
-
|
|
689
|
+
let m = !1, l = () => {
|
|
690
|
+
m = !0, o.abort(), n.abort(), i();
|
|
654
691
|
};
|
|
655
|
-
const
|
|
656
|
-
if (
|
|
657
|
-
` +
|
|
692
|
+
const a = Promise.allSettled([o, n]).then(async ([u, s]) => {
|
|
693
|
+
if (u.status == "rejected") throw new Error(`Whisper.cpp timestamps:
|
|
694
|
+
` + u.reason);
|
|
658
695
|
if (s.status == "rejected") throw new Error(`Pyannote:
|
|
659
696
|
` + s.reason);
|
|
660
|
-
return
|
|
697
|
+
return m || !e.diarization ? u.value : this.diarizeTranscript(u.value, s.value, e.diarization == "llm");
|
|
661
698
|
}).finally(() => i());
|
|
662
|
-
return Object.assign(
|
|
699
|
+
return Object.assign(a, { abort: l });
|
|
663
700
|
}
|
|
664
701
|
async downloadAsrModel(r = this.whisperModel) {
|
|
665
702
|
if (!this.ai.options.whisper) throw new Error("Whisper not configured");
|
|
@@ -680,7 +717,7 @@ class te {
|
|
|
680
717
|
ocr(r) {
|
|
681
718
|
let e;
|
|
682
719
|
const t = new Promise(async (i) => {
|
|
683
|
-
e = await
|
|
720
|
+
e = await V(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
684
721
|
const { data: o } = await e.recognize(r);
|
|
685
722
|
await e.terminate(), i(o.text.trim() || null);
|
|
686
723
|
});
|
|
@@ -736,7 +773,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
736
773
|
return { error: t?.message || t.toString() };
|
|
737
774
|
}
|
|
738
775
|
}
|
|
739
|
-
},
|
|
776
|
+
}, Se = {
|
|
740
777
|
name: "fetch",
|
|
741
778
|
description: "Make HTTP request to URL",
|
|
742
779
|
args: {
|
|
@@ -753,7 +790,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
753
790
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
754
791
|
},
|
|
755
792
|
fn: async (h) => {
|
|
756
|
-
const r =
|
|
793
|
+
const r = J(null), e = await I({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
|
|
757
794
|
return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
|
|
758
795
|
}
|
|
759
796
|
}, oe = {
|
|
@@ -763,7 +800,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
763
800
|
code: { type: "string", description: "CommonJS javascript", required: !0 }
|
|
764
801
|
},
|
|
765
802
|
fn: async (h) => ({ result: j`python -c "${h.code}"` })
|
|
766
|
-
},
|
|
803
|
+
}, _e = {
|
|
767
804
|
name: "read_webpage",
|
|
768
805
|
description: "Extract clean, structured content from a webpage or convert media/documents to accessible formats",
|
|
769
806
|
args: {
|
|
@@ -788,8 +825,8 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
788
825
|
const s = await e.arrayBuffer();
|
|
789
826
|
if (s.byteLength > 10485760)
|
|
790
827
|
return { url: h.url, type: "media", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
|
|
791
|
-
const
|
|
792
|
-
return { url: h.url, type: "media", mimeType: i, dataUrl: `data:${i};base64,${
|
|
828
|
+
const c = Buffer.from(s).toString("base64");
|
|
829
|
+
return { url: h.url, type: "media", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
|
|
793
830
|
}
|
|
794
831
|
if (i.match(/^(text\/(plain|csv|xml)|application\/(json|xml|csv|x-yaml))/) || h.url.match(/\.(txt|json|xml|csv|yaml|yml|md)$/i)) {
|
|
795
832
|
const s = await e.text();
|
|
@@ -799,12 +836,12 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
799
836
|
const s = await e.arrayBuffer();
|
|
800
837
|
if (s.byteLength > 10485760)
|
|
801
838
|
return { url: h.url, type: "binary", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
|
|
802
|
-
const
|
|
803
|
-
return { url: h.url, type: "binary", mimeType: i, dataUrl: `data:${i};base64,${
|
|
839
|
+
const c = Buffer.from(s).toString("base64");
|
|
840
|
+
return { url: h.url, type: "binary", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
|
|
804
841
|
}
|
|
805
842
|
const o = await e.text(), n = Z.load(o);
|
|
806
843
|
n('script, style, nav, footer, header, aside, iframe, noscript, svg, [role="navigation"], [role="banner"], [role="complementary"], .ad, .ads, .advertisement, .cookie, .popup, .modal, .sidebar, .related, .comments, .social-share').remove();
|
|
807
|
-
const
|
|
844
|
+
const m = {
|
|
808
845
|
title: n('meta[property="og:title"]').attr("content") || n("title").text() || "",
|
|
809
846
|
description: n('meta[name="description"]').attr("content") || n('meta[property="og:description"]').attr("content") || "",
|
|
810
847
|
author: n('meta[name="author"]').attr("content") || "",
|
|
@@ -812,30 +849,30 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
812
849
|
image: n('meta[property="og:image"]').attr("content") || ""
|
|
813
850
|
};
|
|
814
851
|
let l = "";
|
|
815
|
-
const
|
|
816
|
-
for (const s of
|
|
817
|
-
const
|
|
818
|
-
if (
|
|
819
|
-
l =
|
|
852
|
+
const a = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content", "body"];
|
|
853
|
+
for (const s of a) {
|
|
854
|
+
const c = n(s).first();
|
|
855
|
+
if (c.length && c.text().trim().length > 200) {
|
|
856
|
+
l = c.text();
|
|
820
857
|
break;
|
|
821
858
|
}
|
|
822
859
|
}
|
|
823
860
|
l || (l = n("body").text()), l = l.replace(/\n\s*\n\s*\n/g, `
|
|
824
861
|
|
|
825
862
|
`).replace(/[ \t]+/g, " ").trim().slice(0, 5e4);
|
|
826
|
-
let
|
|
827
|
-
return l.length < 500 && (n("a[href]").each((s,
|
|
828
|
-
const d = n(
|
|
829
|
-
d && p && !d.startsWith("#") &&
|
|
830
|
-
}),
|
|
863
|
+
let u = [];
|
|
864
|
+
return l.length < 500 && (n("a[href]").each((s, c) => {
|
|
865
|
+
const d = n(c).attr("href"), p = n(c).text().trim();
|
|
866
|
+
d && p && !d.startsWith("#") && u.push({ text: p, href: d });
|
|
867
|
+
}), u = u.slice(0, 50)), {
|
|
831
868
|
url: h.url,
|
|
832
869
|
type: "html",
|
|
833
|
-
title:
|
|
834
|
-
description:
|
|
835
|
-
author:
|
|
836
|
-
published:
|
|
870
|
+
title: m.title.trim(),
|
|
871
|
+
description: m.description.trim(),
|
|
872
|
+
author: m.author.trim(),
|
|
873
|
+
published: m.published,
|
|
837
874
|
content: l,
|
|
838
|
-
links:
|
|
875
|
+
links: u.length ? u : void 0
|
|
839
876
|
};
|
|
840
877
|
}
|
|
841
878
|
}, je = {
|
|
@@ -850,7 +887,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
|
|
|
850
887
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
851
888
|
}).then((o) => o.text());
|
|
852
889
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
853
|
-
const i = new
|
|
890
|
+
const i = new D();
|
|
854
891
|
for (; (e = t.exec(r)) !== null; ) {
|
|
855
892
|
let o = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
856
893
|
if (o && (o = decodeURIComponent(o)), o && i.add(o), i.size >= (h.length || 5)) break;
|
|
@@ -866,12 +903,12 @@ export {
|
|
|
866
903
|
we as DateTimeTool,
|
|
867
904
|
xe as DateTimeUTCTool,
|
|
868
905
|
ke as ExecTool,
|
|
869
|
-
|
|
906
|
+
Se as FetchTool,
|
|
870
907
|
se as JSTool,
|
|
871
908
|
L as LLMProvider,
|
|
872
909
|
P as OpenAi,
|
|
873
910
|
oe as PythonTool,
|
|
874
|
-
|
|
911
|
+
_e as ReadWebpageTool,
|
|
875
912
|
te as Vision,
|
|
876
913
|
je as WebSearchTool
|
|
877
914
|
};
|