@ztimson/ai-utils 0.8.15 → 0.9.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,40 +1,40 @@
1
- import * as O from "node:os";
2
- import { tmpdir as N } from "node:os";
3
- import { Anthropic as U } from "@anthropic-ai/sdk";
4
- import { objectMap as z, JSONAttemptParse as S, findByProp as R, JSONSanitize as _, clean as W, Http as C, consoleInterceptor as J, fn as I, ASet as F } from "@ztimson/utils";
5
- import { OpenAI as B } from "openai";
6
- import { fileURLToPath as D } from "url";
7
- import { join as H, dirname as G } from "path";
8
- import { spawn as w, execSync as K } from "node:child_process";
9
- import { mkdtempSync as Y } from "node:fs";
10
- import b from "node:fs/promises";
11
- import * as M from "node:path";
12
- import q, { join as A } from "node:path";
13
- import { createWorker as V } from "tesseract.js";
14
- import * as Z from "cheerio";
1
+ import * as R from "node:os";
2
+ import { tmpdir as W } from "node:os";
3
+ import { Anthropic as C } from "@anthropic-ai/sdk";
4
+ import { objectMap as O, JSONAttemptParse as $, findByProp as U, JSONSanitize as T, clean as J, Http as F, consoleInterceptor as B, fn as I, decodeHtml as D, ASet as H } from "@ztimson/utils";
5
+ import { OpenAI as G } from "openai";
6
+ import { fileURLToPath as K } from "url";
7
+ import { join as Y, dirname as V } from "path";
8
+ import { spawn as k, execSync as Z } from "node:child_process";
9
+ import { mkdtempSync as Q } from "node:fs";
10
+ import x from "node:fs/promises";
11
+ import * as q from "node:path";
12
+ import M, { join as A } from "node:path";
13
+ import { createWorker as X } from "tesseract.js";
14
+ import * as ee from "cheerio";
15
15
  import { $Sync as j } from "@ztimson/node-utils";
16
16
  class L {
17
17
  }
18
- class Q extends L {
18
+ class te extends L {
19
19
  constructor(r, e, t) {
20
- super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new U({ apiKey: e });
20
+ super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new C({ apiKey: e });
21
21
  }
22
22
  client;
23
23
  toStandard(r) {
24
24
  const e = Date.now(), t = [];
25
- for (let a of r)
26
- if (typeof a.content == "string")
27
- t.push({ timestamp: e, ...a });
25
+ for (let l of r)
26
+ if (typeof l.content == "string")
27
+ t.push({ timestamp: e, ...l });
28
28
  else {
29
- const o = a.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
29
+ const n = l.content?.filter((s) => s.type == "text").map((s) => s.text).join(`
30
30
 
31
31
  `);
32
- o && t.push({ timestamp: e, role: a.role, content: o }), a.content.forEach((n) => {
33
- if (n.type == "tool_use")
34
- t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
35
- else if (n.type == "tool_result") {
36
- const m = t.findLast((l) => l.id == n.tool_use_id);
37
- m && (m[n.is_error ? "error" : "content"] = n.content);
32
+ n && t.push({ timestamp: e, role: l.role, content: n }), l.content.forEach((s) => {
33
+ if (s.type == "tool_use")
34
+ t.push({ timestamp: e, role: "tool", id: s.id, name: s.name, args: s.input, content: void 0 });
35
+ else if (s.type == "tool_result") {
36
+ const o = t.findLast((u) => u.id == s.tool_use_id);
37
+ o && (o[s.is_error ? "error" : "content"] = s.content);
38
38
  }
39
39
  });
40
40
  }
@@ -55,78 +55,78 @@ class Q extends L {
55
55
  }
56
56
  ask(r, e = {}) {
57
57
  const t = new AbortController();
58
- return Object.assign(new Promise(async (a) => {
59
- let o = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
- const n = e.tools || this.ai.options.llm?.tools || [], m = {
58
+ return Object.assign(new Promise(async (l) => {
59
+ let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
+ const s = e.tools || this.ai.options.llm?.tools || [], o = {
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((u) => ({
66
- name: u.name,
67
- description: u.description,
65
+ tools: s.map((m) => ({
66
+ name: m.name,
67
+ description: m.description,
68
68
  input_schema: {
69
69
  type: "object",
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]) : []
70
+ properties: m.args ? O(m.args, (i, p) => ({ ...p, required: void 0 })) : {},
71
+ required: m.args ? Object.entries(m.args).filter((i) => i[1].required).map((i) => i[0]) : []
72
72
  },
73
73
  fn: void 0
74
74
  })),
75
- messages: o,
75
+ messages: n,
76
76
  stream: !!e.stream
77
77
  };
78
- let l, i = !0;
78
+ let u, a = !0;
79
79
  do {
80
- if (l = await this.client.messages.create(m).catch((s) => {
81
- throw s.message += `
80
+ if (u = await this.client.messages.create(o).catch((i) => {
81
+ throw i.message += `
82
82
 
83
83
  Messages:
84
- ${JSON.stringify(o, null, 2)}`, s;
84
+ ${JSON.stringify(n, null, 2)}`, i;
85
85
  }), e.stream) {
86
- i ? i = !1 : e.stream({ text: `
86
+ a ? a = !1 : e.stream({ text: `
87
87
 
88
- ` }), l.content = [];
89
- for await (const s of l) {
88
+ ` }), u.content = [];
89
+ for await (const i of u) {
90
90
  if (t.signal.aborted) break;
91
- if (s.type === "content_block_start")
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
- else if (s.type === "content_block_delta")
94
- if (s.delta.type === "text_delta") {
95
- const c = s.delta.text;
96
- l.content.at(-1).text += c, e.stream({ text: c });
97
- } else s.delta.type === "input_json_delta" && (l.content.at(-1).input += s.delta.partial_json);
98
- else if (s.type === "content_block_stop") {
99
- const c = l.content.at(-1);
100
- c.input != null && (c.input = c.input ? S(c.input, {}) : {});
101
- } else if (s.type === "message_stop")
91
+ if (i.type === "content_block_start")
92
+ i.content_block.type === "text" ? u.content.push({ type: "text", text: "" }) : i.content_block.type === "tool_use" && u.content.push({ type: "tool_use", id: i.content_block.id, name: i.content_block.name, input: "" });
93
+ else if (i.type === "content_block_delta")
94
+ if (i.delta.type === "text_delta") {
95
+ const p = i.delta.text;
96
+ u.content.at(-1).text += p, e.stream({ text: p });
97
+ } else i.delta.type === "input_json_delta" && (u.content.at(-1).input += i.delta.partial_json);
98
+ else if (i.type === "content_block_stop") {
99
+ const p = u.content.at(-1);
100
+ p.input != null && (p.input = p.input ? $(p.input, {}) : {});
101
+ } else if (i.type === "message_stop")
102
102
  break;
103
103
  }
104
104
  }
105
- const u = l.content.filter((s) => s.type === "tool_use");
106
- if (u.length && !t.signal.aborted) {
107
- o.push({ role: "assistant", content: l.content });
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" };
105
+ const m = u.content.filter((i) => i.type === "tool_use");
106
+ if (m.length && !t.signal.aborted) {
107
+ n.push({ role: "assistant", content: u.content });
108
+ const i = await Promise.all(m.map(async (p) => {
109
+ const d = s.find(U("name", p.name));
110
+ if (e.stream && e.stream({ tool: p.name }), !d) return { tool_use_id: p.id, is_error: !0, content: "Tool not found" };
111
111
  try {
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
- } catch (p) {
115
- return { type: "tool_result", tool_use_id: c.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
112
+ const c = await d.fn(p.input, e?.stream, this.ai);
113
+ return { type: "tool_result", tool_use_id: p.id, content: typeof c == "object" ? T(c) : c };
114
+ } catch (c) {
115
+ return { type: "tool_result", tool_use_id: p.id, is_error: !0, content: c?.message || c?.toString() || "Unknown" };
116
116
  }
117
117
  }));
118
- o.push({ role: "user", content: s }), m.messages = o;
118
+ n.push({ role: "user", content: i }), o.messages = n;
119
119
  }
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(`
120
+ } while (!t.signal.aborted && u.content.some((m) => m.type === "tool_use"));
121
+ n.push({ role: "assistant", content: u.content.filter((m) => m.type == "text").map((m) => m.text).join(`
122
122
 
123
- `) }), o = this.toStandard(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), a(o.at(-1)?.content);
123
+ `) }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), l(n.at(-1)?.content);
124
124
  }), { abort: () => t.abort() });
125
125
  }
126
126
  }
127
127
  class P extends L {
128
- constructor(r, e, t, a) {
129
- super(), this.ai = r, this.host = e, this.token = t, this.model = a, this.client = new B(W({
128
+ constructor(r, e, t, l) {
129
+ super(), this.ai = r, this.host = e, this.token = t, this.model = l, this.client = new G(J({
130
130
  baseURL: e,
131
131
  apiKey: t || e ? "ignored" : void 0
132
132
  }));
@@ -136,17 +136,17 @@ class P extends L {
136
136
  for (let e = 0; e < r.length; e++) {
137
137
  const t = r[e];
138
138
  if (t.role === "assistant" && t.tool_calls) {
139
- const a = t.tool_calls.map((o) => ({
139
+ const l = t.tool_calls.map((n) => ({
140
140
  role: "tool",
141
- id: o.id,
142
- name: o.function.name,
143
- args: S(o.function.arguments, {}),
141
+ id: n.id,
142
+ name: n.function.name,
143
+ args: $(n.function.arguments, {}),
144
144
  timestamp: t.timestamp
145
145
  }));
146
- r.splice(e, 1, ...a), e += a.length - 1;
146
+ r.splice(e, 1, ...l), e += l.length - 1;
147
147
  } else if (t.role === "tool" && t.content) {
148
- const a = r.find((o) => t.tool_call_id == o.id);
149
- a && (t.content.includes('"error":') ? a.error = t.content : a.content = t.content), r.splice(e, 1), e--;
148
+ const l = r.find((n) => t.tool_call_id == n.id);
149
+ l && (t.content.includes('"error":') ? l.error = t.content : l.content = t.content), r.splice(e, 1), e--;
150
150
  }
151
151
  r[e]?.timestamp || (r[e].timestamp = Date.now());
152
152
  }
@@ -167,53 +167,53 @@ class P extends L {
167
167
  content: t.error || t.content
168
168
  });
169
169
  else {
170
- const { timestamp: a, ...o } = t;
171
- e.push(o);
170
+ const { timestamp: l, ...n } = t;
171
+ e.push(n);
172
172
  }
173
173
  return e;
174
174
  }, []);
175
175
  }
176
176
  ask(r, e = {}) {
177
177
  const t = new AbortController();
178
- return Object.assign(new Promise(async (a, o) => {
178
+ return Object.assign(new Promise(async (l, n) => {
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
- let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
181
- const m = e.tools || this.ai.options.llm?.tools || [], l = {
180
+ let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
181
+ const o = e.tools || this.ai.options.llm?.tools || [], u = {
182
182
  model: e.model || this.model,
183
- messages: n,
183
+ messages: s,
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: m.map((s) => ({
187
+ tools: o.map((i) => ({
188
188
  type: "function",
189
189
  function: {
190
- name: s.name,
191
- description: s.description,
190
+ name: i.name,
191
+ description: i.description,
192
192
  parameters: {
193
193
  type: "object",
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]) : []
194
+ properties: i.args ? O(i.args, (p, d) => ({ ...d, required: void 0 })) : {},
195
+ required: i.args ? Object.entries(i.args).filter((p) => p[1].required).map((p) => p[0]) : []
196
196
  }
197
197
  }
198
198
  }))
199
199
  };
200
- let i, u = !0;
200
+ let a, m = !0;
201
201
  do {
202
- if (i = await this.client.chat.completions.create(l).catch((c) => {
203
- throw c.message += `
202
+ if (a = await this.client.chat.completions.create(u).catch((p) => {
203
+ throw p.message += `
204
204
 
205
205
  Messages:
206
- ${JSON.stringify(n, null, 2)}`, c;
206
+ ${JSON.stringify(s, null, 2)}`, p;
207
207
  }), e.stream) {
208
- u ? u = !1 : e.stream({ text: `
208
+ m ? m = !1 : e.stream({ text: `
209
209
 
210
- ` }), i.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
- for await (const c of i) {
210
+ ` }), a.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
+ for await (const p of a) {
212
212
  if (t.signal.aborted) break;
213
- if (c.choices[0].delta.content && (i.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 = i.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))) : i.choices[0].message.tool_calls.push({
213
+ if (p.choices[0].delta.content && (a.choices[0].message.content += p.choices[0].delta.content, e.stream({ text: p.choices[0].delta.content })), p.choices[0].delta.tool_calls)
214
+ for (const d of p.choices[0].delta.tool_calls) {
215
+ const c = a.choices[0].message.tool_calls.find((f) => f.index === d.index);
216
+ c ? (d.id && (c.id = d.id), d.type && (c.type = d.type), d.function && (c.function || (c.function = {}), d.function.name && (c.function.name = d.function.name), d.function.arguments && (c.function.arguments = (c.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,30 +225,30 @@ ${JSON.stringify(n, null, 2)}`, c;
225
225
  }
226
226
  }
227
227
  }
228
- const s = i.choices[0].message.tool_calls || [];
229
- if (s.length && !t.signal.aborted) {
230
- n.push(i.choices[0].message);
231
- const c = await Promise.all(s.map(async (d) => {
232
- const p = m?.find(R("name", d.function.name));
233
- if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
228
+ const i = a.choices[0].message.tool_calls || [];
229
+ if (i.length && !t.signal.aborted) {
230
+ s.push(a.choices[0].message);
231
+ const p = await Promise.all(i.map(async (d) => {
232
+ const c = o?.find(U("name", d.function.name));
233
+ if (e.stream && e.stream({ tool: d.function.name }), !c) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
234
234
  try {
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) };
235
+ const f = $(d.function.arguments, {}), g = await c.fn(f, e.stream, this.ai);
236
+ return { role: "tool", tool_call_id: d.id, content: typeof g == "object" ? T(g) : g };
237
237
  } catch (f) {
238
- return { role: "tool", tool_call_id: d.id, content: _({ error: f?.message || f?.toString() || "Unknown" }) };
238
+ return { role: "tool", tool_call_id: d.id, content: T({ error: f?.message || f?.toString() || "Unknown" }) };
239
239
  }
240
240
  }));
241
- n.push(...c), l.messages = n;
241
+ s.push(...p), u.messages = s;
242
242
  }
243
- } while (!t.signal.aborted && i.choices?.[0]?.message?.tool_calls?.length);
244
- n.push({ role: "assistant", content: i.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), a(n.at(-1)?.content);
243
+ } while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
244
+ s.push({ role: "assistant", content: a.choices[0].message.content || "" }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), l(s.at(-1)?.content);
245
245
  }), { abort: () => t.abort() });
246
246
  }
247
247
  }
248
- class X {
248
+ class re {
249
249
  constructor(r) {
250
250
  this.ai = r, r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
251
- this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new Q(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new P(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new P(this.ai, t.host || null, t.token, e));
251
+ this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new te(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new P(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new P(this.ai, t.host || null, t.token, e));
252
252
  });
253
253
  }
254
254
  defaultModel;
@@ -270,27 +270,27 @@ class X {
270
270
  };
271
271
  const t = e.model || this.defaultModel;
272
272
  if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
273
- let a = () => {
273
+ let l = () => {
274
274
  };
275
- return Object.assign(new Promise(async (o) => {
275
+ return Object.assign(new Promise(async (n) => {
276
276
  if (e.history || (e.history = []), e.memory) {
277
- const m = async (i, u, s = 10) => {
278
- const [c, d] = await Promise.all([
279
- u ? this.embedding(u) : Promise.resolve(null),
280
- i ? this.embedding(i) : Promise.resolve(null)
277
+ const o = async (a, m, i = 10) => {
278
+ const [p, d] = await Promise.all([
279
+ m ? this.embedding(m) : Promise.resolve(null),
280
+ a ? this.embedding(a) : Promise.resolve(null)
281
281
  ]);
282
- return (e.memory || []).map((p) => {
283
- const f = (c ? this.cosineSimilarity(p.embeddings[0], c[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
284
- return { ...p, score: f };
285
- }).toSorted((p, f) => p.score - f.score).slice(0, s).map((p) => `- ${p.owner}: ${p.fact}`).join(`
282
+ return (e.memory || []).map((c) => {
283
+ const f = (p ? this.cosineSimilarity(c.embeddings[0], p[0].embedding) : 0) + (d ? this.cosineSimilarity(c.embeddings[1], d[0].embedding) : 0);
284
+ return { ...c, score: f };
285
+ }).toSorted((c, f) => c.score - f.score).slice(0, i).map((c) => `- ${c.owner}: ${c.fact}`).join(`
286
286
  `);
287
287
  };
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 m(r);
292
- l.length && e.history.push({ role: "tool", name: "recall", id: "auto_recall_" + Math.random().toString(), args: {}, content: `Things I remembered:
293
- ${l}` }), e.tools = [{
291
+ const u = await o(r);
292
+ u.length && e.history.push({ role: "tool", name: "recall", id: "auto_recall_" + Math.random().toString(), args: {}, content: `Things I remembered:
293
+ ${u}` }), e.tools = [{
294
294
  name: "recall",
295
295
  description: "Recall the closest memories you have regarding a query using RAG",
296
296
  args: {
@@ -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: (i) => {
302
- if (!i.subject && !i.query) throw new Error("Either a subject or query argument is required");
303
- return m(i.query, i.subject, i.topK);
301
+ fn: (a) => {
302
+ if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
303
+ return o(a.query, a.subject, a.topK);
304
304
  }
305
305
  }, {
306
306
  name: "remember",
@@ -309,31 +309,31 @@ ${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 (i) => {
312
+ fn: async (a) => {
313
313
  if (!e.memory) return;
314
- const u = await Promise.all([
315
- this.embedding(i.owner),
316
- this.embedding(`${i.owner}: ${i.fact}`)
317
- ]), s = { owner: i.owner, fact: i.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!";
314
+ const m = await Promise.all([
315
+ this.embedding(a.owner),
316
+ this.embedding(`${a.owner}: ${a.fact}`)
317
+ ]), i = { owner: a.owner, fact: a.fact, embeddings: [m[0][0].embedding, m[1][0].embedding] };
318
+ return e.memory.splice(0, e.memory.length, ...e.memory.filter((p) => !(this.cosineSimilarity(i.embeddings[0], p.embeddings[0]) >= 0.9 && this.cosineSimilarity(i.embeddings[1], p.embeddings[1]) >= 0.8)), i), "Remembered!";
319
319
  }
320
320
  }, ...e.tools || []];
321
321
  }
322
- const n = await this.models[t].ask(r, e);
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);
322
+ const s = await this.models[t].ask(r, e);
323
+ if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((o) => o.role != "tool" || o.name != "recall" && o.name != "remember")), e.compress) {
324
+ const o = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
325
+ e.history.splice(0, e.history.length, ...o);
326
326
  }
327
- return o(n);
328
- }), { abort: a });
327
+ return n(s);
328
+ }), { abort: l });
329
329
  }
330
330
  async code(r, e) {
331
331
  const t = await this.ask(r, { ...e, system: [
332
332
  e?.system,
333
333
  "Return your response in a code block"
334
- ].filter((o) => !!o).join(`
335
- `) }), a = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
336
- return a ? a[1].trim() : null;
334
+ ].filter((n) => !!n).join(`
335
+ `) }), l = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
336
+ return l ? l[1].trim() : null;
337
337
  }
338
338
  /**
339
339
  * Compress chat history to reduce context size
@@ -343,17 +343,17 @@ ${l}` }), e.tools = [{
343
343
  * @param {LLMRequest} options LLM options
344
344
  * @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
345
345
  */
346
- async compressHistory(r, e, t, a) {
346
+ async compressHistory(r, e, t, l) {
347
347
  if (this.estimateTokens(r) < e) return r;
348
- let o = 0, n = 0;
348
+ let n = 0, s = 0;
349
349
  for (let d of r.toReversed())
350
- if (n += this.estimateTokens(d.content), n < t) o++;
350
+ if (s += this.estimateTokens(d.content), s < t) n++;
351
351
  else break;
352
- if (r.length <= o) return r;
353
- const m = r[0].role == "system" ? r[0] : null, l = o == 0 ? [] : r.slice(-o), i = (o == 0 ? r : r.slice(0, -o)).filter((d) => d.role === "assistant" || d.role === "user"), u = await this.summarize(i.map((d) => `[${d.role}]: ${d.content}`).join(`
352
+ if (r.length <= n) return r;
353
+ const o = r[0].role == "system" ? r[0] : null, u = n == 0 ? [] : r.slice(-n), a = (n == 0 ? r : r.slice(0, -n)).filter((d) => d.role === "assistant" || d.role === "user"), m = await this.summarize(a.map((d) => `[${d.role}]: ${d.content}`).join(`
354
354
 
355
- `), 500, a), 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;
355
+ `), 500, l), i = Date.now(), p = [{ role: "tool", name: "summary", id: "summary_" + i, args: {}, content: `Conversation Summary: ${m?.summary}`, timestamp: i }, ...u];
356
+ return o && p.splice(0, 0, o), p;
357
357
  }
358
358
  /**
359
359
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -363,11 +363,11 @@ ${l}` }), e.tools = [{
363
363
  */
364
364
  cosineSimilarity(r, e) {
365
365
  if (r.length !== e.length) throw new Error("Vectors must be same length");
366
- let t = 0, a = 0, o = 0;
367
- for (let m = 0; m < r.length; m++)
368
- t += r[m] * e[m], a += r[m] * r[m], o += e[m] * e[m];
369
- const n = Math.sqrt(a) * Math.sqrt(o);
370
- return n === 0 ? 0 : t / n;
366
+ let t = 0, l = 0, n = 0;
367
+ for (let o = 0; o < r.length; o++)
368
+ t += r[o] * e[o], l += r[o] * r[o], n += e[o] * e[o];
369
+ const s = Math.sqrt(l) * Math.sqrt(n);
370
+ return s === 0 ? 0 : t / s;
371
371
  }
372
372
  /**
373
373
  * Chunk text into parts for AI digestion
@@ -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 a = (l, i = "") => l ? Object.entries(l).flatMap(([u, s]) => {
381
- const c = i ? `${i}${isNaN(+u) ? `.${u}` : `[${u}]`}` : u;
382
- return typeof s == "object" && !Array.isArray(s) ? a(s, c) : `${c}: ${Array.isArray(s) ? s.join(", ") : s}`;
383
- }) : [], n = (typeof r == "object" ? a(r) : r.toString().split(`
384
- `)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
385
- `]), m = [];
386
- for (let l = 0; l < n.length; ) {
387
- let i = "", u = l;
388
- for (; u < n.length; ) {
389
- const c = i + (i ? " " : "") + n[u];
390
- if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
391
- `)) > e && i) break;
392
- i = c, u++;
380
+ const l = (u, a = "") => u ? Object.entries(u).flatMap(([m, i]) => {
381
+ const p = a ? `${a}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
382
+ return typeof i == "object" && !Array.isArray(i) ? l(i, p) : `${p}: ${Array.isArray(i) ? i.join(", ") : i}`;
383
+ }) : [], s = (typeof r == "object" ? l(r) : r.toString().split(`
384
+ `)).flatMap((u) => [...u.split(/\s+/).filter(Boolean), `
385
+ `]), o = [];
386
+ for (let u = 0; u < s.length; ) {
387
+ let a = "", m = u;
388
+ for (; m < s.length; ) {
389
+ const p = a + (a ? " " : "") + s[m];
390
+ if (this.estimateTokens(p.replace(/\s*\n\s*/g, `
391
+ `)) > e && a) break;
392
+ a = p, m++;
393
393
  }
394
- const s = i.replace(/\s*\n\s*/g, `
394
+ const i = a.replace(/\s*\n\s*/g, `
395
395
  `).trim();
396
- s && m.push(s), l = Math.max(u - t, u === l ? l + 1 : u);
396
+ i && o.push(i), u = Math.max(m - t, m === u ? u + 1 : m);
397
397
  }
398
- return m;
398
+ return o;
399
399
  }
400
400
  /**
401
401
  * Create a vector representation of a string
@@ -404,39 +404,39 @@ ${l}` }), e.tools = [{
404
404
  * @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
405
405
  */
406
406
  embedding(r, e = {}) {
407
- let { maxTokens: t = 500, overlapTokens: a = 50 } = e, o = !1;
408
- const n = () => {
409
- o = !0;
410
- }, m = (i) => new Promise((u, s) => {
411
- if (o) return s(new Error("Aborted"));
412
- const c = [
413
- H(G(D(import.meta.url)), "embedder.js"),
407
+ let { maxTokens: t = 500, overlapTokens: l = 50 } = e, n = !1;
408
+ const s = () => {
409
+ n = !0;
410
+ }, o = (a) => new Promise((m, i) => {
411
+ if (n) return i(new Error("Aborted"));
412
+ const p = [
413
+ Y(V(K(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", c, { stdio: ["pipe", "pipe", "ignore"] });
417
- d.stdin.write(i), d.stdin.end();
418
- let p = "";
419
- d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
420
- if (o) return s(new Error("Aborted"));
416
+ ], d = k("node", p, { stdio: ["pipe", "pipe", "ignore"] });
417
+ d.stdin.write(a), d.stdin.end();
418
+ let c = "";
419
+ d.stdout.on("data", (f) => c += f.toString()), d.on("close", (f) => {
420
+ if (n) return i(new Error("Aborted"));
421
421
  if (f === 0)
422
422
  try {
423
- const g = JSON.parse(p);
424
- u(g.embedding);
423
+ const g = JSON.parse(c);
424
+ m(g.embedding);
425
425
  } catch {
426
- s(new Error("Failed to parse embedding output"));
426
+ i(new Error("Failed to parse embedding output"));
427
427
  }
428
428
  else
429
- s(new Error(`Embedder process exited with code ${f}`));
430
- }), d.on("error", s);
431
- }), l = (async () => {
432
- const i = this.chunk(r, t, a), u = [];
433
- for (let s = 0; s < i.length && !o; s++) {
434
- const c = i[s], d = await m(c);
435
- u.push({ index: s, embedding: d, text: c, tokens: this.estimateTokens(c) });
429
+ i(new Error(`Embedder process exited with code ${f}`));
430
+ }), d.on("error", i);
431
+ }), u = (async () => {
432
+ const a = this.chunk(r, t, l), m = [];
433
+ for (let i = 0; i < a.length && !n; i++) {
434
+ const p = a[i], d = await o(p);
435
+ m.push({ index: i, embedding: d, text: p, tokens: this.estimateTokens(p) });
436
436
  }
437
- return u;
437
+ return m;
438
438
  })();
439
- return Object.assign(l, { abort: n });
439
+ return Object.assign(u, { abort: s });
440
440
  }
441
441
  /**
442
442
  * Estimate variable as tokens
@@ -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, m = 10) => n.toLowerCase().split("").map((l, i) => l.charCodeAt(0) * (i + 1) % m / m).slice(0, m), a = t(r), o = e.map((n) => t(n)).map((n) => this.cosineSimilarity(a, n));
459
- return { avg: o.reduce((n, m) => n + m, 0) / o.length, max: Math.max(...o), similarities: o };
458
+ const t = (s, o = 10) => s.toLowerCase().split("").map((u, a) => u.charCodeAt(0) * (a + 1) % o / o).slice(0, o), l = t(r), n = e.map((s) => t(s)).map((s) => this.cosineSimilarity(l, s));
459
+ return { avg: n.reduce((s, o) => s + o, 0) / n.length, max: Math.max(...n), similarities: n };
460
460
  }
461
461
  /**
462
462
  * Ask a question with JSON response
@@ -466,28 +466,28 @@ ${l}` }), e.tools = [{
466
466
  * @returns {Promise<{} | {} | RegExpExecArray | null>}
467
467
  */
468
468
  async json(r, e, t) {
469
- let a = `Your job is to convert input to JSON using tool calls. Call the \`submit\` tool at least once with JSON matching this schema:
469
+ let l = `Your job is to convert input to JSON using tool calls. Call the \`submit\` tool at least once with JSON matching this schema:
470
470
  \`\`\`json
471
471
  ${e}
472
472
  \`\`\`
473
473
 
474
474
  Responses are ignored`;
475
- return t?.system && (a += `
475
+ return t?.system && (l += `
476
476
 
477
- ` + t.system), new Promise(async (o, n) => {
478
- let m = !1;
479
- const l = await this.ask(r, {
477
+ ` + t.system), new Promise(async (n, s) => {
478
+ let o = !1;
479
+ const u = await this.ask(r, {
480
480
  temperature: 0.3,
481
481
  ...t,
482
- system: a,
482
+ system: l,
483
483
  tools: [{
484
484
  name: "submit",
485
485
  description: "Submit JSON",
486
486
  args: { json: { type: "string", description: "Javascript parsable JSON string", required: !0 } },
487
- fn: (i) => {
487
+ fn: (a) => {
488
488
  try {
489
- const u = JSON.parse(i.json);
490
- o(u), m = !0;
489
+ const m = JSON.parse(a.json);
490
+ n(m), o = !0;
491
491
  } catch {
492
492
  return "Invalid JSON";
493
493
  }
@@ -495,8 +495,8 @@ Responses are ignored`;
495
495
  }
496
496
  }, ...t?.tools || []]
497
497
  });
498
- m || n(`AI failed to create JSON:
499
- ${l}`);
498
+ o || s(`AI failed to create JSON:
499
+ ${u}`);
500
500
  });
501
501
  }
502
502
  /**
@@ -507,28 +507,28 @@ ${l}`);
507
507
  * @returns {Promise<string>} Summary
508
508
  */
509
509
  async summarize(r, e = 500, t) {
510
- let a = `Your job is to summarize the users message using tool calls. Call the \`submit\` tool at least once with the shortest summary possible that's <= ${e} words. The tool call will respond with the token count. Responses are ignored`;
511
- return t?.system && (a += `
510
+ let l = `Your job is to summarize the users message using tool calls. Call the \`submit\` tool at least once with the shortest summary possible that's <= ${e} words. The tool call will respond with the token count. Responses are ignored`;
511
+ return t?.system && (l += `
512
512
 
513
- ` + t.system), new Promise(async (o, n) => {
514
- let m = !1;
515
- const l = await this.ask(r, {
513
+ ` + t.system), new Promise(async (n, s) => {
514
+ let o = !1;
515
+ const u = await this.ask(r, {
516
516
  temperature: 0.3,
517
517
  ...t,
518
- system: a,
518
+ system: l,
519
519
  tools: [{
520
520
  name: "submit",
521
521
  description: "Submit summary",
522
522
  args: { summary: { type: "string", description: "Text summarization", required: !0 } },
523
- fn: (i) => i.summary ? i.summary.split(" ").length > e ? `Too long: ${e} words` : (m = !0, o(i.summary || null), `Saved: ${e} words`) : "No summary provided"
523
+ fn: (a) => a.summary ? a.summary.split(" ").length > e ? `Too long: ${e} words` : (o = !0, n(a.summary || null), `Saved: ${e} words`) : "No summary provided"
524
524
  }, ...t?.tools || []]
525
525
  });
526
- m || n(`AI failed to create summary:
527
- ${l}`);
526
+ o || s(`AI failed to create summary:
527
+ ${u}`);
528
528
  });
529
529
  }
530
530
  }
531
- class ee {
531
+ class ne {
532
532
  constructor(r) {
533
533
  this.ai = r, r.options.whisper && (this.whisperModel = r.options.asr || "ggml-base.en.bin", this.downloadAsrModel()), this.pyannote = `
534
534
  import sys
@@ -551,21 +551,21 @@ print(json.dumps(segments))
551
551
  pyannote;
552
552
  whisperModel;
553
553
  async addPunctuation(r, e, t = 150) {
554
- const a = (n) => {
555
- if (n = n.toLowerCase().replace(/[^a-z]/g, ""), n.length <= 3) return 1;
556
- const m = n.match(/[aeiouy]+/g);
557
- let l = m ? m.length : 1;
558
- return n.endsWith("e") && l--, Math.max(1, l);
554
+ const l = (s) => {
555
+ if (s = s.toLowerCase().replace(/[^a-z]/g, ""), s.length <= 3) return 1;
556
+ const o = s.match(/[aeiouy]+/g);
557
+ let u = o ? o.length : 1;
558
+ return s.endsWith("e") && u--, Math.max(1, u);
559
559
  };
560
- let o = "";
561
- return r.transcription.filter((n, m) => {
562
- let l = !1;
563
- const i = r.transcription[m - 1], u = r.transcription[m + 1];
564
- return !n.text && u ? (u.offsets.from = n.offsets.from, u.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && i && (i.offsets.to = n.offsets.to, i.timestamps.to = n.timestamps.to, i.text += n.text, l = !0), !!n.text && !l;
565
- }).forEach((n) => {
566
- const m = /^[A-Z]/.test(n.text.trim()), l = n.offsets.to - n.offsets.from, u = a(n.text.trim()) * t;
567
- m && l > u * 2 && n.text[0] == " " && (o += "."), o += n.text;
568
- }), e ? this.ai.language.ask(o, {
560
+ let n = "";
561
+ return r.transcription.filter((s, o) => {
562
+ let u = !1;
563
+ const a = r.transcription[o - 1], m = r.transcription[o + 1];
564
+ return !s.text && m ? (m.offsets.from = s.offsets.from, m.timestamps.from = s.offsets.from) : s.text && s.text[0] != " " && a && (a.offsets.to = s.offsets.to, a.timestamps.to = s.timestamps.to, a.text += s.text, u = !0), !!s.text && !u;
565
+ }).forEach((s) => {
566
+ const o = /^[A-Z]/.test(s.text.trim()), u = s.offsets.to - s.offsets.from, m = l(s.text.trim()) * t;
567
+ o && u > m * 2 && s.text[0] == " " && (n += "."), n += s.text;
568
+ }), e ? this.ai.language.ask(n, {
569
569
  system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
570
570
  temperature: 0.1,
571
571
  tools: [{
@@ -575,141 +575,141 @@ print(json.dumps(segments))
575
575
  find: { type: "string", description: "Text to find", required: !0 },
576
576
  replace: { type: "string", description: "Text to replace", required: !0 }
577
577
  },
578
- fn: (n) => o = o.replace(n.find, n.replace)
578
+ fn: (s) => n = n.replace(s.find, s.replace)
579
579
  }]
580
- }).then(() => o) : o.trim();
580
+ }).then(() => n) : n.trim();
581
581
  }
582
582
  async diarizeTranscript(r, e, t) {
583
- const a = /* @__PURE__ */ new Map();
584
- let o = 0;
585
- e.forEach((p) => {
586
- a.has(p.speaker) || a.set(p.speaker, ++o);
583
+ const l = /* @__PURE__ */ new Map();
584
+ let n = 0;
585
+ e.forEach((c) => {
586
+ l.has(c.speaker) || l.set(c.speaker, ++n);
587
587
  });
588
- const n = await this.addPunctuation(r, t), m = n.match(/[^.!?]+[.!?]+/g) || [n], l = r.transcription.filter((p) => p.text.trim()), i = m.map((p) => {
589
- if (p = p.trim(), !p) return null;
590
- const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
591
- f.forEach((x) => {
592
- const k = l.find((y) => x === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
593
- if (!k) return;
594
- const E = k.offsets.from / 1e3, $ = e.find((y) => E >= y.start && E <= y.end);
595
- if ($) {
596
- const y = a.get($.speaker);
597
- g.set(y, (g.get(y) || 0) + 1);
588
+ const s = await this.addPunctuation(r, t), o = s.match(/[^.!?]+[.!?]+/g) || [s], u = r.transcription.filter((c) => c.text.trim()), a = o.map((c) => {
589
+ if (c = c.trim(), !c) return null;
590
+ const f = c.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
591
+ f.forEach((y) => {
592
+ const _ = u.find((w) => y === w.text.trim().toLowerCase().replace(/[^\w]/g, ""));
593
+ if (!_) return;
594
+ const v = _.offsets.from / 1e3, E = e.find((w) => v >= w.start && v <= w.end);
595
+ if (E) {
596
+ const w = l.get(E.speaker);
597
+ g.set(w, (g.get(w) || 0) + 1);
598
598
  }
599
599
  });
600
- let T = 1, v = 0;
601
- return g.forEach((x, k) => {
602
- x > v && (v = x, T = k);
603
- }), { speaker: T, text: p };
604
- }).filter((p) => p !== null), u = [];
605
- i.forEach((p) => {
606
- const f = u[u.length - 1];
607
- f && f.speaker === p.speaker ? f.text += " " + p.text : u.push({ ...p });
600
+ let b = 1, S = 0;
601
+ return g.forEach((y, _) => {
602
+ y > S && (S = y, b = _);
603
+ }), { speaker: b, text: c };
604
+ }).filter((c) => c !== null), m = [];
605
+ a.forEach((c) => {
606
+ const f = m[m.length - 1];
607
+ f && f.speaker === c.speaker ? f.text += " " + c.text : m.push({ ...c });
608
608
  });
609
- let s = u.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
609
+ let i = m.map((c) => `[Speaker ${c.speaker}]: ${c.text}`).join(`
610
610
  `).trim();
611
- if (!t) return s;
612
- let c = this.ai.language.chunk(s, 500, 0);
613
- c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
614
- const d = await this.ai.language.json(c.join(`
611
+ if (!t) return i;
612
+ let p = this.ai.language.chunk(i, 500, 0);
613
+ p.length > 4 && (p = [...p.slice(0, 3), p.at(-1)]);
614
+ const d = await this.ai.language.json(p.join(`
615
615
  `), '{1: "Detected Name", 2: "Second Name"}', {
616
616
  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",
617
617
  temperature: 0.1
618
618
  });
619
- return Object.entries(d).forEach(([p, f]) => s = s.replaceAll(`[Speaker ${p}]`, `[${f}]`)), s;
619
+ return Object.entries(d).forEach(([c, f]) => i = i.replaceAll(`[Speaker ${c}]`, `[${f}]`)), i;
620
620
  }
621
621
  runAsr(r, e = {}) {
622
622
  let t;
623
- const a = new Promise((o, n) => {
624
- this.downloadAsrModel(e.model).then((m) => {
623
+ const l = new Promise((n, s) => {
624
+ this.downloadAsrModel(e.model).then((o) => {
625
625
  if (e.diarization) {
626
- let l = M.join(M.dirname(r), "transcript");
627
- t = w(
626
+ let u = q.join(q.dirname(r), "transcript");
627
+ t = k(
628
628
  this.ai.options.whisper,
629
- ["-m", m, "-f", r, "-np", "-ml", "1", "-oj", "-of", l],
629
+ ["-m", o, "-f", r, "-np", "-ml", "1", "-oj", "-of", u],
630
630
  { stdio: ["ignore", "ignore", "pipe"] }
631
- ), t.on("error", (i) => n(i)), t.on("close", async (i) => {
632
- if (i === 0) {
633
- l = await b.readFile(l + ".json", "utf-8"), b.rm(l + ".json").catch(() => {
631
+ ), t.on("error", (a) => s(a)), t.on("close", async (a) => {
632
+ if (a === 0) {
633
+ u = await x.readFile(u + ".json", "utf-8"), x.rm(u + ".json").catch(() => {
634
634
  });
635
635
  try {
636
- o(JSON.parse(l));
636
+ n(JSON.parse(u));
637
637
  } catch {
638
- n(new Error("Failed to parse whisper JSON"));
638
+ s(new Error("Failed to parse whisper JSON"));
639
639
  }
640
640
  } else
641
- n(new Error(`Exit code ${i}`));
641
+ s(new Error(`Exit code ${a}`));
642
642
  });
643
643
  } else {
644
- let l = "";
645
- t = w(this.ai.options.whisper, ["-m", m, "-f", r, "-np", "-nt"]), t.on("error", (i) => n(i)), t.stdout.on("data", (i) => l += i.toString()), t.on("close", async (i) => {
646
- i === 0 ? o(l.trim() || null) : n(new Error(`Exit code ${i}`));
644
+ let u = "";
645
+ t = k(this.ai.options.whisper, ["-m", o, "-f", r, "-np", "-nt"]), t.on("error", (a) => s(a)), t.stdout.on("data", (a) => u += a.toString()), t.on("close", async (a) => {
646
+ a === 0 ? n(u.trim() || null) : s(new Error(`Exit code ${a}`));
647
647
  });
648
648
  }
649
649
  });
650
650
  });
651
- return Object.assign(a, { abort: () => t?.kill("SIGTERM") });
651
+ return Object.assign(l, { abort: () => t?.kill("SIGTERM") });
652
652
  }
653
653
  runDiarization(r) {
654
654
  let e = !1, t = () => {
655
655
  e = !0;
656
656
  };
657
- const a = (n) => new Promise((m) => {
658
- const l = w(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
659
- l.on("close", (i) => m(i === 0)), l.on("error", () => m(!1));
660
- }), o = Promise.all([
661
- a("python"),
662
- a("python3")
663
- ]).then((async ([n, m]) => {
657
+ const l = (s) => new Promise((o) => {
658
+ const u = k(s, ["-W", "ignore", "-c", "import pyannote.audio"]);
659
+ u.on("close", (a) => o(a === 0)), u.on("error", () => o(!1));
660
+ }), n = Promise.all([
661
+ l("python"),
662
+ l("python3")
663
+ ]).then((async ([s, o]) => {
664
664
  if (e) return;
665
- if (!n && !m) throw new Error("Pyannote is not installed: pip install pyannote.audio");
666
- const l = m ? "python3" : "python";
667
- return new Promise((i, u) => {
665
+ if (!s && !o) throw new Error("Pyannote is not installed: pip install pyannote.audio");
666
+ const u = o ? "python3" : "python";
667
+ return new Promise((a, m) => {
668
668
  if (e) return;
669
- let s = "";
670
- const c = w(l, ["-W", "ignore", "-c", this.pyannote, r]);
671
- c.stdout.on("data", (d) => s += d.toString()), c.stderr.on("data", (d) => console.error(d.toString())), c.on("close", (d) => {
669
+ let i = "";
670
+ const p = k(u, ["-W", "ignore", "-c", this.pyannote, r]);
671
+ p.stdout.on("data", (d) => i += d.toString()), p.stderr.on("data", (d) => console.error(d.toString())), p.on("close", (d) => {
672
672
  if (d === 0)
673
673
  try {
674
- i(JSON.parse(s));
674
+ a(JSON.parse(i));
675
675
  } catch {
676
- u(new Error("Failed to parse diarization output"));
676
+ m(new Error("Failed to parse diarization output"));
677
677
  }
678
678
  else
679
- u(new Error(`Python process exited with code ${d}`));
680
- }), c.on("error", u), t = () => c.kill("SIGTERM");
679
+ m(new Error(`Python process exited with code ${d}`));
680
+ }), p.on("error", m), t = () => p.kill("SIGTERM");
681
681
  });
682
682
  }));
683
- return Object.assign(o, { abort: t });
683
+ return Object.assign(n, { abort: t });
684
684
  }
685
685
  asr(r, e = {}) {
686
686
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
687
- const t = A(Y(A(N(), "audio-")), "converted.wav");
688
- K(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
689
- const a = () => b.rm(q.dirname(t), { recursive: !0, force: !0 }).catch(() => {
687
+ const t = A(Q(A(W(), "audio-")), "converted.wav");
688
+ Z(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
689
+ const l = () => x.rm(M.dirname(t), { recursive: !0, force: !0 }).catch(() => {
690
690
  });
691
691
  if (!e.diarization) return this.runAsr(t, { model: e.model });
692
- const o = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
693
- let m = !1, l = () => {
694
- m = !0, o.abort(), n.abort(), a();
692
+ const n = this.runAsr(t, { model: e.model, diarization: !0 }), s = this.runDiarization(t);
693
+ let o = !1, u = () => {
694
+ o = !0, n.abort(), s.abort(), l();
695
695
  };
696
- const i = Promise.allSettled([o, n]).then(async ([u, s]) => {
697
- if (u.status == "rejected") throw new Error(`Whisper.cpp timestamps:
698
- ` + u.reason);
699
- if (s.status == "rejected") throw new Error(`Pyannote:
700
- ` + s.reason);
701
- return m || !e.diarization ? u.value : this.diarizeTranscript(u.value, s.value, e.diarization == "llm");
702
- }).finally(() => a());
703
- return Object.assign(i, { abort: l });
696
+ const a = Promise.allSettled([n, s]).then(async ([m, i]) => {
697
+ if (m.status == "rejected") throw new Error(`Whisper.cpp timestamps:
698
+ ` + m.reason);
699
+ if (i.status == "rejected") throw new Error(`Pyannote:
700
+ ` + i.reason);
701
+ return o || !e.diarization ? m.value : this.diarizeTranscript(m.value, i.value, e.diarization == "llm");
702
+ }).finally(() => l());
703
+ return Object.assign(a, { abort: u });
704
704
  }
705
705
  async downloadAsrModel(r = this.whisperModel) {
706
706
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
707
707
  r.endsWith(".bin") || (r += ".bin");
708
- const e = q.join(this.ai.options.path, r);
709
- return await b.stat(e).then(() => !0).catch(() => !1) ? e : this.downloads[r] ? this.downloads[r] : (this.downloads[r] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${r}`).then((t) => t.arrayBuffer()).then((t) => Buffer.from(t)).then(async (t) => (await b.writeFile(e, t), delete this.downloads[r], e)), this.downloads[r]);
708
+ const e = M.join(this.ai.options.path, r);
709
+ return await x.stat(e).then(() => !0).catch(() => !1) ? e : this.downloads[r] ? this.downloads[r] : (this.downloads[r] = fetch(`https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${r}`).then((t) => t.arrayBuffer()).then((t) => Buffer.from(t)).then(async (t) => (await x.writeFile(e, t), delete this.downloads[r], e)), this.downloads[r]);
710
710
  }
711
711
  }
712
- class te {
712
+ class se {
713
713
  constructor(r) {
714
714
  this.ai = r;
715
715
  }
@@ -720,17 +720,17 @@ class te {
720
720
  */
721
721
  ocr(r) {
722
722
  let e;
723
- const t = new Promise(async (a) => {
724
- e = await V(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
725
- const { data: o } = await e.recognize(r);
726
- await e.terminate(), a(o.text.trim() || null);
723
+ const t = new Promise(async (l) => {
724
+ e = await X(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
725
+ const { data: n } = await e.recognize(r);
726
+ await e.terminate(), l(n.text.trim() || null);
727
727
  });
728
728
  return Object.assign(t, { abort: () => e?.terminate() });
729
729
  }
730
730
  }
731
- class be {
731
+ class ke {
732
732
  constructor(r) {
733
- this.options = r, r.path || (r.path = O.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new ee(this), this.language = new X(this), this.vision = new te(this);
733
+ this.options = r, r.path || (r.path = R.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new ne(this), this.language = new re(this), this.vision = new se(this);
734
734
  }
735
735
  /** Audio processing AI */
736
736
  audio;
@@ -739,37 +739,37 @@ class be {
739
739
  /** Vision processing AI */
740
740
  vision;
741
741
  }
742
- const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").pop() || "bash", ne = {
742
+ const z = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", oe = () => R.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").pop() || "bash", ie = {
743
743
  name: "cli",
744
744
  description: "Use the command line interface, returns any output",
745
745
  args: { command: { type: "string", description: "Command to run", required: !0 } },
746
746
  fn: (h) => j`${h.command}`
747
- }, we = {
747
+ }, Se = {
748
748
  name: "get_datetime",
749
749
  description: "Get local date / time",
750
750
  args: {},
751
751
  fn: async () => (/* @__PURE__ */ new Date()).toString()
752
- }, xe = {
752
+ }, _e = {
753
753
  name: "get_datetime_utc",
754
754
  description: "Get current UTC date / time",
755
755
  args: {},
756
756
  fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
757
- }, ke = {
757
+ }, $e = {
758
758
  name: "exec",
759
759
  description: "Run code/scripts",
760
760
  args: {
761
- language: { type: "string", description: `Execution language (CLI: ${re()})`, enum: ["cli", "node", "python"], required: !0 },
761
+ language: { type: "string", description: `Execution language (CLI: ${oe()})`, enum: ["cli", "node", "python"], required: !0 },
762
762
  code: { type: "string", description: "Code to execute", required: !0 }
763
763
  },
764
764
  fn: async (h, r, e) => {
765
765
  try {
766
766
  switch (h.language) {
767
767
  case "cli":
768
- return await ne.fn({ command: h.code }, r, e);
768
+ return await ie.fn({ command: h.code }, r, e);
769
769
  case "node":
770
- return await se.fn({ code: h.code }, r, e);
770
+ return await ae.fn({ code: h.code }, r, e);
771
771
  case "python":
772
- return await oe.fn({ code: h.code }, r, e);
772
+ return await ce.fn({ code: h.code }, r, e);
773
773
  default:
774
774
  throw new Error(`Unsupported language: ${h.language}`);
775
775
  }
@@ -777,7 +777,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
777
777
  return { error: t?.message || t.toString() };
778
778
  }
779
779
  }
780
- }, Se = {
780
+ }, Te = {
781
781
  name: "fetch",
782
782
  description: "Make HTTP request to URL",
783
783
  args: {
@@ -786,100 +786,122 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
786
786
  headers: { type: "object", description: "HTTP headers to send", default: {} },
787
787
  body: { type: "object", description: "HTTP body to send" }
788
788
  },
789
- fn: (h) => new C({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
790
- }, se = {
789
+ fn: (h) => new F({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
790
+ }, ae = {
791
791
  name: "exec_javascript",
792
792
  description: "Execute commonjs javascript",
793
793
  args: {
794
794
  code: { type: "string", description: "CommonJS javascript", required: !0 }
795
795
  },
796
796
  fn: async (h) => {
797
- const r = J(null), e = await I({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
797
+ const r = B(null), e = await I({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
798
798
  return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
799
799
  }
800
- }, oe = {
800
+ }, ce = {
801
801
  name: "exec_javascript",
802
802
  description: "Execute commonjs javascript",
803
803
  args: {
804
804
  code: { type: "string", description: "CommonJS javascript", required: !0 }
805
805
  },
806
806
  fn: async (h) => ({ result: j`python -c "${h.code}"` })
807
- }, _e = {
807
+ }, je = {
808
808
  name: "read_webpage",
809
- description: "Extract clean, structured content from a webpage or convert media/documents to accessible formats",
809
+ description: "Extract clean content from webpages, or convert media/documents to accessible formats",
810
810
  args: {
811
- url: { type: "string", description: "URL to extract content from", required: !0 },
812
- mimeRegex: { type: "string", description: 'Optional: Regex pattern to filter MIME types (e.g., "^image/", "text/", "application/pdf")' },
813
- maxSize: { type: "number", description: "Optional: Max file size in bytes for binary content (default: 10MB)" }
811
+ url: { type: "string", description: "URL to read", required: !0 },
812
+ mimeRegex: { type: "string", description: 'Optional regex to filter MIME types (e.g., "^image/", "text/")' }
814
813
  },
815
814
  fn: async (h) => {
816
- const e = await fetch(h.url, {
815
+ const t = await fetch(h.url, {
817
816
  headers: {
818
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
817
+ "User-Agent": "AiTools-Webpage/1.0",
819
818
  Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
820
819
  "Accept-Language": "en-US,en;q=0.5"
821
820
  },
822
821
  redirect: "follow"
823
- }).catch((s) => {
824
- throw new Error(`Failed to fetch: ${s.message}`);
825
- }), t = e.headers.get("content-type") || "", a = t.split(";")[0].trim().toLowerCase();
826
- if (t.match(/charset=([^;]+)/)?.[1], h.mimeRegex && !new RegExp(h.mimeRegex, "i").test(a))
827
- return { url: h.url, error: "MIME type rejected", mimeType: a, filter: h.mimeRegex };
828
- if (a.startsWith("image/") || a.startsWith("audio/") || a.startsWith("video/")) {
829
- const s = await e.arrayBuffer();
830
- if (s.byteLength > 10485760)
831
- return { url: h.url, type: "media", mimeType: a, error: "File too large", size: s.byteLength, maxSize: 10485760 };
832
- const c = Buffer.from(s).toString("base64");
833
- return { url: h.url, type: "media", mimeType: a, dataUrl: `data:${a};base64,${c}`, size: s.byteLength };
822
+ }).catch((c) => {
823
+ throw new Error(`Failed to fetch: ${c.message}`);
824
+ }), n = (t.headers.get("content-type") || "").split(";")[0].trim().toLowerCase();
825
+ if (h.mimeRegex && !new RegExp(h.mimeRegex, "i").test(n))
826
+ return `❌ MIME type rejected: ${n} (filter: ${h.mimeRegex})`;
827
+ if (n.match(/^(image|audio|video)\//)) {
828
+ const c = await t.arrayBuffer();
829
+ if (c.byteLength > 10485760)
830
+ return `❌ File too large: ${(c.byteLength / 1024 / 1024).toFixed(1)}MB (max 10MB)
831
+ Type: ${n}`;
832
+ const f = Buffer.from(c).toString("base64");
833
+ return `## Media File
834
+ **Type:** ${n}
835
+ **Size:** ${(c.byteLength / 1024).toFixed(1)}KB
836
+ **Data URL:** \`data:${n};base64,${f.slice(0, 100)}...\``;
834
837
  }
835
- if (a.match(/^(text\/(plain|csv|xml)|application\/(json|xml|csv|x-yaml))/) || h.url.match(/\.(txt|json|xml|csv|yaml|yml|md)$/i)) {
836
- const s = await e.text();
837
- return { url: h.url, type: "text", mimeType: a, content: s.slice(0, 1e5) };
838
+ if (n.match(/^text\/(plain|csv|xml)/) || h.url.match(/\.(txt|csv|xml|md|yaml|yml)$/i)) {
839
+ const c = await t.text(), f = c.length > 5e4 ? c.slice(0, 5e4) : c;
840
+ return `## Text File
841
+ **Type:** ${n}
842
+ **URL:** ${h.url}
843
+
844
+ ${f}`;
838
845
  }
839
- if (a === "application/pdf" || a.startsWith("application/") && !a.includes("html")) {
840
- const s = await e.arrayBuffer();
841
- if (s.byteLength > 10485760)
842
- return { url: h.url, type: "binary", mimeType: a, error: "File too large", size: s.byteLength, maxSize: 10485760 };
843
- const c = Buffer.from(s).toString("base64");
844
- return { url: h.url, type: "binary", mimeType: a, dataUrl: `data:${a};base64,${c}`, size: s.byteLength };
846
+ if (n.match(/application\/(json|xml|csv)/)) {
847
+ const c = await t.text(), f = c.length > 5e4 ? c.slice(0, 5e4) : c;
848
+ return `## Structured Data
849
+ **Type:** ${n}
850
+ **URL:** ${h.url}
851
+
852
+ \`\`\`
853
+ ${f}
854
+ \`\`\``;
845
855
  }
846
- const o = await e.text(), n = Z.load(o);
847
- 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();
848
- const m = {
849
- title: n('meta[property="og:title"]').attr("content") || n("title").text() || "",
850
- description: n('meta[name="description"]').attr("content") || n('meta[property="og:description"]').attr("content") || "",
851
- author: n('meta[name="author"]').attr("content") || "",
852
- published: n('meta[property="article:published_time"]').attr("content") || n("time").attr("datetime") || "",
853
- image: n('meta[property="og:image"]').attr("content") || ""
854
- };
855
- let l = "";
856
- const i = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content", "body"];
857
- for (const s of i) {
858
- const c = n(s).first();
859
- if (c.length && c.text().trim().length > 200) {
860
- l = c.text();
861
- break;
856
+ if (n === "application/pdf" || n.startsWith("application/") && !n.includes("html")) {
857
+ const c = await t.arrayBuffer();
858
+ if (c.byteLength > 10485760)
859
+ return `❌ File too large: ${(c.byteLength / 1024 / 1024).toFixed(1)}MB (max 10MB)
860
+ Type: ${n}`;
861
+ const f = Buffer.from(c).toString("base64");
862
+ return `## Binary File
863
+ **Type:** ${n}
864
+ **Size:** ${(c.byteLength / 1024).toFixed(1)}KB
865
+ **Data URL:** \`data:${n};base64,${f.slice(0, 100)}...\``;
866
+ }
867
+ const s = await t.text(), o = ee.load(s);
868
+ o("script, style, nav, footer, header, aside, iframe, noscript, svg").remove(), o('[role="navigation"], [role="banner"], [role="complementary"]').remove(), o('[aria-hidden="true"], [hidden], .visually-hidden, .sr-only, .screen-reader-text').remove(), o(".ad, .ads, .advertisement, .cookie, .popup, .modal, .sidebar, .related, .comments, .social-share").remove(), o('button, [class*="share"], [class*="follow"], [class*="social"]').remove();
869
+ const u = o('meta[property="og:title"]').attr("content") || o("title").text().trim() || "", a = o('meta[name="description"]').attr("content") || o('meta[property="og:description"]').attr("content") || "", m = o('meta[name="author"]').attr("content") || "";
870
+ let i = "";
871
+ const p = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content"];
872
+ for (const c of p) {
873
+ const f = o(c).first();
874
+ if (f.length && f.text().trim().length > 200) {
875
+ const g = [];
876
+ if (f.find("p").each((b, S) => {
877
+ const y = o(S).text().trim();
878
+ y.length > 80 && g.push(y);
879
+ }), g.length > 2) {
880
+ i = g.join(`
881
+
882
+ `);
883
+ break;
884
+ }
862
885
  }
863
886
  }
864
- l || (l = n("body").text()), l = l.replace(/\n\s*\n\s*\n/g, `
887
+ if (!i) {
888
+ const c = [];
889
+ o("body p").each((f, g) => {
890
+ const b = o(g).text().trim();
891
+ b.length > 80 && c.push(b);
892
+ }), i = c.slice(0, 30).join(`
865
893
 
866
- `).replace(/[ \t]+/g, " ").trim().slice(0, 5e4);
867
- let u = [];
868
- return l.length < 500 && (n("a[href]").each((s, c) => {
869
- const d = n(c).attr("href"), p = n(c).text().trim();
870
- d && p && !d.startsWith("#") && u.push({ text: p, href: d });
871
- }), u = u.slice(0, 50)), {
872
- url: h.url,
873
- type: "html",
874
- title: m.title.trim(),
875
- description: m.description.trim(),
876
- author: m.author.trim(),
877
- published: m.published,
878
- content: l,
879
- links: u.length ? u : void 0
880
- };
894
+ `);
895
+ }
896
+ const d = [`## ${u || "Webpage"}`];
897
+ return a && d.push(`_${a}_`), m && d.push(`👤 ${m}`), d.push(`🔗 ${h.url}
898
+ `), d.push(i), D(d.join(`
899
+
900
+ `).replaceAll(/\n{3,}/g, `
901
+
902
+ `));
881
903
  }
882
- }, je = {
904
+ }, ve = {
883
905
  name: "web_search",
884
906
  description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
885
907
  args: {
@@ -888,32 +910,110 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
888
910
  },
889
911
  fn: async (h) => {
890
912
  const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
891
- headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
892
- }).then((o) => o.text());
913
+ headers: { "User-Agent": z, "Accept-Language": "en-US,en;q=0.9" }
914
+ }).then((n) => n.text());
893
915
  let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
894
- const a = new F();
916
+ const l = new H();
895
917
  for (; (e = t.exec(r)) !== null; ) {
896
- let o = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
897
- if (o && (o = decodeURIComponent(o)), o && a.add(o), a.size >= (h.length || 5)) break;
918
+ let n = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
919
+ if (n && (n = decodeURIComponent(n)), n && l.add(n), l.size >= (h.length || 5)) break;
898
920
  }
899
- return a;
921
+ return l;
900
922
  }
901
923
  };
924
+ class N {
925
+ async get(r) {
926
+ return (await fetch(r, { headers: { "User-Agent": z } })).json();
927
+ }
928
+ api(r) {
929
+ const e = new URLSearchParams({ ...r, format: "json", utf8: "1" }).toString();
930
+ return this.get(`https://en.wikipedia.org/w/api.php?${e}`);
931
+ }
932
+ clean(r) {
933
+ return r.replace(/\n{3,}/g, `
934
+
935
+ `).replace(/ {2,}/g, " ").replace(/\[\d+\]/g, "").trim();
936
+ }
937
+ truncate(r, e) {
938
+ if (r.length <= e) return r;
939
+ const t = r.slice(0, e), l = t.lastIndexOf(`
940
+
941
+ `);
942
+ return l > e * 0.7 ? t.slice(0, l) : t;
943
+ }
944
+ async searchTitles(r, e = 6) {
945
+ return (await this.api({ action: "query", list: "search", srsearch: r, srlimit: e, srprop: "snippet" })).query?.search || [];
946
+ }
947
+ async fetchExtract(r, e = !1) {
948
+ const t = { action: "query", prop: "extracts", titles: r, explaintext: 1, redirects: 1 };
949
+ e && (t.exintro = 1);
950
+ const l = await this.api(t), n = Object.values(l.query?.pages || {})[0];
951
+ return this.clean(n?.extract || "");
952
+ }
953
+ pageUrl(r) {
954
+ return `https://en.wikipedia.org/wiki/${encodeURIComponent(r.replace(/ /g, "_"))}`;
955
+ }
956
+ stripHtml(r) {
957
+ return r.replace(/<[^>]+>/g, "");
958
+ }
959
+ async lookup(r, e = "intro") {
960
+ const t = await this.searchTitles(r, 6);
961
+ if (!t.length) return `❌ No Wikipedia articles found for "${r}"`;
962
+ const l = t[0].title, n = this.pageUrl(l), s = await this.fetchExtract(l, e === "intro"), o = this.truncate(s, e === "intro" ? 2e3 : 8e3);
963
+ return `## ${l}
964
+ 🔗 ${n}
965
+
966
+ ${o}`;
967
+ }
968
+ async search(r) {
969
+ const e = await this.searchTitles(r, 8);
970
+ if (!e.length) return `❌ No results for "${r}"`;
971
+ const t = [`### Search results for "${r}"
972
+ `];
973
+ for (let l = 0; l < e.length; l++) {
974
+ const n = e[l], s = this.truncate(this.stripHtml(n.snippet || ""), 150);
975
+ t.push(`**${l + 1}. ${n.title}**
976
+ ${s}
977
+ ${this.pageUrl(n.title)}`);
978
+ }
979
+ return t.join(`
980
+
981
+ `);
982
+ }
983
+ }
984
+ const Ee = {
985
+ name: "wikipedia_lookup",
986
+ description: "Get Wikipedia article content",
987
+ args: {
988
+ query: { type: "string", description: "Topic or article title", required: !0 },
989
+ detail: { type: "string", description: 'Content level: "intro" (summary, default) or "full" (complete article)', enum: ["intro", "full"], default: "intro" }
990
+ },
991
+ fn: async (h) => new N().lookup(h.query, h.detail || "intro")
992
+ }, qe = {
993
+ name: "wikipedia_search",
994
+ description: "Search Wikipedia for matching articles",
995
+ args: {
996
+ query: { type: "string", description: "Search terms", required: !0 }
997
+ },
998
+ fn: async (h) => new N().search(h.query)
999
+ };
902
1000
  export {
903
- be as Ai,
904
- Q as Anthropic,
905
- ee as Audio,
906
- ne as CliTool,
907
- we as DateTimeTool,
908
- xe as DateTimeUTCTool,
909
- ke as ExecTool,
910
- Se as FetchTool,
911
- se as JSTool,
1001
+ ke as Ai,
1002
+ te as Anthropic,
1003
+ ne as Audio,
1004
+ ie as CliTool,
1005
+ Se as DateTimeTool,
1006
+ _e as DateTimeUTCTool,
1007
+ $e as ExecTool,
1008
+ Te as FetchTool,
1009
+ ae as JSTool,
912
1010
  L as LLMProvider,
913
1011
  P as OpenAi,
914
- oe as PythonTool,
915
- _e as ReadWebpageTool,
916
- te as Vision,
917
- je as WebSearchTool
1012
+ ce as PythonTool,
1013
+ je as ReadWebpageTool,
1014
+ se as Vision,
1015
+ ve as WebSearchTool,
1016
+ Ee as WikipediaLookupTool,
1017
+ qe as WikipediaSearchTool
918
1018
  };
919
1019
  //# sourceMappingURL=index.mjs.map