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