@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/index.mjs CHANGED
@@ -1,236 +1,243 @@
1
- import * as S from "node:os";
2
- import { objectMap as k, JSONAttemptParse as y, findByProp as x, JSONSanitize as b, clean as T, Http as M, consoleInterceptor as q, fn as P, ASet as E } from "@ztimson/utils";
3
- import { Anthropic as $ } from "@anthropic-ai/sdk";
4
- import { OpenAI as A } from "openai";
5
- import { Worker as v } from "worker_threads";
6
- import { fileURLToPath as O } from "url";
7
- import { join as R, dirname as U } from "path";
8
- import { spawn as W } from "node:child_process";
9
- import w from "node:fs/promises";
10
- import L from "node:path";
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 N from "cheerio";
14
- import { $ as C, $Sync as D } from "@ztimson/node-utils";
15
- class j {
12
+ import * as z from "cheerio";
13
+ import { $ as C, $Sync as W } from "@ztimson/node-utils";
14
+ class S {
16
15
  }
17
- class J extends j {
18
- constructor(t, e, s) {
19
- super(), this.ai = t, this.apiToken = e, this.model = s, this.client = new $({ apiKey: e });
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(t) {
23
- for (let e = 0; e < t.length; e++) {
24
- const s = e;
25
- typeof t[s].content != "string" && (t[s].role == "assistant" ? t[s].content.filter((r) => r.type == "tool_use").forEach((r) => {
26
- t.splice(e + 1, 0, { role: "tool", id: r.id, name: r.name, args: r.input, timestamp: Date.now() });
27
- }) : t[s].role == "user" && t[s].content.filter((r) => r.type == "tool_result").forEach((r) => {
28
- const c = t.find((o) => o.id == r.tool_use_id);
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
- `), t[s].content || t.splice(s, 1)), t[s].timestamp || (t[s].timestamp = Date.now());
33
- }
34
- return t.filter((e) => !!e.content);
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(t) {
37
- for (let e = 0; e < t.length; e++)
38
- if (t[e].role == "tool") {
39
- const s = t[e];
40
- t.splice(
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: s.id, name: s.name, input: s.args }] },
44
- { role: "user", content: [{ type: "tool_result", tool_use_id: s.id, is_error: !!s.error, content: s.error || s.content }] }
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 t.map(({ timestamp: e, ...s }) => s);
52
+ return r.map(({ timestamp: e, ...t }) => t);
48
53
  }
49
- ask(t, e = {}) {
50
- const s = new AbortController();
51
- return Object.assign(new Promise(async (r, c) => {
52
- const o = this.fromStandard([...e.history || [], { role: "user", content: t, timestamp: Date.now() }]), a = e.tools || this.ai.options.llm?.tools || [], m = {
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: a.map((l) => ({
58
- name: l.name,
59
- description: l.description,
63
+ tools: s.map((d) => ({
64
+ name: d.name,
65
+ description: d.description,
60
66
  input_schema: {
61
67
  type: "object",
62
- properties: l.args ? k(l.args, (i, u) => ({ ...u, required: void 0 })) : {},
63
- required: l.args ? Object.entries(l.args).filter((i) => i[1].required).map((i) => i[0]) : []
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: o,
73
+ messages: n,
68
74
  stream: !!e.stream
69
75
  };
70
- let n, d = !0;
76
+ let a, l = !0;
71
77
  do {
72
- if (n = await this.client.messages.create(m).catch((i) => {
73
- throw i.message += `
78
+ if (a = await this.client.messages.create(o).catch((c) => {
79
+ throw c.message += `
74
80
 
75
81
  Messages:
76
- ${JSON.stringify(o, null, 2)}`, i;
82
+ ${JSON.stringify(n, null, 2)}`, c;
77
83
  }), e.stream) {
78
- d ? d = !1 : e.stream({ text: `
84
+ l ? l = !1 : e.stream({ text: `
79
85
 
80
- ` }), n.content = [];
81
- for await (const i of n) {
82
- if (s.signal.aborted) break;
83
- if (i.type === "content_block_start")
84
- 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: "" });
85
- else if (i.type === "content_block_delta")
86
- if (i.delta.type === "text_delta") {
87
- const u = i.delta.text;
88
- n.content.at(-1).text += u, e.stream({ text: u });
89
- } else i.delta.type === "input_json_delta" && (n.content.at(-1).input += i.delta.partial_json);
90
- else if (i.type === "content_block_stop") {
91
- const u = n.content.at(-1);
92
- u.input != null && (u.input = u.input ? y(u.input, {}) : {});
93
- } else if (i.type === "message_stop")
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 l = n.content.filter((i) => i.type === "tool_use");
98
- if (l.length && !s.signal.aborted) {
99
- o.push({ role: "assistant", content: n.content });
100
- const i = await Promise.all(l.map(async (u) => {
101
- const h = a.find(x("name", u.name));
102
- if (e.stream && e.stream({ tool: u.name }), !h) return { tool_use_id: u.id, is_error: !0, content: "Tool not found" };
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 f = await h.fn(u.input, e?.stream, this.ai);
105
- return { type: "tool_result", tool_use_id: u.id, content: b(f) };
106
- } catch (f) {
107
- return { type: "tool_result", tool_use_id: u.id, is_error: !0, content: f?.message || f?.toString() || "Unknown" };
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
- o.push({ role: "user", content: i }), m.messages = o;
116
+ n.push({ role: "user", content: c }), o.messages = n;
111
117
  }
112
- } while (!s.signal.aborted && n.content.some((l) => l.type === "tool_use"));
113
- o.push({ role: "assistant", content: n.content.filter((l) => l.type == "text").map((l) => l.text).join(`
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(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), r(o.at(-1)?.content);
116
- }), { abort: () => s.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 _ extends j {
120
- constructor(t, e, s, r) {
121
- super(), this.ai = t, this.host = e, this.token = s, this.model = r, this.client = new A(T({
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: s
129
+ apiKey: t
124
130
  }));
125
131
  }
126
132
  client;
127
- toStandard(t) {
128
- for (let e = 0; e < t.length; e++) {
129
- const s = t[e];
130
- if (s.role === "assistant" && s.tool_calls) {
131
- const r = s.tool_calls.map((c) => ({
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: c.id,
134
- name: c.function.name,
135
- args: y(c.function.arguments, {}),
136
- timestamp: s.timestamp
139
+ id: n.id,
140
+ name: n.function.name,
141
+ args: g(n.function.arguments, {}),
142
+ timestamp: t.timestamp
137
143
  }));
138
- t.splice(e, 1, ...r), e += r.length - 1;
139
- } else if (s.role === "tool" && s.content) {
140
- const r = t.find((c) => s.tool_call_id == c.id);
141
- r && (s.content.includes('"error":') ? r.error = s.content : r.content = s.content), t.splice(e, 1), e--;
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
- t[e]?.timestamp || (t[e].timestamp = Date.now());
149
+ r[e]?.timestamp || (r[e].timestamp = Date.now());
144
150
  }
145
- return t;
151
+ return r;
146
152
  }
147
- fromStandard(t) {
148
- return t.reduce((e, s) => {
149
- if (s.role === "tool")
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: s.id, type: "function", function: { name: s.name, arguments: JSON.stringify(s.args) } }],
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: s.id,
159
- content: s.error || s.content
164
+ tool_call_id: t.id,
165
+ content: t.error || t.content
160
166
  });
161
167
  else {
162
- const { timestamp: r, ...c } = s;
163
- e.push(c);
168
+ const { timestamp: i, ...n } = t;
169
+ e.push(n);
164
170
  }
165
171
  return e;
166
172
  }, []);
167
173
  }
168
- ask(t, e = {}) {
169
- const s = new AbortController();
170
- return Object.assign(new Promise(async (r, c) => {
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
- const o = this.fromStandard([...e.history || [], { role: "user", content: t, timestamp: Date.now() }]), a = e.tools || this.ai.options.llm?.tools || [], m = {
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: o,
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: a.map((l) => ({
185
+ tools: o.map((c) => ({
179
186
  type: "function",
180
187
  function: {
181
- name: l.name,
182
- description: l.description,
188
+ name: c.name,
189
+ description: c.description,
183
190
  parameters: {
184
191
  type: "object",
185
- properties: l.args ? k(l.args, (i, u) => ({ ...u, required: void 0 })) : {},
186
- required: l.args ? Object.entries(l.args).filter((i) => i[1].required).map((i) => i[0]) : []
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 n, d = !0;
198
+ let l, d = !0;
192
199
  do {
193
- if (n = await this.client.chat.completions.create(m).catch((i) => {
194
- throw i.message += `
200
+ if (l = await this.client.chat.completions.create(a).catch((m) => {
201
+ throw m.message += `
195
202
 
196
203
  Messages:
197
- ${JSON.stringify(o, null, 2)}`, i;
204
+ ${JSON.stringify(s, null, 2)}`, m;
198
205
  }), e.stream) {
199
206
  d ? d = !1 : e.stream({ text: `
200
207
 
201
- ` }), n.choices = [{ message: { content: "", tool_calls: [] } }];
202
- for await (const i of n) {
203
- if (s.signal.aborted) break;
204
- i.choices[0].delta.content && (n.choices[0].message.content += i.choices[0].delta.content, e.stream({ text: i.choices[0].delta.content })), i.choices[0].delta.tool_calls && (n.choices[0].message.tool_calls = i.choices[0].delta.tool_calls);
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 l = n.choices[0].message.tool_calls || [];
208
- if (l.length && !s.signal.aborted) {
209
- o.push(n.choices[0].message);
210
- const i = await Promise.all(l.map(async (u) => {
211
- const h = a?.find(x("name", u.function.name));
212
- if (e.stream && e.stream({ tool: u.function.name }), !h) return { role: "tool", tool_call_id: u.id, content: '{"error": "Tool not found"}' };
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 = y(u.function.arguments, {}), g = await h.fn(f, e.stream, this.ai);
215
- return { role: "tool", tool_call_id: u.id, content: b(g) };
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: u.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
224
+ return { role: "tool", tool_call_id: h.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
218
225
  }
219
226
  }));
220
- o.push(...i), m.messages = o;
227
+ s.push(...m), a.messages = s;
221
228
  }
222
- } while (!s.signal.aborted && n.choices?.[0]?.message?.tool_calls?.length);
223
- o.push({ role: "assistant", content: n.choices[0].message.content || "" }), this.toStandard(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), r(o.at(-1)?.content);
224
- }), { abort: () => s.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 H {
228
- constructor(t) {
229
- this.ai = t, this.embedWorker = new v(R(U(O(import.meta.url)), "embedder.js")), this.embedWorker.on("message", ({ id: e, embedding: s }) => {
230
- const r = this.embedQueue.get(e);
231
- r && (r.resolve(s), this.embedQueue.delete(e));
232
- }), t.options.llm?.models && Object.entries(t.options.llm.models).forEach(([e, s]) => {
233
- this.defaultModel || (this.defaultModel = e), s.proto == "anthropic" ? this.models[e] = new J(this.ai, s.token, e) : s.proto == "ollama" ? this.models[e] = new _(this.ai, s.host, "not-needed", e) : s.proto == "openai" && (this.models[e] = new _(this.ai, s.host || null, s.token, 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(t, e = {}) {
248
- const s = e.model || this.defaultModel;
249
- if (!this.models[s]) throw new Error(`Model does not exist: ${s}`);
250
- let r = () => {
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 (c) => {
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 a = async (n, d, l = 50) => {
258
- const [i, u] = await Promise.all([
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
- n ? this.embedding(n) : Promise.resolve(null)
267
+ l ? this.embedding(l) : Promise.resolve(null)
261
268
  ]);
262
- return (e.memory || []).map((h) => ({ ...h, score: i ? this.cosineSimilarity(h.embeddings[0], i[0].embedding) : 1 })).filter((h) => h.score >= 0.8).map((h) => ({ ...h, score: u ? this.cosineSimilarity(h.embeddings[1], u[0].embedding) : h.score })).filter((h) => h.score >= 0.2).toSorted((h, f) => h.score - f.score).slice(0, l);
263
- }, m = await a(t);
264
- m.length && e.history.push({ role: "assistant", content: `Things I remembered:
265
- ` + m.map((n) => `${n.owner}: ${n.fact}`).join(`
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: (n) => {
275
- if (!n.subject && !n.query) throw new Error("Either a subject or query argument is required");
276
- return a(n.query, n.subject, n.limit || 5);
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 o = await this.models[s].ask(t, e);
287
+ const s = await this.models[t].ask(r, e);
281
288
  if (e.memory) {
282
- const a = e.history?.findIndex((m) => m.role == "assistant" && m.content.startsWith("Things I remembered:"));
283
- a != null && a >= 0 && e.history?.splice(a, 1);
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 a = null;
293
+ let o = null;
287
294
  if (e.compress)
288
- a = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...a.history);
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 m = e.history?.findLastIndex((n) => n.role == "user") ?? -1;
291
- a = await this.ai.language.compressHistory(m != -1 ? e.history.slice(m) : e.history, 0, 0, e);
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 m = e.memory.filter((n) => !a.memory.some((d) => this.cosineSimilarity(n.embeddings[1], d.embeddings[1]) > 0.8)).concat(a.memory);
295
- e.memory.splice(0, e.memory.length, ...m);
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 c(o);
299
- }), { abort: r });
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(t, e, s, r) {
310
- if (this.estimateTokens(t) < e) return { history: t, memory: [] };
311
- let c = 0, o = 0;
312
- for (let h of t.toReversed())
313
- if (o += this.estimateTokens(h.content), o < s) c++;
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 (t.length <= c) return { history: t, memory: [] };
316
- const a = t[0].role == "system" ? t[0] : null, m = c == 0 ? [] : t.slice(-c), n = (c == 0 ? t : t.slice(0, -c)).filter((h) => h.role === "assistant" || h.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]]}
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
- ${n.map((h) => `${h.role}: ${h.content}`).join(`
325
+ ${l.map((u) => `${u.role}: ${u.content}`).join(`
319
326
 
320
- `)}`, { model: r?.model, temperature: r?.temperature || 0.3 }), l = /* @__PURE__ */ new Date(), i = await Promise.all((d?.facts || [])?.map(async ([h, f]) => {
321
- const g = await Promise.all([this.embedding(h), this.embedding(`${h}: ${f}`)]);
322
- return { owner: h, fact: f, embeddings: [g[0][0].embedding, g[1][0].embedding], timestamp: l };
323
- })), u = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...m];
324
- return a && u.splice(0, 0, a), { history: u, memory: i };
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(t, e) {
333
- if (t.length !== e.length) throw new Error("Vectors must be same length");
334
- let s = 0, r = 0, c = 0;
335
- for (let a = 0; a < t.length; a++)
336
- s += t[a] * e[a], r += t[a] * t[a], c += e[a] * e[a];
337
- const o = Math.sqrt(r) * Math.sqrt(c);
338
- return o === 0 ? 0 : s / o;
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(t, e = 500, s = 50) {
348
- const r = (m, n = "") => m ? Object.entries(m).flatMap(([d, l]) => {
349
- const i = n ? `${n}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
350
- return typeof l == "object" && !Array.isArray(l) ? r(l, i) : `${i}: ${Array.isArray(l) ? l.join(", ") : l}`;
351
- }) : [], o = (typeof t == "object" ? r(t) : t.split(`
352
- `)).flatMap((m) => [...m.split(/\s+/).filter(Boolean), `
353
- `]), a = [];
354
- for (let m = 0; m < o.length; ) {
355
- let n = "", d = m;
356
- for (; d < o.length; ) {
357
- const i = n + (n ? " " : "") + o[d];
358
- if (this.estimateTokens(i.replace(/\s*\n\s*/g, `
359
- `)) > e && n) break;
360
- n = i, d++;
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 l = n.replace(/\s*\n\s*/g, `
369
+ const c = l.replace(/\s*\n\s*/g, `
363
370
  `).trim();
364
- l && a.push(l), m = Math.max(d - s, d === m ? m + 1 : d);
371
+ c && o.push(c), a = Math.max(d - t, d === a ? a + 1 : d);
365
372
  }
366
- return a;
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(t, e = 500, s = 50) {
376
- const r = (o) => new Promise((a, m) => {
377
- const n = this.embedId++;
378
- this.embedQueue.set(n, { resolve: a, reject: m }), this.embedWorker?.postMessage({
379
- id: n,
380
- text: o,
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
- }), c = this.chunk(t, e, s);
385
- return Promise.all(c.map(async (o, a) => ({
386
- index: a,
387
- embedding: await r(o),
388
- text: o,
389
- tokens: this.estimateTokens(o)
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(t) {
398
- const e = JSON.stringify(t);
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(t, ...e) {
414
+ fuzzyMatch(r, ...e) {
408
415
  if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
409
- const s = (o, a = 10) => o.toLowerCase().split("").map((m, n) => m.charCodeAt(0) * (n + 1) % a / a).slice(0, a), r = s(t), c = e.map((o) => s(o)).map((o) => this.cosineSimilarity(r, o));
410
- return { avg: c.reduce((o, a) => o + a, 0) / c.length, max: Math.max(...c), similarities: c };
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(t, e) {
419
- let s = await this.ask(t, { system: "Respond using a JSON blob matching any provided examples", ...e });
420
- if (!s) return {};
421
- const r = /```(?:.+)?\s*([\s\S]*?)```/.exec(s), c = r ? r[1].trim() : s;
422
- return y(c, {});
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(t, e, s) {
432
- return this.ask(t, { system: `Generate a brief summary <= ${e} tokens. Output nothing else`, temperature: 0.3, ...s });
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 z {
436
- constructor(t) {
437
- this.ai = t, t.options.whisper?.binary && (this.whisperModel = t.options.whisper?.model.endsWith(".bin") ? t.options.whisper?.model : t.options.whisper?.model + ".bin", this.downloadAsrModel());
442
+ class H {
443
+ constructor(r) {
444
+ this.ai = r;
438
445
  }
439
- downloads = {};
440
- whisperModel;
441
- asr(t, e = this.whisperModel) {
442
- if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
443
- let s = () => {
444
- };
445
- const r = new Promise(async (c, o) => {
446
- const a = await this.downloadAsrModel(e);
447
- let m = "";
448
- const n = W(this.ai.options.whisper?.binary, ["-nt", "-np", "-m", a, "-f", t], { stdio: ["ignore", "pipe", "ignore"] });
449
- s = () => n.kill("SIGTERM"), n.on("error", (d) => o(d)), n.stdout.on("data", (d) => m += d.toString()), n.on("close", (d) => {
450
- d === 0 ? c(m.trim() || null) : o(new Error(`Exit code ${d}`));
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 downloadAsrModel(t = this.whisperModel) {
456
- if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
457
- t.endsWith(".bin") || (t += ".bin");
458
- const e = L.join(this.ai.options.path, t);
459
- return await w.stat(e).then(() => !0).catch(() => !1) ? e : this.downloads[t] ? this.downloads[t] : (this.downloads[t] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${t}`).then((s) => s.arrayBuffer()).then((s) => Buffer.from(s)).then(async (s) => (await w.writeFile(e, s), delete this.downloads[t], e)), this.downloads[t]);
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 F {
463
- constructor(t) {
464
- this.ai = t;
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(t) {
534
+ ocr(r) {
472
535
  let e;
473
- const s = new Promise(async (r) => {
474
- e = await I(this.ai.options.tesseract?.model || "eng", 2, { cachePath: this.ai.options.path });
475
- const { data: c } = await e.recognize(t);
476
- await e.terminate(), r(c.text.trim() || null);
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(s, { abort: () => e?.terminate() });
541
+ return Object.assign(t, { abort: () => e?.terminate() });
479
542
  }
480
543
  }
481
- class ae {
482
- constructor(t) {
483
- this.options = t, t.path || (t.path = S.tmpdir()), process.env.TRANSFORMERS_CACHE = t.path, this.audio = new z(this), this.language = new H(this), this.vision = new F(this);
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 B = {
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
- }, ce = {
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
- }, le = {
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, t, e) => {
572
+ fn: async (p, r, e) => {
510
573
  try {
511
574
  switch (p.type) {
512
575
  case "bash":
513
- return await B.fn({ command: p.code }, t, e);
576
+ return await F.fn({ command: p.code }, r, e);
514
577
  case "node":
515
- return await G.fn({ code: p.code }, t, e);
578
+ return await G.fn({ code: p.code }, r, e);
516
579
  case "python":
517
- return await Q.fn({ code: p.code }, t, e);
580
+ return await B.fn({ code: p.code }, r, e);
518
581
  }
519
- } catch (s) {
520
- return { error: s?.message || s.toString() };
582
+ } catch (t) {
583
+ return { error: t?.message || t.toString() };
521
584
  }
522
585
  }
523
- }, me = {
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 M({ url: p.url, headers: p.headers }).request({ method: p.method || "GET", body: p.body })
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 t = q(null), e = await P({ console: t }, p.code, !0).catch((s) => t.output.error.push(s));
541
- return { ...t.output, return: e, stdout: void 0, stderr: void 0 };
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
- }, Q = {
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: D`python -c "${p.code}"` })
550
- }, de = {
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 t = await fetch(p.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((o) => o.text()).catch((o) => {
559
- throw new Error(`Failed to fetch: ${o.message}`);
560
- }), e = N.load(t);
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 s = {
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 r = "";
567
- const c = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
568
- for (const o of c) {
569
- const a = e(o).first();
570
- if (a.length && a.text().trim().length > 200) {
571
- r = a.text();
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 r || (r = e("body").text()), r = r.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: p.url, title: s.title.trim(), description: s.description.trim(), content: r, focus: p.focus };
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
- }, ue = {
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 t = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(p.query)}`, {
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((c) => c.text());
588
- let e, s = /<a .*?href="(.+?)".+?<\/a>/g;
589
- const r = new E();
590
- for (; (e = s.exec(t)) !== null; ) {
591
- let c = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
592
- if (c && (c = decodeURIComponent(c)), c && r.add(c), r.size >= (p.length || 5)) break;
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=(.+)&amp?/.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 r;
657
+ return i;
595
658
  }
596
659
  };
597
660
  export {
598
- ae as Ai,
599
- J as Anthropic,
600
- z as Audio,
601
- B as CliTool,
602
- ce as DateTimeTool,
603
- le as ExecTool,
604
- me as FetchTool,
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
- j as LLMProvider,
607
- _ as OpenAi,
608
- Q as PythonTool,
609
- de as ReadWebpageTool,
610
- F as Vision,
611
- ue as WebSearchTool
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