@ztimson/ai-utils 0.8.9 → 0.8.11

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
1
  import * as O from "node:os";
2
- import { tmpdir as L } from "node:os";
3
- import { objectMap as R, JSONAttemptParse as _, findByProp as U, JSONSanitize as S, clean as W, Http as N, consoleInterceptor as C, fn as J, ASet as F } from "@ztimson/utils";
4
- import { Anthropic as H } from "@anthropic-ai/sdk";
2
+ import { tmpdir as U } from "node:os";
3
+ import { objectMap as z, JSONAttemptParse as _, findByProp as R, JSONSanitize as S, clean as W, Http as C, consoleInterceptor as N, fn as J, ASet as F } from "@ztimson/utils";
4
+ import { Anthropic as B } from "@anthropic-ai/sdk";
5
5
  import { OpenAI as D } from "openai";
6
- import { fileURLToPath as G } from "url";
7
- import { join as I, dirname as B } from "path";
8
- import { spawn as b, execSync as K } from "node:child_process";
6
+ import { fileURLToPath as H } from "url";
7
+ import { join as I, dirname as G } from "path";
8
+ import { spawn as w, execSync as K } from "node:child_process";
9
9
  import { mkdtempSync as V } from "node:fs";
10
- import w from "node:fs/promises";
10
+ import b from "node:fs/promises";
11
11
  import * as M from "node:path";
12
- import P, { join as q } from "node:path";
12
+ import q, { join as A } from "node:path";
13
13
  import { createWorker as Y } from "tesseract.js";
14
14
  import * as Z from "cheerio";
15
15
  import { $Sync as j } from "@ztimson/node-utils";
16
- class z {
16
+ class L {
17
17
  }
18
- class Q extends z {
18
+ class Q extends L {
19
19
  constructor(r, e, t) {
20
- super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new H({ apiKey: e });
20
+ super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new B({ 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 i of r)
26
+ if (typeof i.content == "string")
27
+ t.push({ timestamp: e, ...i });
28
28
  else {
29
- const s = a.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
29
+ const o = i.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
30
30
 
31
31
  `);
32
- s && t.push({ timestamp: e, role: a.role, content: s }), a.content.forEach((n) => {
32
+ o && t.push({ timestamp: e, role: i.role, content: o }), i.content.forEach((n) => {
33
33
  if (n.type == "tool_use")
34
34
  t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
35
35
  else if (n.type == "tool_result") {
36
- const c = t.findLast((u) => u.id == n.tool_use_id);
37
- c && (c[n.is_error ? "error" : "content"] = n.content);
36
+ const u = t.findLast((l) => l.id == n.tool_use_id);
37
+ u && (u[n.is_error ? "error" : "content"] = n.content);
38
38
  }
39
39
  });
40
40
  }
@@ -55,9 +55,9 @@ class Q extends z {
55
55
  }
56
56
  ask(r, e = {}) {
57
57
  const t = new AbortController();
58
- return Object.assign(new Promise(async (a) => {
59
- let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
- const n = e.tools || this.ai.options.llm?.tools || [], c = {
58
+ return Object.assign(new Promise(async (i) => {
59
+ let o = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
+ const n = e.tools || this.ai.options.llm?.tools || [], u = {
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 || "",
@@ -67,66 +67,66 @@ class Q extends z {
67
67
  description: m.description,
68
68
  input_schema: {
69
69
  type: "object",
70
- properties: m.args ? R(m.args, (o, l) => ({ ...l, required: void 0 })) : {},
71
- required: m.args ? Object.entries(m.args).filter((o) => o[1].required).map((o) => o[0]) : []
70
+ properties: m.args ? z(m.args, (s, a) => ({ ...a, required: void 0 })) : {},
71
+ required: m.args ? Object.entries(m.args).filter((s) => s[1].required).map((s) => s[0]) : []
72
72
  },
73
73
  fn: void 0
74
74
  })),
75
- messages: s,
75
+ messages: o,
76
76
  stream: !!e.stream
77
77
  };
78
- let u, i = !0;
78
+ let l, c = !0;
79
79
  do {
80
- if (u = await this.client.messages.create(c).catch((o) => {
81
- throw o.message += `
80
+ if (l = await this.client.messages.create(u).catch((s) => {
81
+ throw s.message += `
82
82
 
83
83
  Messages:
84
- ${JSON.stringify(s, null, 2)}`, o;
84
+ ${JSON.stringify(o, null, 2)}`, s;
85
85
  }), e.stream) {
86
- i ? i = !1 : e.stream({ text: `
86
+ c ? c = !1 : e.stream({ text: `
87
87
 
88
- ` }), u.content = [];
89
- for await (const o of u) {
88
+ ` }), l.content = [];
89
+ for await (const s of l) {
90
90
  if (t.signal.aborted) break;
91
- if (o.type === "content_block_start")
92
- o.content_block.type === "text" ? u.content.push({ type: "text", text: "" }) : o.content_block.type === "tool_use" && u.content.push({ type: "tool_use", id: o.content_block.id, name: o.content_block.name, input: "" });
93
- else if (o.type === "content_block_delta")
94
- if (o.delta.type === "text_delta") {
95
- const l = o.delta.text;
96
- u.content.at(-1).text += l, e.stream({ text: l });
97
- } else o.delta.type === "input_json_delta" && (u.content.at(-1).input += o.delta.partial_json);
98
- else if (o.type === "content_block_stop") {
99
- const l = u.content.at(-1);
100
- l.input != null && (l.input = l.input ? _(l.input, {}) : {});
101
- } else if (o.type === "message_stop")
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 a = s.delta.text;
96
+ l.content.at(-1).text += a, e.stream({ text: a });
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 a = l.content.at(-1);
100
+ a.input != null && (a.input = a.input ? _(a.input, {}) : {});
101
+ } else if (s.type === "message_stop")
102
102
  break;
103
103
  }
104
104
  }
105
- const m = u.content.filter((o) => o.type === "tool_use");
105
+ const m = l.content.filter((s) => s.type === "tool_use");
106
106
  if (m.length && !t.signal.aborted) {
107
- s.push({ role: "assistant", content: u.content });
108
- const o = await Promise.all(m.map(async (l) => {
109
- const d = n.find(U("name", l.name));
110
- if (e.stream && e.stream({ tool: l.name }), !d) return { tool_use_id: l.id, is_error: !0, content: "Tool not found" };
107
+ o.push({ role: "assistant", content: l.content });
108
+ const s = await Promise.all(m.map(async (a) => {
109
+ const d = n.find(R("name", a.name));
110
+ if (e.stream && e.stream({ tool: a.name }), !d) return { tool_use_id: a.id, is_error: !0, content: "Tool not found" };
111
111
  try {
112
- const p = await d.fn(l.input, e?.stream, this.ai);
113
- return { type: "tool_result", tool_use_id: l.id, content: S(p) };
112
+ const p = await d.fn(a.input, e?.stream, this.ai);
113
+ return { type: "tool_result", tool_use_id: a.id, content: S(p) };
114
114
  } catch (p) {
115
- return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
115
+ return { type: "tool_result", tool_use_id: a.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
116
116
  }
117
117
  }));
118
- s.push({ role: "user", content: o }), c.messages = s;
118
+ o.push({ role: "user", content: s }), u.messages = o;
119
119
  }
120
- } while (!t.signal.aborted && u.content.some((m) => m.type === "tool_use"));
121
- s.push({ role: "assistant", content: u.content.filter((m) => m.type == "text").map((m) => m.text).join(`
120
+ } while (!t.signal.aborted && l.content.some((m) => m.type === "tool_use"));
121
+ o.push({ role: "assistant", content: l.content.filter((m) => m.type == "text").map((m) => m.text).join(`
122
122
 
123
- `) }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), a(s.at(-1)?.content);
123
+ `) }), o = this.toStandard(o), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...o), i(o.at(-1)?.content);
124
124
  }), { abort: () => t.abort() });
125
125
  }
126
126
  }
127
- class A extends z {
128
- constructor(r, e, t, a) {
129
- super(), this.ai = r, this.host = e, this.token = t, this.model = a, this.client = new D(W({
127
+ class P extends L {
128
+ constructor(r, e, t, i) {
129
+ super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new D(W({
130
130
  baseURL: e,
131
131
  apiKey: t || e ? "ignored" : void 0
132
132
  }));
@@ -136,17 +136,17 @@ class A extends z {
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((s) => ({
139
+ const i = t.tool_calls.map((o) => ({
140
140
  role: "tool",
141
- id: s.id,
142
- name: s.function.name,
143
- args: _(s.function.arguments, {}),
141
+ id: o.id,
142
+ name: o.function.name,
143
+ args: _(o.function.arguments, {}),
144
144
  timestamp: t.timestamp
145
145
  }));
146
- r.splice(e, 1, ...a), e += a.length - 1;
146
+ r.splice(e, 1, ...i), e += i.length - 1;
147
147
  } else if (t.role === "tool" && t.content) {
148
- const a = r.find((s) => t.tool_call_id == s.id);
149
- a && (t.content.includes('"error":') ? a.error = t.content : a.content = t.content), r.splice(e, 1), e--;
148
+ const i = r.find((o) => t.tool_call_id == o.id);
149
+ i && (t.content.includes('"error":') ? i.error = t.content : i.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 A extends z {
167
167
  content: t.error || t.content
168
168
  });
169
169
  else {
170
- const { timestamp: a, ...s } = t;
171
- e.push(s);
170
+ const { timestamp: i, ...o } = t;
171
+ e.push(o);
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, s) => {
178
+ return Object.assign(new Promise(async (i, o) => {
179
179
  e.system && (e.history?.[0]?.role != "system" ? e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() }) : e.history[0].content = e.system);
180
180
  let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
181
- const c = e.tools || this.ai.options.llm?.tools || [], u = {
181
+ const u = e.tools || this.ai.options.llm?.tools || [], l = {
182
182
  model: e.model || this.model,
183
183
  messages: n,
184
184
  stream: !!e.stream,
185
185
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
186
186
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
187
- tools: c.map((o) => ({
187
+ tools: u.map((s) => ({
188
188
  type: "function",
189
189
  function: {
190
- name: o.name,
191
- description: o.description,
190
+ name: s.name,
191
+ description: s.description,
192
192
  parameters: {
193
193
  type: "object",
194
- properties: o.args ? R(o.args, (l, d) => ({ ...d, required: void 0 })) : {},
195
- required: o.args ? Object.entries(o.args).filter((l) => l[1].required).map((l) => l[0]) : []
194
+ properties: s.args ? z(s.args, (a, d) => ({ ...d, required: void 0 })) : {},
195
+ required: s.args ? Object.entries(s.args).filter((a) => a[1].required).map((a) => a[0]) : []
196
196
  }
197
197
  }
198
198
  }))
199
199
  };
200
- let i, m = !0;
200
+ let c, m = !0;
201
201
  do {
202
- if (i = await this.client.chat.completions.create(u).catch((l) => {
203
- throw l.message += `
202
+ if (c = await this.client.chat.completions.create(l).catch((a) => {
203
+ throw a.message += `
204
204
 
205
205
  Messages:
206
- ${JSON.stringify(n, null, 2)}`, l;
206
+ ${JSON.stringify(n, null, 2)}`, a;
207
207
  }), e.stream) {
208
208
  m ? m = !1 : e.stream({ text: `
209
209
 
210
- ` }), i.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
- for await (const l of i) {
210
+ ` }), c.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
+ for await (const a of c) {
212
212
  if (t.signal.aborted) break;
213
- if (l.choices[0].delta.content && (i.choices[0].message.content += l.choices[0].delta.content, e.stream({ text: l.choices[0].delta.content })), l.choices[0].delta.tool_calls)
214
- for (const d of l.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 (a.choices[0].delta.content && (c.choices[0].message.content += a.choices[0].delta.content, e.stream({ text: a.choices[0].delta.content })), a.choices[0].delta.tool_calls)
214
+ for (const d of a.choices[0].delta.tool_calls) {
215
+ const p = c.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))) : c.choices[0].message.tool_calls.push({
217
217
  index: d.index,
218
218
  id: d.id || "",
219
219
  type: d.type || "function",
@@ -225,11 +225,11 @@ ${JSON.stringify(n, null, 2)}`, l;
225
225
  }
226
226
  }
227
227
  }
228
- const o = i.choices[0].message.tool_calls || [];
229
- if (o.length && !t.signal.aborted) {
230
- n.push(i.choices[0].message);
231
- const l = await Promise.all(o.map(async (d) => {
232
- const p = c?.find(U("name", d.function.name));
228
+ const s = c.choices[0].message.tool_calls || [];
229
+ if (s.length && !t.signal.aborted) {
230
+ n.push(c.choices[0].message);
231
+ const a = await Promise.all(s.map(async (d) => {
232
+ const p = u?.find(R("name", d.function.name));
233
233
  if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
234
234
  try {
235
235
  const f = _(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
@@ -238,17 +238,17 @@ ${JSON.stringify(n, null, 2)}`, l;
238
238
  return { role: "tool", tool_call_id: d.id, content: S({ error: f?.message || f?.toString() || "Unknown" }) };
239
239
  }
240
240
  }));
241
- n.push(...l), u.messages = n;
241
+ n.push(...a), l.messages = n;
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 && c.choices?.[0]?.message?.tool_calls?.length);
244
+ n.push({ role: "assistant", content: c.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), i(n.at(-1)?.content);
245
245
  }), { abort: () => t.abort() });
246
246
  }
247
247
  }
248
248
  class X {
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 A(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new A(this.ai, t.host || null, t.token, e));
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));
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 i = () => {
274
274
  };
275
- return Object.assign(new Promise(async (s) => {
275
+ return Object.assign(new Promise(async (o) => {
276
276
  if (e.history || (e.history = []), e.memory) {
277
- const c = async (i, m, o = 10) => {
278
- const [l, d] = await Promise.all([
277
+ const u = async (c, m, s = 10) => {
278
+ const [a, d] = await Promise.all([
279
279
  m ? this.embedding(m) : Promise.resolve(null),
280
- i ? this.embedding(i) : Promise.resolve(null)
280
+ c ? this.embedding(c) : Promise.resolve(null)
281
281
  ]);
282
282
  return (e.memory || []).map((p) => {
283
- const f = (l ? this.cosineSimilarity(p.embeddings[0], l[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
283
+ const f = (a ? this.cosineSimilarity(p.embeddings[0], a[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
284
284
  return { ...p, score: f };
285
- }).toSorted((p, f) => p.score - f.score).slice(0, o).map((p) => `- ${p.owner}: ${p.fact}`).join(`
285
+ }).toSorted((p, f) => p.score - f.score).slice(0, s).map((p) => `- ${p.owner}: ${p.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 u = await c(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 = [{
291
+ const l = await u(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 = [{
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 @@ ${u}` }), 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 c(i.query, i.subject, i.topK);
301
+ fn: (c) => {
302
+ if (!c.subject && !c.query) throw new Error("Either a subject or query argument is required");
303
+ return u(c.query, c.subject, c.topK);
304
304
  }
305
305
  }, {
306
306
  name: "remember",
@@ -309,31 +309,31 @@ ${u}` }), 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 (c) => {
313
313
  if (!e.memory) return;
314
314
  const m = await Promise.all([
315
- this.embedding(i.owner),
316
- this.embedding(`${i.owner}: ${i.fact}`)
317
- ]), o = { owner: i.owner, fact: i.fact, embeddings: [m[0][0].embedding, m[1][0].embedding] };
318
- return e.memory.splice(0, e.memory.length, ...e.memory.filter((l) => !(this.cosineSimilarity(o.embeddings[0], l.embeddings[0]) >= 0.9 && this.cosineSimilarity(o.embeddings[1], l.embeddings[1]) >= 0.8)), o), "Remembered!";
315
+ this.embedding(c.owner),
316
+ this.embedding(`${c.owner}: ${c.fact}`)
317
+ ]), s = { owner: c.owner, fact: c.fact, embeddings: [m[0][0].embedding, m[1][0].embedding] };
318
+ return e.memory.splice(0, e.memory.length, ...e.memory.filter((a) => !(this.cosineSimilarity(s.embeddings[0], a.embeddings[0]) >= 0.9 && this.cosineSimilarity(s.embeddings[1], a.embeddings[1]) >= 0.8)), s), "Remembered!";
319
319
  }
320
320
  }, ...e.tools || []];
321
321
  }
322
322
  const n = await this.models[t].ask(r, e);
323
- if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((c) => c.role != "tool" || c.name != "recall" && c.name != "remember")), e.compress) {
324
- const c = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
325
- e.history.splice(0, e.history.length, ...c);
323
+ if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((u) => u.role != "tool" || u.name != "recall" && u.name != "remember")), e.compress) {
324
+ const u = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
325
+ e.history.splice(0, e.history.length, ...u);
326
326
  }
327
- return s(n);
328
- }), { abort: a });
327
+ return o(n);
328
+ }), { abort: i });
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((s) => !!s).join(`
335
- `) }), a = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
336
- return a ? a[1].trim() : null;
334
+ ].filter((o) => !!o).join(`
335
+ `) }), i = /```(?:.+)?\s*([\s\S]*?)```/.exec(t);
336
+ return i ? i[1].trim() : null;
337
337
  }
338
338
  /**
339
339
  * Compress chat history to reduce context size
@@ -343,17 +343,17 @@ ${u}` }), 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, i) {
347
347
  if (this.estimateTokens(r) < e) return r;
348
- let s = 0, n = 0;
348
+ let o = 0, n = 0;
349
349
  for (let d of r.toReversed())
350
- if (n += this.estimateTokens(d.content), n < t) s++;
350
+ if (n += this.estimateTokens(d.content), n < t) o++;
351
351
  else break;
352
- if (r.length <= s) return r;
353
- const c = r[0].role == "system" ? r[0] : null, u = s == 0 ? [] : r.slice(-s), i = (s == 0 ? r : r.slice(0, -s)).filter((d) => d.role === "assistant" || d.role === "user"), m = await this.summarize(i.map((d) => `[${d.role}]: ${d.content}`).join(`
352
+ if (r.length <= o) return r;
353
+ const u = r[0].role == "system" ? r[0] : null, l = o == 0 ? [] : r.slice(-o), c = (o == 0 ? r : r.slice(0, -o)).filter((d) => d.role === "assistant" || d.role === "user"), m = await this.summarize(c.map((d) => `[${d.role}]: ${d.content}`).join(`
354
354
 
355
- `), 500, a), o = Date.now(), l = [{ role: "tool", name: "summary", id: "summary_" + o, args: {}, content: `Conversation Summary: ${m?.summary}`, timestamp: o }, ...u];
356
- return c && l.splice(0, 0, c), l;
355
+ `), 500, i), s = Date.now(), a = [{ role: "tool", name: "summary", id: "summary_" + s, args: {}, content: `Conversation Summary: ${m?.summary}`, timestamp: s }, ...l];
356
+ return u && a.splice(0, 0, u), a;
357
357
  }
358
358
  /**
359
359
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -363,10 +363,10 @@ ${u}` }), 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, s = 0;
367
- for (let c = 0; c < r.length; c++)
368
- t += r[c] * e[c], a += r[c] * r[c], s += e[c] * e[c];
369
- const n = Math.sqrt(a) * Math.sqrt(s);
366
+ let t = 0, i = 0, o = 0;
367
+ for (let u = 0; u < r.length; u++)
368
+ t += r[u] * e[u], i += r[u] * r[u], o += e[u] * e[u];
369
+ const n = Math.sqrt(i) * Math.sqrt(o);
370
370
  return n === 0 ? 0 : t / n;
371
371
  }
372
372
  /**
@@ -377,25 +377,25 @@ ${u}` }), e.tools = [{
377
377
  * @returns {string[]} Chunked strings
378
378
  */
379
379
  chunk(r, e = 500, t = 50) {
380
- const a = (u, i = "") => u ? Object.entries(u).flatMap(([m, o]) => {
381
- const l = i ? `${i}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
382
- return typeof o == "object" && !Array.isArray(o) ? a(o, l) : `${l}: ${Array.isArray(o) ? o.join(", ") : o}`;
383
- }) : [], n = (typeof r == "object" ? a(r) : r.toString().split(`
384
- `)).flatMap((u) => [...u.split(/\s+/).filter(Boolean), `
385
- `]), c = [];
386
- for (let u = 0; u < n.length; ) {
387
- let i = "", m = u;
380
+ const i = (l, c = "") => l ? Object.entries(l).flatMap(([m, s]) => {
381
+ const a = c ? `${c}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
382
+ return typeof s == "object" && !Array.isArray(s) ? i(s, a) : `${a}: ${Array.isArray(s) ? s.join(", ") : s}`;
383
+ }) : [], n = (typeof r == "object" ? i(r) : r.toString().split(`
384
+ `)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
385
+ `]), u = [];
386
+ for (let l = 0; l < n.length; ) {
387
+ let c = "", m = l;
388
388
  for (; m < n.length; ) {
389
- const l = i + (i ? " " : "") + n[m];
390
- if (this.estimateTokens(l.replace(/\s*\n\s*/g, `
391
- `)) > e && i) break;
392
- i = l, m++;
389
+ const a = c + (c ? " " : "") + n[m];
390
+ if (this.estimateTokens(a.replace(/\s*\n\s*/g, `
391
+ `)) > e && c) break;
392
+ c = a, m++;
393
393
  }
394
- const o = i.replace(/\s*\n\s*/g, `
394
+ const s = c.replace(/\s*\n\s*/g, `
395
395
  `).trim();
396
- o && c.push(o), u = Math.max(m - t, m === u ? u + 1 : m);
396
+ s && u.push(s), l = Math.max(m - t, m === l ? l + 1 : m);
397
397
  }
398
- return c;
398
+ return u;
399
399
  }
400
400
  /**
401
401
  * Create a vector representation of a string
@@ -404,39 +404,39 @@ ${u}` }), 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, s = !1;
407
+ let { maxTokens: t = 500, overlapTokens: i = 50 } = e, o = !1;
408
408
  const n = () => {
409
- s = !0;
410
- }, c = (i) => new Promise((m, o) => {
411
- if (s) return o(new Error("Aborted"));
412
- const l = [
413
- I(B(G(import.meta.url)), "embedder.js"),
409
+ o = !0;
410
+ }, u = (c) => new Promise((m, s) => {
411
+ if (o) return s(new Error("Aborted"));
412
+ const a = [
413
+ I(G(H(import.meta.url)), "embedder.js"),
414
414
  this.ai.options.path,
415
415
  this.ai.options?.embedder || "bge-small-en-v1.5"
416
- ], d = b("node", l, { stdio: ["pipe", "pipe", "ignore"] });
417
- d.stdin.write(i), d.stdin.end();
416
+ ], d = w("node", a, { stdio: ["pipe", "pipe", "ignore"] });
417
+ d.stdin.write(c), d.stdin.end();
418
418
  let p = "";
419
419
  d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
420
- if (s) return o(new Error("Aborted"));
420
+ if (o) return s(new Error("Aborted"));
421
421
  if (f === 0)
422
422
  try {
423
423
  const g = JSON.parse(p);
424
424
  m(g.embedding);
425
425
  } catch {
426
- o(new Error("Failed to parse embedding output"));
426
+ s(new Error("Failed to parse embedding output"));
427
427
  }
428
428
  else
429
- o(new Error(`Embedder process exited with code ${f}`));
430
- }), d.on("error", o);
431
- }), u = (async () => {
432
- const i = this.chunk(r, t, a), m = [];
433
- for (let o = 0; o < i.length && !s; o++) {
434
- const l = i[o], d = await c(l);
435
- m.push({ index: o, embedding: d, text: l, tokens: this.estimateTokens(l) });
429
+ s(new Error(`Embedder process exited with code ${f}`));
430
+ }), d.on("error", s);
431
+ }), l = (async () => {
432
+ const c = this.chunk(r, t, i), m = [];
433
+ for (let s = 0; s < c.length && !o; s++) {
434
+ const a = c[s], d = await u(a);
435
+ m.push({ index: s, embedding: d, text: a, tokens: this.estimateTokens(a) });
436
436
  }
437
437
  return m;
438
438
  })();
439
- return Object.assign(u, { abort: n });
439
+ return Object.assign(l, { abort: n });
440
440
  }
441
441
  /**
442
442
  * Estimate variable as tokens
@@ -455,8 +455,8 @@ ${u}` }), 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, c = 10) => n.toLowerCase().split("").map((u, i) => u.charCodeAt(0) * (i + 1) % c / c).slice(0, c), a = t(r), s = e.map((n) => t(n)).map((n) => this.cosineSimilarity(a, n));
459
- return { avg: s.reduce((n, c) => n + c, 0) / s.length, max: Math.max(...s), similarities: s };
458
+ const t = (n, u = 10) => n.toLowerCase().split("").map((l, c) => l.charCodeAt(0) * (c + 1) % u / u).slice(0, u), i = t(r), o = e.map((n) => t(n)).map((n) => this.cosineSimilarity(i, n));
459
+ return { avg: o.reduce((n, u) => n + u, 0) / o.length, max: Math.max(...o), similarities: o };
460
460
  }
461
461
  /**
462
462
  * Ask a question with JSON response
@@ -466,15 +466,15 @@ ${u}` }), e.tools = [{
466
466
  * @returns {Promise<{} | {} | RegExpExecArray | null>}
467
467
  */
468
468
  async json(r, e, t) {
469
- const a = await this.code(r, { ...t, system: [
469
+ const i = await this.code(r, { ...t, system: [
470
470
  t?.system,
471
471
  `Only respond using JSON matching this schema:
472
472
  \`\`\`json
473
473
  ${e}
474
474
  \`\`\``
475
- ].filter((s) => !!s).join(`
475
+ ].filter((o) => !!o).join(`
476
476
  `) });
477
- return a ? _(a, {}) : null;
477
+ return i ? _(i, {}) : null;
478
478
  }
479
479
  /**
480
480
  * Create a summary of some text
@@ -510,21 +510,21 @@ print(json.dumps(segments))
510
510
  pyannote;
511
511
  whisperModel;
512
512
  async addPunctuation(r, e, t = 150) {
513
- const a = (n) => {
513
+ const i = (n) => {
514
514
  if (n = n.toLowerCase().replace(/[^a-z]/g, ""), n.length <= 3) return 1;
515
- const c = n.match(/[aeiouy]+/g);
516
- let u = c ? c.length : 1;
517
- return n.endsWith("e") && u--, Math.max(1, u);
515
+ const u = n.match(/[aeiouy]+/g);
516
+ let l = u ? u.length : 1;
517
+ return n.endsWith("e") && l--, Math.max(1, l);
518
518
  };
519
- let s = "";
520
- return r.transcription.filter((n, c) => {
521
- let u = !1;
522
- const i = r.transcription[c - 1], m = r.transcription[c + 1];
523
- return !n.text && m ? (m.offsets.from = n.offsets.from, m.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, u = !0), !!n.text && !u;
519
+ let o = "";
520
+ return r.transcription.filter((n, u) => {
521
+ let l = !1;
522
+ const c = r.transcription[u - 1], m = r.transcription[u + 1];
523
+ return !n.text && m ? (m.offsets.from = n.offsets.from, m.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && c && (c.offsets.to = n.offsets.to, c.timestamps.to = n.timestamps.to, c.text += n.text, l = !0), !!n.text && !l;
524
524
  }).forEach((n) => {
525
- const c = /^[A-Z]/.test(n.text.trim()), u = n.offsets.to - n.offsets.from, m = a(n.text.trim()) * t;
526
- c && u > m * 2 && n.text[0] == " " && (s += "."), s += n.text;
527
- }), e ? this.ai.language.ask(s, {
525
+ const u = /^[A-Z]/.test(n.text.trim()), l = n.offsets.to - n.offsets.from, m = i(n.text.trim()) * t;
526
+ u && l > m * 2 && n.text[0] == " " && (o += "."), o += n.text;
527
+ }), e ? this.ai.language.ask(o, {
528
528
  system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
529
529
  temperature: 0.1,
530
530
  tools: [{
@@ -534,138 +534,138 @@ print(json.dumps(segments))
534
534
  find: { type: "string", description: "Text to find", required: !0 },
535
535
  replace: { type: "string", description: "Text to replace", required: !0 }
536
536
  },
537
- fn: (n) => s = s.replace(n.find, n.replace)
537
+ fn: (n) => o = o.replace(n.find, n.replace)
538
538
  }]
539
- }).then(() => s) : s.trim();
539
+ }).then(() => o) : o.trim();
540
540
  }
541
541
  async diarizeTranscript(r, e, t) {
542
- const a = /* @__PURE__ */ new Map();
543
- let s = 0;
542
+ const i = /* @__PURE__ */ new Map();
543
+ let o = 0;
544
544
  e.forEach((p) => {
545
- a.has(p.speaker) || a.set(p.speaker, ++s);
545
+ i.has(p.speaker) || i.set(p.speaker, ++o);
546
546
  });
547
- const n = await this.addPunctuation(r, t), c = n.match(/[^.!?]+[.!?]+/g) || [n], u = r.transcription.filter((p) => p.text.trim()), i = c.map((p) => {
547
+ const n = await this.addPunctuation(r, t), u = n.match(/[^.!?]+[.!?]+/g) || [n], l = r.transcription.filter((p) => p.text.trim()), c = u.map((p) => {
548
548
  if (p = p.trim(), !p) return null;
549
549
  const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
550
- f.forEach((k) => {
551
- const x = u.find((y) => k === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
552
- if (!x) return;
553
- const $ = x.offsets.from / 1e3, v = e.find((y) => $ >= y.start && $ <= y.end);
550
+ f.forEach((x) => {
551
+ const k = l.find((y) => x === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
552
+ if (!k) return;
553
+ const $ = k.offsets.from / 1e3, v = e.find((y) => $ >= y.start && $ <= y.end);
554
554
  if (v) {
555
- const y = a.get(v.speaker);
555
+ const y = i.get(v.speaker);
556
556
  g.set(y, (g.get(y) || 0) + 1);
557
557
  }
558
558
  });
559
559
  let T = 1, E = 0;
560
- return g.forEach((k, x) => {
561
- k > E && (E = k, T = x);
560
+ return g.forEach((x, k) => {
561
+ x > E && (E = x, T = k);
562
562
  }), { speaker: T, text: p };
563
563
  }).filter((p) => p !== null), m = [];
564
- i.forEach((p) => {
564
+ c.forEach((p) => {
565
565
  const f = m[m.length - 1];
566
566
  f && f.speaker === p.speaker ? f.text += " " + p.text : m.push({ ...p });
567
567
  });
568
- let o = m.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
568
+ let s = m.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
569
569
  `).trim();
570
- if (!t) return o;
571
- let l = this.ai.language.chunk(o, 500, 0);
572
- l.length > 4 && (l = [...l.slice(0, 3), l.at(-1)]);
573
- const d = await this.ai.language.json(l.join(`
570
+ if (!t) return s;
571
+ let a = this.ai.language.chunk(s, 500, 0);
572
+ a.length > 4 && (a = [...a.slice(0, 3), a.at(-1)]);
573
+ const d = await this.ai.language.json(a.join(`
574
574
  `), '{1: "Detected Name", 2: "Second Name"}', {
575
575
  system: "Use the following transcript to identify speakers. Only identify speakers you are positive about, dont mention speakers you are unsure about in your response",
576
576
  temperature: 0.1
577
577
  });
578
- return Object.entries(d).forEach(([p, f]) => o = o.replaceAll(`[Speaker ${p}]`, `[${f}]`)), o;
578
+ return Object.entries(d).forEach(([p, f]) => s = s.replaceAll(`[Speaker ${p}]`, `[${f}]`)), s;
579
579
  }
580
580
  runAsr(r, e = {}) {
581
581
  let t;
582
- const a = new Promise((s, n) => {
583
- this.downloadAsrModel(e.model).then((c) => {
582
+ const i = new Promise((o, n) => {
583
+ this.downloadAsrModel(e.model).then((u) => {
584
584
  if (e.diarization) {
585
- let u = M.join(M.dirname(r), "transcript");
586
- t = b(
585
+ let l = M.join(M.dirname(r), "transcript");
586
+ t = w(
587
587
  this.ai.options.whisper,
588
- ["-m", c, "-f", r, "-np", "-ml", "1", "-oj", "-of", u],
588
+ ["-m", u, "-f", r, "-np", "-ml", "1", "-oj", "-of", l],
589
589
  { stdio: ["ignore", "ignore", "pipe"] }
590
- ), t.on("error", (i) => n(i)), t.on("close", async (i) => {
591
- if (i === 0) {
592
- u = await w.readFile(u + ".json", "utf-8"), w.rm(u + ".json").catch(() => {
590
+ ), t.on("error", (c) => n(c)), t.on("close", async (c) => {
591
+ if (c === 0) {
592
+ l = await b.readFile(l + ".json", "utf-8"), b.rm(l + ".json").catch(() => {
593
593
  });
594
594
  try {
595
- s(JSON.parse(u));
595
+ o(JSON.parse(l));
596
596
  } catch {
597
597
  n(new Error("Failed to parse whisper JSON"));
598
598
  }
599
599
  } else
600
- n(new Error(`Exit code ${i}`));
600
+ n(new Error(`Exit code ${c}`));
601
601
  });
602
602
  } else {
603
- let u = "";
604
- t = b(this.ai.options.whisper, ["-m", c, "-f", r, "-np", "-nt"]), t.on("error", (i) => n(i)), t.stdout.on("data", (i) => u += i.toString()), t.on("close", async (i) => {
605
- i === 0 ? s(u.trim() || null) : n(new Error(`Exit code ${i}`));
603
+ let l = "";
604
+ t = w(this.ai.options.whisper, ["-m", u, "-f", r, "-np", "-nt"]), t.on("error", (c) => n(c)), t.stdout.on("data", (c) => l += c.toString()), t.on("close", async (c) => {
605
+ c === 0 ? o(l.trim() || null) : n(new Error(`Exit code ${c}`));
606
606
  });
607
607
  }
608
608
  });
609
609
  });
610
- return Object.assign(a, { abort: () => t?.kill("SIGTERM") });
610
+ return Object.assign(i, { abort: () => t?.kill("SIGTERM") });
611
611
  }
612
612
  runDiarization(r) {
613
613
  let e = !1, t = () => {
614
614
  e = !0;
615
615
  };
616
- const a = (n) => new Promise((c) => {
617
- const u = b(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
618
- u.on("close", (i) => c(i === 0)), u.on("error", () => c(!1));
619
- }), s = Promise.all([
620
- a("python"),
621
- a("python3")
622
- ]).then((async ([n, c]) => {
616
+ const i = (n) => new Promise((u) => {
617
+ const l = w(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
618
+ l.on("close", (c) => u(c === 0)), l.on("error", () => u(!1));
619
+ }), o = Promise.all([
620
+ i("python"),
621
+ i("python3")
622
+ ]).then((async ([n, u]) => {
623
623
  if (e) return;
624
- if (!n && !c) throw new Error("Pyannote is not installed: pip install pyannote.audio");
625
- const u = c ? "python3" : "python";
626
- return new Promise((i, m) => {
624
+ if (!n && !u) throw new Error("Pyannote is not installed: pip install pyannote.audio");
625
+ const l = u ? "python3" : "python";
626
+ return new Promise((c, m) => {
627
627
  if (e) return;
628
- let o = "";
629
- const l = b(u, ["-W", "ignore", "-c", this.pyannote, r]);
630
- l.stdout.on("data", (d) => o += d.toString()), l.stderr.on("data", (d) => console.error(d.toString())), l.on("close", (d) => {
628
+ let s = "";
629
+ const a = w(l, ["-W", "ignore", "-c", this.pyannote, r]);
630
+ a.stdout.on("data", (d) => s += d.toString()), a.stderr.on("data", (d) => console.error(d.toString())), a.on("close", (d) => {
631
631
  if (d === 0)
632
632
  try {
633
- i(JSON.parse(o));
633
+ c(JSON.parse(s));
634
634
  } catch {
635
635
  m(new Error("Failed to parse diarization output"));
636
636
  }
637
637
  else
638
638
  m(new Error(`Python process exited with code ${d}`));
639
- }), l.on("error", m), t = () => l.kill("SIGTERM");
639
+ }), a.on("error", m), t = () => a.kill("SIGTERM");
640
640
  });
641
641
  }));
642
- return Object.assign(s, { abort: t });
642
+ return Object.assign(o, { abort: t });
643
643
  }
644
644
  asr(r, e = {}) {
645
645
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
646
- const t = q(V(q(L(), "audio-")), "converted.wav");
646
+ const t = A(V(A(U(), "audio-")), "converted.wav");
647
647
  K(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
648
- const a = () => w.rm(P.dirname(t), { recursive: !0, force: !0 }).catch(() => {
648
+ const i = () => b.rm(q.dirname(t), { recursive: !0, force: !0 }).catch(() => {
649
649
  });
650
650
  if (!e.diarization) return this.runAsr(t, { model: e.model });
651
- const s = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
652
- let c = !1, u = () => {
653
- c = !0, s.abort(), n.abort(), a();
651
+ const o = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
652
+ let u = !1, l = () => {
653
+ u = !0, o.abort(), n.abort(), i();
654
654
  };
655
- const i = Promise.allSettled([s, n]).then(async ([m, o]) => {
655
+ const c = Promise.allSettled([o, n]).then(async ([m, s]) => {
656
656
  if (m.status == "rejected") throw new Error(`Whisper.cpp timestamps:
657
657
  ` + m.reason);
658
- if (o.status == "rejected") throw new Error(`Pyannote:
659
- ` + o.reason);
660
- return c || !e.diarization ? m.value : this.diarizeTranscript(m.value, o.value, e.diarization == "llm");
661
- }).finally(() => a());
662
- return Object.assign(i, { abort: u });
658
+ if (s.status == "rejected") throw new Error(`Pyannote:
659
+ ` + s.reason);
660
+ return u || !e.diarization ? m.value : this.diarizeTranscript(m.value, s.value, e.diarization == "llm");
661
+ }).finally(() => i());
662
+ return Object.assign(c, { abort: l });
663
663
  }
664
664
  async downloadAsrModel(r = this.whisperModel) {
665
665
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
666
666
  r.endsWith(".bin") || (r += ".bin");
667
- const e = P.join(this.ai.options.path, r);
668
- return await w.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 w.writeFile(e, t), delete this.downloads[r], e)), this.downloads[r]);
667
+ const e = q.join(this.ai.options.path, r);
668
+ 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]);
669
669
  }
670
670
  }
671
671
  class te {
@@ -679,15 +679,15 @@ class te {
679
679
  */
680
680
  ocr(r) {
681
681
  let e;
682
- const t = new Promise(async (a) => {
682
+ const t = new Promise(async (i) => {
683
683
  e = await Y(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
684
- const { data: s } = await e.recognize(r);
685
- await e.terminate(), a(s.text.trim() || null);
684
+ const { data: o } = await e.recognize(r);
685
+ await e.terminate(), i(o.text.trim() || null);
686
686
  });
687
687
  return Object.assign(t, { abort: () => e?.terminate() });
688
688
  }
689
689
  }
690
- class we {
690
+ class be {
691
691
  constructor(r) {
692
692
  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);
693
693
  }
@@ -703,8 +703,13 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
703
703
  description: "Use the command line interface, returns any output",
704
704
  args: { command: { type: "string", description: "Command to run", required: !0 } },
705
705
  fn: (h) => j`${h.command}`
706
- }, be = {
706
+ }, we = {
707
707
  name: "get_datetime",
708
+ description: "Get local date / time",
709
+ args: {},
710
+ fn: async () => (/* @__PURE__ */ new Date()).toString()
711
+ }, xe = {
712
+ name: "get_datetime_utc",
708
713
  description: "Get current UTC date / time",
709
714
  args: {},
710
715
  fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
@@ -731,7 +736,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
731
736
  return { error: t?.message || t.toString() };
732
737
  }
733
738
  }
734
- }, xe = {
739
+ }, _e = {
735
740
  name: "fetch",
736
741
  description: "Make HTTP request to URL",
737
742
  args: {
@@ -740,7 +745,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
740
745
  headers: { type: "object", description: "HTTP headers to send", default: {} },
741
746
  body: { type: "object", description: "HTTP body to send" }
742
747
  },
743
- fn: (h) => new N({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
748
+ fn: (h) => new C({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
744
749
  }, se = {
745
750
  name: "exec_javascript",
746
751
  description: "Execute commonjs javascript",
@@ -748,7 +753,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
748
753
  code: { type: "string", description: "CommonJS javascript", required: !0 }
749
754
  },
750
755
  fn: async (h) => {
751
- const r = C(null), e = await J({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
756
+ const r = N(null), e = await J({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
752
757
  return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
753
758
  }
754
759
  }, oe = {
@@ -758,34 +763,82 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
758
763
  code: { type: "string", description: "CommonJS javascript", required: !0 }
759
764
  },
760
765
  fn: async (h) => ({ result: j`python -c "${h.code}"` })
761
- }, _e = {
766
+ }, Se = {
762
767
  name: "read_webpage",
763
- description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
768
+ description: "Extract clean, structured content from a webpage or convert media/documents to accessible formats",
764
769
  args: {
765
770
  url: { type: "string", description: "URL to extract content from", required: !0 },
766
- focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
771
+ mimeRegex: { type: "string", description: 'Optional: Regex pattern to filter MIME types (e.g., "^image/", "text/", "application/pdf")' },
772
+ maxSize: { type: "number", description: "Optional: Max file size in bytes for binary content (default: 10MB)" }
767
773
  },
768
774
  fn: async (h) => {
769
- const r = await fetch(h.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((n) => n.text()).catch((n) => {
770
- throw new Error(`Failed to fetch: ${n.message}`);
771
- }), e = Z.load(r);
772
- e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
773
- const t = {
774
- title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
775
- description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
775
+ const e = await fetch(h.url, {
776
+ headers: {
777
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
778
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
779
+ "Accept-Language": "en-US,en;q=0.5"
780
+ },
781
+ redirect: "follow"
782
+ }).catch((s) => {
783
+ throw new Error(`Failed to fetch: ${s.message}`);
784
+ }), t = e.headers.get("content-type") || "", i = t.split(";")[0].trim().toLowerCase();
785
+ if (t.match(/charset=([^;]+)/)?.[1], h.mimeRegex && !new RegExp(h.mimeRegex, "i").test(i))
786
+ return { url: h.url, error: "MIME type rejected", mimeType: i, filter: h.mimeRegex };
787
+ if (i.startsWith("image/") || i.startsWith("audio/") || i.startsWith("video/")) {
788
+ const s = await e.arrayBuffer();
789
+ if (s.byteLength > 10485760)
790
+ return { url: h.url, type: "media", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
791
+ const a = Buffer.from(s).toString("base64");
792
+ return { url: h.url, type: "media", mimeType: i, dataUrl: `data:${i};base64,${a}`, size: s.byteLength };
793
+ }
794
+ if (i.match(/^(text\/(plain|csv|xml)|application\/(json|xml|csv|x-yaml))/) || h.url.match(/\.(txt|json|xml|csv|yaml|yml|md)$/i)) {
795
+ const s = await e.text();
796
+ return { url: h.url, type: "text", mimeType: i, content: s.slice(0, 1e5) };
797
+ }
798
+ if (i === "application/pdf" || i.startsWith("application/") && !i.includes("html")) {
799
+ const s = await e.arrayBuffer();
800
+ if (s.byteLength > 10485760)
801
+ return { url: h.url, type: "binary", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
802
+ const a = Buffer.from(s).toString("base64");
803
+ return { url: h.url, type: "binary", mimeType: i, dataUrl: `data:${i};base64,${a}`, size: s.byteLength };
804
+ }
805
+ const o = await e.text(), n = Z.load(o);
806
+ n('script, style, nav, footer, header, aside, iframe, noscript, svg, [role="navigation"], [role="banner"], [role="complementary"], .ad, .ads, .advertisement, .cookie, .popup, .modal, .sidebar, .related, .comments, .social-share').remove();
807
+ const u = {
808
+ title: n('meta[property="og:title"]').attr("content") || n("title").text() || "",
809
+ description: n('meta[name="description"]').attr("content") || n('meta[property="og:description"]').attr("content") || "",
810
+ author: n('meta[name="author"]').attr("content") || "",
811
+ published: n('meta[property="article:published_time"]').attr("content") || n("time").attr("datetime") || "",
812
+ image: n('meta[property="og:image"]').attr("content") || ""
776
813
  };
777
- let a = "";
778
- const s = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
779
- for (const n of s) {
780
- const c = e(n).first();
781
- if (c.length && c.text().trim().length > 200) {
782
- a = c.text();
814
+ let l = "";
815
+ const c = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content", "body"];
816
+ for (const s of c) {
817
+ const a = n(s).first();
818
+ if (a.length && a.text().trim().length > 200) {
819
+ l = a.text();
783
820
  break;
784
821
  }
785
822
  }
786
- return a || (a = e("body").text()), a = a.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: a, focus: h.focus };
823
+ l || (l = n("body").text()), l = l.replace(/\n\s*\n\s*\n/g, `
824
+
825
+ `).replace(/[ \t]+/g, " ").trim().slice(0, 5e4);
826
+ let m = [];
827
+ return l.length < 500 && (n("a[href]").each((s, a) => {
828
+ const d = n(a).attr("href"), p = n(a).text().trim();
829
+ d && p && !d.startsWith("#") && m.push({ text: p, href: d });
830
+ }), m = m.slice(0, 50)), {
831
+ url: h.url,
832
+ type: "html",
833
+ title: u.title.trim(),
834
+ description: u.description.trim(),
835
+ author: u.author.trim(),
836
+ published: u.published,
837
+ content: l,
838
+ links: m.length ? m : void 0
839
+ };
787
840
  }
788
- }, Se = {
841
+ }, je = {
789
842
  name: "web_search",
790
843
  description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
791
844
  args: {
@@ -795,30 +848,31 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
795
848
  fn: async (h) => {
796
849
  const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
797
850
  headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
798
- }).then((s) => s.text());
851
+ }).then((o) => o.text());
799
852
  let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
800
- const a = new F();
853
+ const i = new F();
801
854
  for (; (e = t.exec(r)) !== null; ) {
802
- let s = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
803
- if (s && (s = decodeURIComponent(s)), s && a.add(s), a.size >= (h.length || 5)) break;
855
+ let o = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
856
+ if (o && (o = decodeURIComponent(o)), o && i.add(o), i.size >= (h.length || 5)) break;
804
857
  }
805
- return a;
858
+ return i;
806
859
  }
807
860
  };
808
861
  export {
809
- we as Ai,
862
+ be as Ai,
810
863
  Q as Anthropic,
811
864
  ee as Audio,
812
865
  ne as CliTool,
813
- be as DateTimeTool,
866
+ we as DateTimeTool,
867
+ xe as DateTimeUTCTool,
814
868
  ke as ExecTool,
815
- xe as FetchTool,
869
+ _e as FetchTool,
816
870
  se as JSTool,
817
- z as LLMProvider,
818
- A as OpenAi,
871
+ L as LLMProvider,
872
+ P as OpenAi,
819
873
  oe as PythonTool,
820
- _e as ReadWebpageTool,
874
+ Se as ReadWebpageTool,
821
875
  te as Vision,
822
- Se as WebSearchTool
876
+ je as WebSearchTool
823
877
  };
824
878
  //# sourceMappingURL=index.mjs.map