@ztimson/ai-utils 0.8.10 → 0.8.12

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 D } from "@ztimson/utils";
4
- import { Anthropic as F } from "@anthropic-ai/sdk";
5
- import { OpenAI as H } 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";
9
- import { mkdtempSync as V } from "node:fs";
10
- import w from "node:fs/promises";
2
+ import { tmpdir as U } from "node:os";
3
+ import { Anthropic as W } from "@anthropic-ai/sdk";
4
+ import { objectMap as z, JSONAttemptParse as S, findByProp as R, JSONSanitize as _, clean as N, Http as C, consoleInterceptor as J, fn as I, ASet as D } from "@ztimson/utils";
5
+ import { OpenAI as F } from "openai";
6
+ import { fileURLToPath as B } from "url";
7
+ import { join as H, dirname as G } from "path";
8
+ 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
11
  import * as M from "node:path";
12
- import P, { join as q } from "node:path";
13
- import { createWorker as Y } from "tesseract.js";
12
+ import q, { join as A } from "node:path";
13
+ import { createWorker as V } from "tesseract.js";
14
14
  import * as Z from "cheerio";
15
15
  import { $Sync as j } from "@ztimson/node-utils";
16
- 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 F({ apiKey: e });
20
+ super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new W({ apiKey: e });
21
21
  }
22
22
  client;
23
23
  toStandard(r) {
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 m = t.findLast((l) => l.id == n.tool_use_id);
37
+ m && (m[n.is_error ? "error" : "content"] = n.content);
38
38
  }
39
39
  });
40
40
  }
@@ -55,78 +55,78 @@ 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 || [], m = {
61
61
  model: e.model || this.model,
62
62
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
63
63
  system: e.system || this.ai.options.llm?.system || "",
64
64
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
65
- tools: n.map((m) => ({
66
- name: m.name,
67
- description: m.description,
65
+ tools: n.map((u) => ({
66
+ name: u.name,
67
+ description: u.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: u.args ? z(u.args, (s, c) => ({ ...c, required: void 0 })) : {},
71
+ required: u.args ? Object.entries(u.args).filter((s) => s[1].required).map((s) => s[0]) : []
72
72
  },
73
73
  fn: void 0
74
74
  })),
75
- messages: s,
75
+ messages: o,
76
76
  stream: !!e.stream
77
77
  };
78
- let u, i = !0;
78
+ let l, a = !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(m).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
+ a ? a = !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 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")
102
102
  break;
103
103
  }
104
104
  }
105
- const m = u.content.filter((o) => o.type === "tool_use");
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" };
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" };
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(c.input, e?.stream, this.ai);
113
+ return { type: "tool_result", tool_use_id: c.id, content: _(p) };
114
114
  } catch (p) {
115
- return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: p?.message || p?.toString() || "Unknown" };
115
+ return { type: "tool_result", tool_use_id: c.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 }), m.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((u) => u.type === "tool_use"));
121
+ o.push({ role: "assistant", content: l.content.filter((u) => u.type == "text").map((u) => u.text).join(`
122
122
 
123
- `) }), 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 H(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 F(N({
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: S(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 m = e.tools || this.ai.options.llm?.tools || [], l = {
182
182
  model: e.model || this.model,
183
183
  messages: n,
184
184
  stream: !!e.stream,
185
185
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
186
186
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
187
- tools: c.map((o) => ({
187
+ tools: m.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, (c, d) => ({ ...d, required: void 0 })) : {},
195
+ required: s.args ? Object.entries(s.args).filter((c) => c[1].required).map((c) => c[0]) : []
196
196
  }
197
197
  }
198
198
  }))
199
199
  };
200
- let i, m = !0;
200
+ let a, u = !0;
201
201
  do {
202
- if (i = await this.client.chat.completions.create(u).catch((l) => {
203
- throw l.message += `
202
+ if (a = await this.client.chat.completions.create(l).catch((c) => {
203
+ throw c.message += `
204
204
 
205
205
  Messages:
206
- ${JSON.stringify(n, null, 2)}`, l;
206
+ ${JSON.stringify(n, null, 2)}`, c;
207
207
  }), e.stream) {
208
- m ? m = !1 : e.stream({ text: `
208
+ u ? u = !1 : e.stream({ text: `
209
209
 
210
- ` }), i.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
- for await (const l of i) {
210
+ ` }), a.choices = [{ message: { role: "assistant", content: "", tool_calls: [] } }];
211
+ for await (const c of a) {
212
212
  if (t.signal.aborted) break;
213
- if (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 (c.choices[0].delta.content && (a.choices[0].message.content += c.choices[0].delta.content, e.stream({ text: c.choices[0].delta.content })), c.choices[0].delta.tool_calls)
214
+ for (const d of c.choices[0].delta.tool_calls) {
215
+ const p = a.choices[0].message.tool_calls.find((f) => f.index === d.index);
216
+ p ? (d.id && (p.id = d.id), d.type && (p.type = d.type), d.function && (p.function || (p.function = {}), d.function.name && (p.function.name = d.function.name), d.function.arguments && (p.function.arguments = (p.function.arguments || "") + d.function.arguments))) : a.choices[0].message.tool_calls.push({
217
217
  index: d.index,
218
218
  id: d.id || "",
219
219
  type: d.type || "function",
@@ -225,30 +225,30 @@ ${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 = a.choices[0].message.tool_calls || [];
229
+ if (s.length && !t.signal.aborted) {
230
+ n.push(a.choices[0].message);
231
+ const c = await Promise.all(s.map(async (d) => {
232
+ const p = m?.find(R("name", d.function.name));
233
233
  if (e.stream && e.stream({ tool: d.function.name }), !p) return { role: "tool", tool_call_id: d.id, content: '{"error": "Tool not found"}' };
234
234
  try {
235
- const f = _(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
236
- return { role: "tool", tool_call_id: d.id, content: S(g) };
235
+ const f = S(d.function.arguments, {}), g = await p.fn(f, e.stream, this.ai);
236
+ return { role: "tool", tool_call_id: d.id, content: _(g) };
237
237
  } catch (f) {
238
- return { role: "tool", tool_call_id: d.id, content: S({ error: f?.message || f?.toString() || "Unknown" }) };
238
+ return { role: "tool", tool_call_id: d.id, content: _({ error: f?.message || f?.toString() || "Unknown" }) };
239
239
  }
240
240
  }));
241
- n.push(...l), u.messages = n;
241
+ n.push(...c), 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 && a.choices?.[0]?.message?.tool_calls?.length);
244
+ n.push({ role: "assistant", content: a.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), i(n.at(-1)?.content);
245
245
  }), { abort: () => t.abort() });
246
246
  }
247
247
  }
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([
279
- m ? this.embedding(m) : Promise.resolve(null),
280
- i ? this.embedding(i) : Promise.resolve(null)
277
+ const m = async (a, u, s = 10) => {
278
+ const [c, d] = await Promise.all([
279
+ u ? this.embedding(u) : Promise.resolve(null),
280
+ a ? this.embedding(a) : Promise.resolve(null)
281
281
  ]);
282
282
  return (e.memory || []).map((p) => {
283
- const f = (l ? this.cosineSimilarity(p.embeddings[0], l[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
283
+ const f = (c ? this.cosineSimilarity(p.embeddings[0], c[0].embedding) : 0) + (d ? this.cosineSimilarity(p.embeddings[1], d[0].embedding) : 0);
284
284
  return { ...p, score: f };
285
- }).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 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 = [{
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: (a) => {
302
+ if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
303
+ return m(a.query, a.subject, a.topK);
304
304
  }
305
305
  }, {
306
306
  name: "remember",
@@ -309,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 (a) => {
313
313
  if (!e.memory) return;
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!";
314
+ const u = await Promise.all([
315
+ this.embedding(a.owner),
316
+ this.embedding(`${a.owner}: ${a.fact}`)
317
+ ]), s = { owner: a.owner, fact: a.fact, embeddings: [u[0][0].embedding, u[1][0].embedding] };
318
+ return e.memory.splice(0, e.memory.length, ...e.memory.filter((c) => !(this.cosineSimilarity(s.embeddings[0], c.embeddings[0]) >= 0.9 && this.cosineSimilarity(s.embeddings[1], c.embeddings[1]) >= 0.8)), s), "Remembered!";
319
319
  }
320
320
  }, ...e.tools || []];
321
321
  }
322
322
  const n = await this.models[t].ask(r, e);
323
- if (e.memory && e.history.splice(0, e.history.length, ...e.history.filter((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((m) => m.role != "tool" || m.name != "recall" && m.name != "remember")), e.compress) {
324
+ const m = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e);
325
+ e.history.splice(0, e.history.length, ...m);
326
326
  }
327
- 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 m = r[0].role == "system" ? r[0] : null, l = o == 0 ? [] : r.slice(-o), a = (o == 0 ? r : r.slice(0, -o)).filter((d) => d.role === "assistant" || d.role === "user"), u = await this.summarize(a.map((d) => `[${d.role}]: ${d.content}`).join(`
354
354
 
355
- `), 500, 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(), c = [{ role: "tool", name: "summary", id: "summary_" + s, args: {}, content: `Conversation Summary: ${u?.summary}`, timestamp: s }, ...l];
356
+ return m && c.splice(0, 0, m), c;
357
357
  }
358
358
  /**
359
359
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -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 m = 0; m < r.length; m++)
368
+ t += r[m] * e[m], i += r[m] * r[m], o += e[m] * e[m];
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;
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++;
380
+ const i = (l, a = "") => l ? Object.entries(l).flatMap(([u, s]) => {
381
+ const c = a ? `${a}${isNaN(+u) ? `.${u}` : `[${u}]`}` : u;
382
+ return typeof s == "object" && !Array.isArray(s) ? i(s, c) : `${c}: ${Array.isArray(s) ? s.join(", ") : s}`;
383
+ }) : [], n = (typeof r == "object" ? i(r) : r.toString().split(`
384
+ `)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
385
+ `]), m = [];
386
+ for (let l = 0; l < n.length; ) {
387
+ let a = "", u = l;
388
+ for (; u < n.length; ) {
389
+ const c = a + (a ? " " : "") + n[u];
390
+ if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
391
+ `)) > e && a) break;
392
+ a = c, u++;
393
393
  }
394
- const o = i.replace(/\s*\n\s*/g, `
394
+ const s = a.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 && m.push(s), l = Math.max(u - t, u === l ? l + 1 : u);
397
397
  }
398
- return c;
398
+ return m;
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
+ }, m = (a) => new Promise((u, s) => {
411
+ if (o) return s(new Error("Aborted"));
412
+ const c = [
413
+ H(G(B(import.meta.url)), "embedder.js"),
414
414
  this.ai.options.path,
415
415
  this.ai.options?.embedder || "bge-small-en-v1.5"
416
- ], d = b("node", l, { stdio: ["pipe", "pipe", "ignore"] });
417
- d.stdin.write(i), d.stdin.end();
416
+ ], d = w("node", c, { stdio: ["pipe", "pipe", "ignore"] });
417
+ d.stdin.write(a), d.stdin.end();
418
418
  let p = "";
419
419
  d.stdout.on("data", (f) => p += f.toString()), d.on("close", (f) => {
420
- 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
- m(g.embedding);
424
+ u(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 a = this.chunk(r, t, i), u = [];
433
+ for (let s = 0; s < a.length && !o; s++) {
434
+ const c = a[s], d = await m(c);
435
+ u.push({ index: s, embedding: d, text: c, tokens: this.estimateTokens(c) });
436
436
  }
437
- return m;
437
+ return u;
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, m = 10) => n.toLowerCase().split("").map((l, a) => l.charCodeAt(0) * (a + 1) % m / m).slice(0, m), i = t(r), o = e.map((n) => t(n)).map((n) => this.cosineSimilarity(i, n));
459
+ return { avg: o.reduce((n, m) => n + m, 0) / o.length, max: Math.max(...o), similarities: o };
460
460
  }
461
461
  /**
462
462
  * Ask a question with JSON response
@@ -466,15 +466,35 @@ ${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: [
470
- t?.system,
471
- `Only respond using JSON matching this schema:
469
+ let i = `Your job is to convert input to JSON. Call \`submit\` exactly once with JSON matching this schema:
472
470
  \`\`\`json
473
471
  ${e}
474
- \`\`\``
475
- ].filter((s) => !!s).join(`
476
- `) });
477
- return a ? _(a, {}) : null;
472
+ \`\`\``;
473
+ return t?.system && (i += `
474
+
475
+ ` + t.system), new Promise(async (o, n) => {
476
+ let m = !1;
477
+ const l = await this.ask(r, {
478
+ temperature: 0.3,
479
+ ...t,
480
+ system: i,
481
+ tools: [{
482
+ name: "submit",
483
+ description: "Submit JSON",
484
+ args: { json: { type: "string", description: "Javascript parsable JSON string", required: !0 } },
485
+ fn: (a) => {
486
+ try {
487
+ const u = JSON.parse(a.json);
488
+ o(u), m = !0;
489
+ } catch {
490
+ return "Invalid JSON";
491
+ }
492
+ return "Done";
493
+ }
494
+ }, ...t?.tools || []]
495
+ });
496
+ m || n(`AI failed to create summary: ${l}`);
497
+ });
478
498
  }
479
499
  /**
480
500
  * Create a summary of some text
@@ -483,8 +503,25 @@ ${e}
483
503
  * @param options LLM request options
484
504
  * @returns {Promise<string>} Summary
485
505
  */
486
- summarize(r, e = 500, t) {
487
- return this.ask(r, { system: `Generate the shortest summary possible <= ${e} tokens. Output nothing else`, temperature: 0.3, ...t });
506
+ async summarize(r, e = 500, t) {
507
+ let i = `Your job is to summarize the users message. Call \`submit\` exactly once with the shortest summary possible that's <= ${e} tokens. Output nothing else`;
508
+ return t?.system && (i += `
509
+
510
+ ` + t.system), new Promise(async (o, n) => {
511
+ let m = !1;
512
+ const l = await this.ask(r, {
513
+ temperature: 0.3,
514
+ ...t,
515
+ system: i,
516
+ tools: [{
517
+ name: "submit",
518
+ description: "Submit summary",
519
+ args: { summary: { type: "string", description: "Summarization", required: !0 } },
520
+ fn: (a) => (m = !0, o(a.summary || null), "Done")
521
+ }, ...t?.tools || []]
522
+ });
523
+ m || n(`AI failed to create summary: ${l}`);
524
+ });
488
525
  }
489
526
  }
490
527
  class ee {
@@ -510,21 +547,21 @@ print(json.dumps(segments))
510
547
  pyannote;
511
548
  whisperModel;
512
549
  async addPunctuation(r, e, t = 150) {
513
- const a = (n) => {
550
+ const i = (n) => {
514
551
  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);
552
+ const m = n.match(/[aeiouy]+/g);
553
+ let l = m ? m.length : 1;
554
+ return n.endsWith("e") && l--, Math.max(1, l);
518
555
  };
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;
556
+ let o = "";
557
+ return r.transcription.filter((n, m) => {
558
+ let l = !1;
559
+ const a = r.transcription[m - 1], u = r.transcription[m + 1];
560
+ return !n.text && u ? (u.offsets.from = n.offsets.from, u.timestamps.from = n.offsets.from) : n.text && n.text[0] != " " && a && (a.offsets.to = n.offsets.to, a.timestamps.to = n.timestamps.to, a.text += n.text, l = !0), !!n.text && !l;
524
561
  }).forEach((n) => {
525
- const 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, {
562
+ const m = /^[A-Z]/.test(n.text.trim()), l = n.offsets.to - n.offsets.from, u = i(n.text.trim()) * t;
563
+ m && l > u * 2 && n.text[0] == " " && (o += "."), o += n.text;
564
+ }), e ? this.ai.language.ask(o, {
528
565
  system: "Remove any misplaced punctuation from the following ASR transcript using the replace tool. Avoid modifying words unless there is an obvious typo",
529
566
  temperature: 0.1,
530
567
  tools: [{
@@ -534,138 +571,138 @@ print(json.dumps(segments))
534
571
  find: { type: "string", description: "Text to find", required: !0 },
535
572
  replace: { type: "string", description: "Text to replace", required: !0 }
536
573
  },
537
- fn: (n) => s = s.replace(n.find, n.replace)
574
+ fn: (n) => o = o.replace(n.find, n.replace)
538
575
  }]
539
- }).then(() => s) : s.trim();
576
+ }).then(() => o) : o.trim();
540
577
  }
541
578
  async diarizeTranscript(r, e, t) {
542
- const a = /* @__PURE__ */ new Map();
543
- let s = 0;
579
+ const i = /* @__PURE__ */ new Map();
580
+ let o = 0;
544
581
  e.forEach((p) => {
545
- a.has(p.speaker) || a.set(p.speaker, ++s);
582
+ i.has(p.speaker) || i.set(p.speaker, ++o);
546
583
  });
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) => {
584
+ const n = await this.addPunctuation(r, t), m = n.match(/[^.!?]+[.!?]+/g) || [n], l = r.transcription.filter((p) => p.text.trim()), a = m.map((p) => {
548
585
  if (p = p.trim(), !p) return null;
549
586
  const f = p.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/), g = /* @__PURE__ */ new Map();
550
- 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);
554
- if (v) {
555
- const y = a.get(v.speaker);
587
+ f.forEach((x) => {
588
+ const k = l.find((y) => x === y.text.trim().toLowerCase().replace(/[^\w]/g, ""));
589
+ if (!k) return;
590
+ const v = k.offsets.from / 1e3, $ = e.find((y) => v >= y.start && v <= y.end);
591
+ if ($) {
592
+ const y = i.get($.speaker);
556
593
  g.set(y, (g.get(y) || 0) + 1);
557
594
  }
558
595
  });
559
596
  let T = 1, E = 0;
560
- return g.forEach((k, x) => {
561
- k > E && (E = k, T = x);
597
+ return g.forEach((x, k) => {
598
+ x > E && (E = x, T = k);
562
599
  }), { speaker: T, text: p };
563
- }).filter((p) => p !== null), m = [];
564
- i.forEach((p) => {
565
- const f = m[m.length - 1];
566
- f && f.speaker === p.speaker ? f.text += " " + p.text : m.push({ ...p });
600
+ }).filter((p) => p !== null), u = [];
601
+ a.forEach((p) => {
602
+ const f = u[u.length - 1];
603
+ f && f.speaker === p.speaker ? f.text += " " + p.text : u.push({ ...p });
567
604
  });
568
- let o = m.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
605
+ let s = u.map((p) => `[Speaker ${p.speaker}]: ${p.text}`).join(`
569
606
  `).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(`
607
+ if (!t) return s;
608
+ let c = this.ai.language.chunk(s, 500, 0);
609
+ c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
610
+ const d = await this.ai.language.json(c.join(`
574
611
  `), '{1: "Detected Name", 2: "Second Name"}', {
575
612
  system: "Use the following transcript to identify speakers. Only identify speakers you are positive about, dont mention speakers you are unsure about in your response",
576
613
  temperature: 0.1
577
614
  });
578
- return Object.entries(d).forEach(([p, f]) => o = o.replaceAll(`[Speaker ${p}]`, `[${f}]`)), o;
615
+ return Object.entries(d).forEach(([p, f]) => s = s.replaceAll(`[Speaker ${p}]`, `[${f}]`)), s;
579
616
  }
580
617
  runAsr(r, e = {}) {
581
618
  let t;
582
- const a = new Promise((s, n) => {
583
- this.downloadAsrModel(e.model).then((c) => {
619
+ const i = new Promise((o, n) => {
620
+ this.downloadAsrModel(e.model).then((m) => {
584
621
  if (e.diarization) {
585
- let u = M.join(M.dirname(r), "transcript");
586
- t = b(
622
+ let l = M.join(M.dirname(r), "transcript");
623
+ t = w(
587
624
  this.ai.options.whisper,
588
- ["-m", c, "-f", r, "-np", "-ml", "1", "-oj", "-of", u],
625
+ ["-m", m, "-f", r, "-np", "-ml", "1", "-oj", "-of", l],
589
626
  { 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(() => {
627
+ ), t.on("error", (a) => n(a)), t.on("close", async (a) => {
628
+ if (a === 0) {
629
+ l = await b.readFile(l + ".json", "utf-8"), b.rm(l + ".json").catch(() => {
593
630
  });
594
631
  try {
595
- s(JSON.parse(u));
632
+ o(JSON.parse(l));
596
633
  } catch {
597
634
  n(new Error("Failed to parse whisper JSON"));
598
635
  }
599
636
  } else
600
- n(new Error(`Exit code ${i}`));
637
+ n(new Error(`Exit code ${a}`));
601
638
  });
602
639
  } 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}`));
640
+ let l = "";
641
+ t = w(this.ai.options.whisper, ["-m", m, "-f", r, "-np", "-nt"]), t.on("error", (a) => n(a)), t.stdout.on("data", (a) => l += a.toString()), t.on("close", async (a) => {
642
+ a === 0 ? o(l.trim() || null) : n(new Error(`Exit code ${a}`));
606
643
  });
607
644
  }
608
645
  });
609
646
  });
610
- return Object.assign(a, { abort: () => t?.kill("SIGTERM") });
647
+ return Object.assign(i, { abort: () => t?.kill("SIGTERM") });
611
648
  }
612
649
  runDiarization(r) {
613
650
  let e = !1, t = () => {
614
651
  e = !0;
615
652
  };
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]) => {
653
+ const i = (n) => new Promise((m) => {
654
+ const l = w(n, ["-W", "ignore", "-c", "import pyannote.audio"]);
655
+ l.on("close", (a) => m(a === 0)), l.on("error", () => m(!1));
656
+ }), o = Promise.all([
657
+ i("python"),
658
+ i("python3")
659
+ ]).then((async ([n, m]) => {
623
660
  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) => {
661
+ if (!n && !m) throw new Error("Pyannote is not installed: pip install pyannote.audio");
662
+ const l = m ? "python3" : "python";
663
+ return new Promise((a, u) => {
627
664
  if (e) return;
628
- 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) => {
665
+ let s = "";
666
+ const c = w(l, ["-W", "ignore", "-c", this.pyannote, r]);
667
+ c.stdout.on("data", (d) => s += d.toString()), c.stderr.on("data", (d) => console.error(d.toString())), c.on("close", (d) => {
631
668
  if (d === 0)
632
669
  try {
633
- i(JSON.parse(o));
670
+ a(JSON.parse(s));
634
671
  } catch {
635
- m(new Error("Failed to parse diarization output"));
672
+ u(new Error("Failed to parse diarization output"));
636
673
  }
637
674
  else
638
- m(new Error(`Python process exited with code ${d}`));
639
- }), l.on("error", m), t = () => l.kill("SIGTERM");
675
+ u(new Error(`Python process exited with code ${d}`));
676
+ }), c.on("error", u), t = () => c.kill("SIGTERM");
640
677
  });
641
678
  }));
642
- return Object.assign(s, { abort: t });
679
+ return Object.assign(o, { abort: t });
643
680
  }
644
681
  asr(r, e = {}) {
645
682
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
646
- const t = q(V(q(L(), "audio-")), "converted.wav");
683
+ const t = A(Y(A(U(), "audio-")), "converted.wav");
647
684
  K(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
648
- const a = () => w.rm(P.dirname(t), { recursive: !0, force: !0 }).catch(() => {
685
+ const i = () => b.rm(q.dirname(t), { recursive: !0, force: !0 }).catch(() => {
649
686
  });
650
687
  if (!e.diarization) return this.runAsr(t, { model: e.model });
651
- 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();
688
+ const o = this.runAsr(t, { model: e.model, diarization: !0 }), n = this.runDiarization(t);
689
+ let m = !1, l = () => {
690
+ m = !0, o.abort(), n.abort(), i();
654
691
  };
655
- const i = Promise.allSettled([s, n]).then(async ([m, o]) => {
656
- if (m.status == "rejected") throw new Error(`Whisper.cpp timestamps:
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 });
692
+ const a = Promise.allSettled([o, n]).then(async ([u, s]) => {
693
+ if (u.status == "rejected") throw new Error(`Whisper.cpp timestamps:
694
+ ` + u.reason);
695
+ if (s.status == "rejected") throw new Error(`Pyannote:
696
+ ` + s.reason);
697
+ return m || !e.diarization ? u.value : this.diarizeTranscript(u.value, s.value, e.diarization == "llm");
698
+ }).finally(() => i());
699
+ return Object.assign(a, { abort: l });
663
700
  }
664
701
  async downloadAsrModel(r = this.whisperModel) {
665
702
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
666
703
  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]);
704
+ const e = q.join(this.ai.options.path, r);
705
+ 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
706
  }
670
707
  }
671
708
  class te {
@@ -679,15 +716,15 @@ class te {
679
716
  */
680
717
  ocr(r) {
681
718
  let e;
682
- const t = new Promise(async (a) => {
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);
719
+ const t = new Promise(async (i) => {
720
+ e = await V(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
721
+ const { data: o } = await e.recognize(r);
722
+ await e.terminate(), i(o.text.trim() || null);
686
723
  });
687
724
  return Object.assign(t, { abort: () => e?.terminate() });
688
725
  }
689
726
  }
690
- class we {
727
+ class be {
691
728
  constructor(r) {
692
729
  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
730
  }
@@ -703,17 +740,17 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
703
740
  description: "Use the command line interface, returns any output",
704
741
  args: { command: { type: "string", description: "Command to run", required: !0 } },
705
742
  fn: (h) => j`${h.command}`
706
- }, be = {
743
+ }, we = {
707
744
  name: "get_datetime",
708
745
  description: "Get local date / time",
709
746
  args: {},
710
747
  fn: async () => (/* @__PURE__ */ new Date()).toString()
711
- }, ke = {
748
+ }, xe = {
712
749
  name: "get_datetime_utc",
713
750
  description: "Get current UTC date / time",
714
751
  args: {},
715
752
  fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
716
- }, xe = {
753
+ }, ke = {
717
754
  name: "exec",
718
755
  description: "Run code/scripts",
719
756
  args: {
@@ -736,7 +773,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
736
773
  return { error: t?.message || t.toString() };
737
774
  }
738
775
  }
739
- }, _e = {
776
+ }, Se = {
740
777
  name: "fetch",
741
778
  description: "Make HTTP request to URL",
742
779
  args: {
@@ -745,7 +782,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
745
782
  headers: { type: "object", description: "HTTP headers to send", default: {} },
746
783
  body: { type: "object", description: "HTTP body to send" }
747
784
  },
748
- fn: (h) => new N({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
785
+ fn: (h) => new C({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
749
786
  }, se = {
750
787
  name: "exec_javascript",
751
788
  description: "Execute commonjs javascript",
@@ -753,7 +790,7 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
753
790
  code: { type: "string", description: "CommonJS javascript", required: !0 }
754
791
  },
755
792
  fn: async (h) => {
756
- const r = C(null), e = await J({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
793
+ const r = J(null), e = await I({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
757
794
  return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
758
795
  }
759
796
  }, oe = {
@@ -763,32 +800,80 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
763
800
  code: { type: "string", description: "CommonJS javascript", required: !0 }
764
801
  },
765
802
  fn: async (h) => ({ result: j`python -c "${h.code}"` })
766
- }, Se = {
803
+ }, _e = {
767
804
  name: "read_webpage",
768
- description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
805
+ description: "Extract clean, structured content from a webpage or convert media/documents to accessible formats",
769
806
  args: {
770
807
  url: { type: "string", description: "URL to extract content from", required: !0 },
771
- focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
808
+ mimeRegex: { type: "string", description: 'Optional: Regex pattern to filter MIME types (e.g., "^image/", "text/", "application/pdf")' },
809
+ maxSize: { type: "number", description: "Optional: Max file size in bytes for binary content (default: 10MB)" }
772
810
  },
773
811
  fn: async (h) => {
774
- const r = await fetch(h.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((n) => n.text()).catch((n) => {
775
- throw new Error(`Failed to fetch: ${n.message}`);
776
- }), e = Z.load(r);
777
- e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
778
- const t = {
779
- title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
780
- description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
812
+ const e = await fetch(h.url, {
813
+ headers: {
814
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
815
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
816
+ "Accept-Language": "en-US,en;q=0.5"
817
+ },
818
+ redirect: "follow"
819
+ }).catch((s) => {
820
+ throw new Error(`Failed to fetch: ${s.message}`);
821
+ }), t = e.headers.get("content-type") || "", i = t.split(";")[0].trim().toLowerCase();
822
+ if (t.match(/charset=([^;]+)/)?.[1], h.mimeRegex && !new RegExp(h.mimeRegex, "i").test(i))
823
+ return { url: h.url, error: "MIME type rejected", mimeType: i, filter: h.mimeRegex };
824
+ if (i.startsWith("image/") || i.startsWith("audio/") || i.startsWith("video/")) {
825
+ const s = await e.arrayBuffer();
826
+ if (s.byteLength > 10485760)
827
+ return { url: h.url, type: "media", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
828
+ const c = Buffer.from(s).toString("base64");
829
+ return { url: h.url, type: "media", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
830
+ }
831
+ if (i.match(/^(text\/(plain|csv|xml)|application\/(json|xml|csv|x-yaml))/) || h.url.match(/\.(txt|json|xml|csv|yaml|yml|md)$/i)) {
832
+ const s = await e.text();
833
+ return { url: h.url, type: "text", mimeType: i, content: s.slice(0, 1e5) };
834
+ }
835
+ if (i === "application/pdf" || i.startsWith("application/") && !i.includes("html")) {
836
+ const s = await e.arrayBuffer();
837
+ if (s.byteLength > 10485760)
838
+ return { url: h.url, type: "binary", mimeType: i, error: "File too large", size: s.byteLength, maxSize: 10485760 };
839
+ const c = Buffer.from(s).toString("base64");
840
+ return { url: h.url, type: "binary", mimeType: i, dataUrl: `data:${i};base64,${c}`, size: s.byteLength };
841
+ }
842
+ const o = await e.text(), n = Z.load(o);
843
+ n('script, style, nav, footer, header, aside, iframe, noscript, svg, [role="navigation"], [role="banner"], [role="complementary"], .ad, .ads, .advertisement, .cookie, .popup, .modal, .sidebar, .related, .comments, .social-share').remove();
844
+ const m = {
845
+ title: n('meta[property="og:title"]').attr("content") || n("title").text() || "",
846
+ description: n('meta[name="description"]').attr("content") || n('meta[property="og:description"]').attr("content") || "",
847
+ author: n('meta[name="author"]').attr("content") || "",
848
+ published: n('meta[property="article:published_time"]').attr("content") || n("time").attr("datetime") || "",
849
+ image: n('meta[property="og:image"]').attr("content") || ""
781
850
  };
782
- let a = "";
783
- const s = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
784
- for (const n of s) {
785
- const c = e(n).first();
851
+ let l = "";
852
+ const a = ["article", "main", '[role="main"]', ".content", ".post-content", ".entry-content", ".article-content", "body"];
853
+ for (const s of a) {
854
+ const c = n(s).first();
786
855
  if (c.length && c.text().trim().length > 200) {
787
- a = c.text();
856
+ l = c.text();
788
857
  break;
789
858
  }
790
859
  }
791
- 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 };
860
+ l || (l = n("body").text()), l = l.replace(/\n\s*\n\s*\n/g, `
861
+
862
+ `).replace(/[ \t]+/g, " ").trim().slice(0, 5e4);
863
+ let u = [];
864
+ return l.length < 500 && (n("a[href]").each((s, c) => {
865
+ const d = n(c).attr("href"), p = n(c).text().trim();
866
+ d && p && !d.startsWith("#") && u.push({ text: p, href: d });
867
+ }), u = u.slice(0, 50)), {
868
+ url: h.url,
869
+ type: "html",
870
+ title: m.title.trim(),
871
+ description: m.description.trim(),
872
+ author: m.author.trim(),
873
+ published: m.published,
874
+ content: l,
875
+ links: u.length ? u : void 0
876
+ };
792
877
  }
793
878
  }, je = {
794
879
  name: "web_search",
@@ -800,30 +885,30 @@ const re = () => O.platform() == "win32" ? "cmd" : j`echo $SHELL`?.split("/").po
800
885
  fn: async (h) => {
801
886
  const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
802
887
  headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
803
- }).then((s) => s.text());
888
+ }).then((o) => o.text());
804
889
  let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
805
- const a = new D();
890
+ const i = new D();
806
891
  for (; (e = t.exec(r)) !== null; ) {
807
- let s = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
808
- if (s && (s = decodeURIComponent(s)), s && a.add(s), a.size >= (h.length || 5)) break;
892
+ let o = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
893
+ if (o && (o = decodeURIComponent(o)), o && i.add(o), i.size >= (h.length || 5)) break;
809
894
  }
810
- return a;
895
+ return i;
811
896
  }
812
897
  };
813
898
  export {
814
- we as Ai,
899
+ be as Ai,
815
900
  Q as Anthropic,
816
901
  ee as Audio,
817
902
  ne as CliTool,
818
- be as DateTimeTool,
819
- ke as DateTimeUTCTool,
820
- xe as ExecTool,
821
- _e as FetchTool,
903
+ we as DateTimeTool,
904
+ xe as DateTimeUTCTool,
905
+ ke as ExecTool,
906
+ Se as FetchTool,
822
907
  se as JSTool,
823
- z as LLMProvider,
824
- A as OpenAi,
908
+ L as LLMProvider,
909
+ P as OpenAi,
825
910
  oe as PythonTool,
826
- Se as ReadWebpageTool,
911
+ _e as ReadWebpageTool,
827
912
  te as Vision,
828
913
  je as WebSearchTool
829
914
  };