@ztimson/ai-utils 0.7.8 → 0.7.10

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
@@ -6,11 +6,10 @@ import { OpenAI as U } from "openai";
6
6
  import { fileURLToPath as z } from "url";
7
7
  import { join as N, dirname as L } from "path";
8
8
  import { spawn as g, execSync as C } from "node:child_process";
9
- import { mkdtempSync as J, rmSync as W } from "node:fs";
10
- import k from "node:fs/promises";
9
+ import { mkdtempSync as J } from "node:fs";
10
+ import k, { rm as W } from "node:fs/promises";
11
11
  import _, { join as x } from "node:path";
12
12
  import { createWorker as D } from "tesseract.js";
13
- import "./embedder.mjs";
14
13
  import * as I from "cheerio";
15
14
  import { $ as F, $Sync as H } from "@ztimson/node-utils";
16
15
  class E {
@@ -22,19 +21,19 @@ class G extends E {
22
21
  client;
23
22
  toStandard(r) {
24
23
  const e = Date.now(), t = [];
25
- for (let l of r)
26
- if (typeof l.content == "string")
27
- t.push({ timestamp: e, ...l });
24
+ for (let m of r)
25
+ if (typeof m.content == "string")
26
+ t.push({ timestamp: e, ...m });
28
27
  else {
29
- const n = l.content?.filter((s) => s.type == "text").map((s) => s.text).join(`
28
+ const n = m.content?.filter((s) => s.type == "text").map((s) => s.text).join(`
30
29
 
31
30
  `);
32
- n && t.push({ timestamp: e, role: l.role, content: n }), l.content.forEach((s) => {
31
+ n && t.push({ timestamp: e, role: m.role, content: n }), m.content.forEach((s) => {
33
32
  if (s.type == "tool_use")
34
33
  t.push({ timestamp: e, role: "tool", id: s.id, name: s.name, args: s.input, content: void 0 });
35
34
  else if (s.type == "tool_result") {
36
- const o = t.findLast((c) => c.id == s.tool_use_id);
37
- o && (o[s.is_error ? "error" : "content"] = s.content);
35
+ const i = t.findLast((l) => l.id == s.tool_use_id);
36
+ i && (i[s.is_error ? "error" : "content"] = s.content);
38
37
  }
39
38
  });
40
39
  }
@@ -55,78 +54,78 @@ class G extends E {
55
54
  }
56
55
  ask(r, e = {}) {
57
56
  const t = new AbortController();
58
- return Object.assign(new Promise(async (l) => {
57
+ return Object.assign(new Promise(async (m) => {
59
58
  let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
- const s = e.tools || this.ai.options.llm?.tools || [], o = {
59
+ const s = e.tools || this.ai.options.llm?.tools || [], i = {
61
60
  model: e.model || this.model,
62
61
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
63
62
  system: e.system || this.ai.options.llm?.system || "",
64
63
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
65
- tools: s.map((m) => ({
66
- name: m.name,
67
- description: m.description,
64
+ tools: s.map((a) => ({
65
+ name: a.name,
66
+ description: a.description,
68
67
  input_schema: {
69
68
  type: "object",
70
- properties: m.args ? j(m.args, (i, d) => ({ ...d, required: void 0 })) : {},
71
- required: m.args ? Object.entries(m.args).filter((i) => i[1].required).map((i) => i[0]) : []
69
+ properties: a.args ? j(a.args, (o, c) => ({ ...c, required: void 0 })) : {},
70
+ required: a.args ? Object.entries(a.args).filter((o) => o[1].required).map((o) => o[0]) : []
72
71
  },
73
72
  fn: void 0
74
73
  })),
75
74
  messages: n,
76
75
  stream: !!e.stream
77
76
  };
78
- let c, a = !0;
77
+ let l, d = !0;
79
78
  do {
80
- if (c = await this.client.messages.create(o).catch((i) => {
81
- throw i.message += `
79
+ if (l = await this.client.messages.create(i).catch((o) => {
80
+ throw o.message += `
82
81
 
83
82
  Messages:
84
- ${JSON.stringify(n, null, 2)}`, i;
83
+ ${JSON.stringify(n, null, 2)}`, o;
85
84
  }), e.stream) {
86
- a ? a = !1 : e.stream({ text: `
85
+ d ? d = !1 : e.stream({ text: `
87
86
 
88
- ` }), c.content = [];
89
- for await (const i of c) {
87
+ ` }), l.content = [];
88
+ for await (const o of l) {
90
89
  if (t.signal.aborted) break;
91
- if (i.type === "content_block_start")
92
- i.content_block.type === "text" ? c.content.push({ type: "text", text: "" }) : i.content_block.type === "tool_use" && c.content.push({ type: "tool_use", id: i.content_block.id, name: i.content_block.name, input: "" });
93
- else if (i.type === "content_block_delta")
94
- if (i.delta.type === "text_delta") {
95
- const d = i.delta.text;
96
- c.content.at(-1).text += d, e.stream({ text: d });
97
- } else i.delta.type === "input_json_delta" && (c.content.at(-1).input += i.delta.partial_json);
98
- else if (i.type === "content_block_stop") {
99
- const d = c.content.at(-1);
100
- d.input != null && (d.input = d.input ? w(d.input, {}) : {});
101
- } else if (i.type === "message_stop")
90
+ if (o.type === "content_block_start")
91
+ o.content_block.type === "text" ? l.content.push({ type: "text", text: "" }) : o.content_block.type === "tool_use" && l.content.push({ type: "tool_use", id: o.content_block.id, name: o.content_block.name, input: "" });
92
+ else if (o.type === "content_block_delta")
93
+ if (o.delta.type === "text_delta") {
94
+ const c = o.delta.text;
95
+ l.content.at(-1).text += c, e.stream({ text: c });
96
+ } else o.delta.type === "input_json_delta" && (l.content.at(-1).input += o.delta.partial_json);
97
+ else if (o.type === "content_block_stop") {
98
+ const c = l.content.at(-1);
99
+ c.input != null && (c.input = c.input ? w(c.input, {}) : {});
100
+ } else if (o.type === "message_stop")
102
101
  break;
103
102
  }
104
103
  }
105
- const m = c.content.filter((i) => i.type === "tool_use");
106
- if (m.length && !t.signal.aborted) {
107
- n.push({ role: "assistant", content: c.content });
108
- const i = await Promise.all(m.map(async (d) => {
109
- const p = s.find(T("name", d.name));
110
- if (e.stream && e.stream({ tool: d.name }), !p) return { tool_use_id: d.id, is_error: !0, content: "Tool not found" };
104
+ const a = l.content.filter((o) => o.type === "tool_use");
105
+ if (a.length && !t.signal.aborted) {
106
+ n.push({ role: "assistant", content: l.content });
107
+ const o = await Promise.all(a.map(async (c) => {
108
+ const p = s.find(T("name", c.name));
109
+ if (e.stream && e.stream({ tool: c.name }), !p) return { tool_use_id: c.id, is_error: !0, content: "Tool not found" };
111
110
  try {
112
- const u = await p.fn(d.input, e?.stream, this.ai);
113
- return { type: "tool_result", tool_use_id: d.id, content: b(u) };
111
+ const u = await p.fn(c.input, e?.stream, this.ai);
112
+ return { type: "tool_result", tool_use_id: c.id, content: b(u) };
114
113
  } catch (u) {
115
- return { type: "tool_result", tool_use_id: d.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
114
+ return { type: "tool_result", tool_use_id: c.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
116
115
  }
117
116
  }));
118
- n.push({ role: "user", content: i }), o.messages = n;
117
+ n.push({ role: "user", content: o }), i.messages = n;
119
118
  }
120
- } while (!t.signal.aborted && c.content.some((m) => m.type === "tool_use"));
121
- n.push({ role: "assistant", content: c.content.filter((m) => m.type == "text").map((m) => m.text).join(`
119
+ } while (!t.signal.aborted && l.content.some((a) => a.type === "tool_use"));
120
+ n.push({ role: "assistant", content: l.content.filter((a) => a.type == "text").map((a) => a.text).join(`
122
121
 
123
- `) }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), l(n.at(-1)?.content);
122
+ `) }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), m(n.at(-1)?.content);
124
123
  }), { abort: () => t.abort() });
125
124
  }
126
125
  }
127
126
  class S extends E {
128
- constructor(r, e, t, l) {
129
- super(), this.ai = r, this.host = e, this.token = t, this.model = l, this.client = new U(M({
127
+ constructor(r, e, t, m) {
128
+ super(), this.ai = r, this.host = e, this.token = t, this.model = m, this.client = new U(M({
130
129
  baseURL: e,
131
130
  apiKey: t
132
131
  }));
@@ -136,17 +135,17 @@ class S extends E {
136
135
  for (let e = 0; e < r.length; e++) {
137
136
  const t = r[e];
138
137
  if (t.role === "assistant" && t.tool_calls) {
139
- const l = t.tool_calls.map((n) => ({
138
+ const m = t.tool_calls.map((n) => ({
140
139
  role: "tool",
141
140
  id: n.id,
142
141
  name: n.function.name,
143
142
  args: w(n.function.arguments, {}),
144
143
  timestamp: t.timestamp
145
144
  }));
146
- r.splice(e, 1, ...l), e += l.length - 1;
145
+ r.splice(e, 1, ...m), e += m.length - 1;
147
146
  } else if (t.role === "tool" && t.content) {
148
- const l = r.find((n) => t.tool_call_id == n.id);
149
- l && (t.content.includes('"error":') ? l.error = t.content : l.content = t.content), r.splice(e, 1), e--;
147
+ const m = r.find((n) => t.tool_call_id == n.id);
148
+ m && (t.content.includes('"error":') ? m.error = t.content : m.content = t.content), r.splice(e, 1), e--;
150
149
  }
151
150
  r[e]?.timestamp || (r[e].timestamp = Date.now());
152
151
  }
@@ -167,7 +166,7 @@ class S extends E {
167
166
  content: t.error || t.content
168
167
  });
169
168
  else {
170
- const { timestamp: l, ...n } = t;
169
+ const { timestamp: m, ...n } = t;
171
170
  e.push(n);
172
171
  }
173
172
  return e;
@@ -175,49 +174,49 @@ class S extends E {
175
174
  }
176
175
  ask(r, e = {}) {
177
176
  const t = new AbortController();
178
- return Object.assign(new Promise(async (l, n) => {
177
+ return Object.assign(new Promise(async (m, n) => {
179
178
  e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
180
179
  let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
181
- const o = e.tools || this.ai.options.llm?.tools || [], c = {
180
+ const i = e.tools || this.ai.options.llm?.tools || [], l = {
182
181
  model: e.model || this.model,
183
182
  messages: s,
184
183
  stream: !!e.stream,
185
184
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
186
185
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
187
- tools: o.map((i) => ({
186
+ tools: i.map((o) => ({
188
187
  type: "function",
189
188
  function: {
190
- name: i.name,
191
- description: i.description,
189
+ name: o.name,
190
+ description: o.description,
192
191
  parameters: {
193
192
  type: "object",
194
- properties: i.args ? j(i.args, (d, p) => ({ ...p, required: void 0 })) : {},
195
- required: i.args ? Object.entries(i.args).filter((d) => d[1].required).map((d) => d[0]) : []
193
+ properties: o.args ? j(o.args, (c, p) => ({ ...p, required: void 0 })) : {},
194
+ required: o.args ? Object.entries(o.args).filter((c) => c[1].required).map((c) => c[0]) : []
196
195
  }
197
196
  }
198
197
  }))
199
198
  };
200
- let a, m = !0;
199
+ let d, a = !0;
201
200
  do {
202
- if (a = await this.client.chat.completions.create(c).catch((d) => {
203
- throw d.message += `
201
+ if (d = await this.client.chat.completions.create(l).catch((c) => {
202
+ throw c.message += `
204
203
 
205
204
  Messages:
206
- ${JSON.stringify(s, null, 2)}`, d;
205
+ ${JSON.stringify(s, null, 2)}`, c;
207
206
  }), e.stream) {
208
- m ? m = !1 : e.stream({ text: `
207
+ a ? a = !1 : e.stream({ text: `
209
208
 
210
- ` }), a.choices = [{ message: { content: "", tool_calls: [] } }];
211
- for await (const d of a) {
209
+ ` }), d.choices = [{ message: { content: "", tool_calls: [] } }];
210
+ for await (const c of d) {
212
211
  if (t.signal.aborted) break;
213
- d.choices[0].delta.content && (a.choices[0].message.content += d.choices[0].delta.content, e.stream({ text: d.choices[0].delta.content })), d.choices[0].delta.tool_calls && (a.choices[0].message.tool_calls = d.choices[0].delta.tool_calls);
212
+ c.choices[0].delta.content && (d.choices[0].message.content += c.choices[0].delta.content, e.stream({ text: c.choices[0].delta.content })), c.choices[0].delta.tool_calls && (d.choices[0].message.tool_calls = c.choices[0].delta.tool_calls);
214
213
  }
215
214
  }
216
- const i = a.choices[0].message.tool_calls || [];
217
- if (i.length && !t.signal.aborted) {
218
- s.push(a.choices[0].message);
219
- const d = await Promise.all(i.map(async (p) => {
220
- const u = o?.find(T("name", p.function.name));
215
+ const o = d.choices[0].message.tool_calls || [];
216
+ if (o.length && !t.signal.aborted) {
217
+ s.push(d.choices[0].message);
218
+ const c = await Promise.all(o.map(async (p) => {
219
+ const u = i?.find(T("name", p.function.name));
221
220
  if (e.stream && e.stream({ tool: p.function.name }), !u) return { role: "tool", tool_call_id: p.id, content: '{"error": "Tool not found"}' };
222
221
  try {
223
222
  const f = w(p.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
@@ -226,10 +225,10 @@ ${JSON.stringify(s, null, 2)}`, d;
226
225
  return { role: "tool", tool_call_id: p.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
227
226
  }
228
227
  }));
229
- s.push(...d), c.messages = s;
228
+ s.push(...c), l.messages = s;
230
229
  }
231
- } while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
232
- s.push({ role: "assistant", content: a.choices[0].message.content || "" }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), l(s.at(-1)?.content);
230
+ } while (!t.signal.aborted && d.choices?.[0]?.message?.tool_calls?.length);
231
+ s.push({ role: "assistant", content: d.choices[0].message.content || "" }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), m(s.at(-1)?.content);
233
232
  }), { abort: () => t.abort() });
234
233
  }
235
234
  }
@@ -250,22 +249,22 @@ class B {
250
249
  ask(r, e = {}) {
251
250
  const t = e.model || this.defaultModel;
252
251
  if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
253
- let l = () => {
252
+ let m = () => {
254
253
  };
255
254
  return Object.assign(new Promise(async (n) => {
256
255
  if (e.history || (e.history = []), e.memory) {
257
256
  e.system = (e.system || "") + `
258
257
  You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
259
258
  `;
260
- const o = async (a, m, i = 50) => {
261
- const [d, p] = await Promise.all([
262
- m ? this.embedding(m) : Promise.resolve(null),
263
- a ? this.embedding(a) : Promise.resolve(null)
259
+ const i = async (d, a, o = 50) => {
260
+ const [c, p] = await Promise.all([
261
+ a ? this.embedding(a) : Promise.resolve(null),
262
+ d ? this.embedding(d) : Promise.resolve(null)
264
263
  ]);
265
- return (e.memory || []).map((u) => ({ ...u, score: d ? this.cosineSimilarity(u.embeddings[0], d[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: p ? this.cosineSimilarity(u.embeddings[1], p[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, i);
266
- }, c = await o(r);
267
- c.length && e.history.push({ role: "assistant", content: `Things I remembered:
268
- ` + c.map((a) => `${a.owner}: ${a.fact}`).join(`
264
+ return (e.memory || []).map((u) => ({ ...u, score: c ? this.cosineSimilarity(u.embeddings[0], c[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: p ? this.cosineSimilarity(u.embeddings[1], p[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, o);
265
+ }, l = await i(r);
266
+ l.length && e.history.push({ role: "assistant", content: `Things I remembered:
267
+ ` + l.map((d) => `${d.owner}: ${d.fact}`).join(`
269
268
  `) }), e.tools = [...e.tools || [], {
270
269
  name: "read_memory",
271
270
  description: "Check your long-term memory for more information",
@@ -274,32 +273,32 @@ You have passive persistent memory never make any mention of your memory capabil
274
273
  query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
275
274
  limit: { type: "number", description: "Result limit, default 5" }
276
275
  },
277
- fn: (a) => {
278
- if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
279
- return o(a.query, a.subject, a.limit || 5);
276
+ fn: (d) => {
277
+ if (!d.subject && !d.query) throw new Error("Either a subject or query argument is required");
278
+ return i(d.query, d.subject, d.limit || 5);
280
279
  }
281
280
  }];
282
281
  }
283
282
  const s = await this.models[t].ask(r, e);
284
283
  if (e.memory) {
285
- const o = e.history?.findIndex((c) => c.role == "assistant" && c.content.startsWith("Things I remembered:"));
286
- o != null && o >= 0 && e.history?.splice(o, 1);
284
+ const i = e.history?.findIndex((l) => l.role == "assistant" && l.content.startsWith("Things I remembered:"));
285
+ i != null && i >= 0 && e.history?.splice(i, 1);
287
286
  }
288
287
  if (e.compress || e.memory) {
289
- let o = null;
288
+ let i = null;
290
289
  if (e.compress)
291
- o = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...o.history);
290
+ i = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...i.history);
292
291
  else {
293
- const c = e.history?.findLastIndex((a) => a.role == "user") ?? -1;
294
- o = await this.ai.language.compressHistory(c != -1 ? e.history.slice(c) : e.history, 0, 0, e);
292
+ const l = e.history?.findLastIndex((d) => d.role == "user") ?? -1;
293
+ i = await this.ai.language.compressHistory(l != -1 ? e.history.slice(l) : e.history, 0, 0, e);
295
294
  }
296
295
  if (e.memory) {
297
- const c = e.memory.filter((a) => !o.memory.some((m) => this.cosineSimilarity(a.embeddings[1], m.embeddings[1]) > 0.8)).concat(o.memory);
298
- e.memory.splice(0, e.memory.length, ...c);
296
+ const l = e.memory.filter((d) => !i.memory.some((a) => this.cosineSimilarity(d.embeddings[1], a.embeddings[1]) > 0.8)).concat(i.memory);
297
+ e.memory.splice(0, e.memory.length, ...l);
299
298
  }
300
299
  }
301
300
  return n(s);
302
- }), { abort: l });
301
+ }), { abort: m });
303
302
  }
304
303
  /**
305
304
  * Compress chat history to reduce context size
@@ -309,24 +308,24 @@ You have passive persistent memory never make any mention of your memory capabil
309
308
  * @param {LLMRequest} options LLM options
310
309
  * @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
311
310
  */
312
- async compressHistory(r, e, t, l) {
311
+ async compressHistory(r, e, t, m) {
313
312
  if (this.estimateTokens(r) < e) return { history: r, memory: [] };
314
313
  let n = 0, s = 0;
315
314
  for (let u of r.toReversed())
316
315
  if (s += this.estimateTokens(u.content), s < t) n++;
317
316
  else break;
318
317
  if (r.length <= n) return { history: r, memory: [] };
319
- const o = r[0].role == "system" ? r[0] : null, c = n == 0 ? [] : r.slice(-n), a = (n == 0 ? r : r.slice(0, -n)).filter((u) => u.role === "assistant" || u.role === "user"), m = await this.json(a.map((u) => `${u.role}: ${u.content}`).join(`
318
+ const i = r[0].role == "system" ? r[0] : null, l = n == 0 ? [] : r.slice(-n), d = (n == 0 ? r : r.slice(0, -n)).filter((u) => u.role === "assistant" || u.role === "user"), a = await this.json(d.map((u) => `${u.role}: ${u.content}`).join(`
320
319
 
321
320
  `), "{summary: string, facts: [[subject, fact]]}", {
322
321
  system: "Create the smallest summary possible, no more than 500 tokens. Create a list of NEW facts (split by subject [pro]noun and fact) about what you learned from this conversation that you didn't already know or get from a tool call or system prompt. Focus only on new information about people, topics, or facts. Avoid generating facts about the AI.",
323
- model: l?.model,
324
- temperature: l?.temperature || 0.3
325
- }), i = /* @__PURE__ */ new Date(), d = await Promise.all((m?.facts || [])?.map(async ([u, f]) => {
322
+ model: m?.model,
323
+ temperature: m?.temperature || 0.3
324
+ }), o = /* @__PURE__ */ new Date(), c = await Promise.all((a?.facts || [])?.map(async ([u, f]) => {
326
325
  const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
327
- return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: i };
328
- })), p = [{ role: "assistant", content: `Conversation Summary: ${m?.summary}`, timestamp: Date.now() }, ...c];
329
- return o && p.splice(0, 0, o), { history: p, memory: d };
326
+ return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: o };
327
+ })), p = [{ role: "assistant", content: `Conversation Summary: ${a?.summary}`, timestamp: Date.now() }, ...l];
328
+ return i && p.splice(0, 0, i), { history: p, memory: c };
330
329
  }
331
330
  /**
332
331
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -336,10 +335,10 @@ You have passive persistent memory never make any mention of your memory capabil
336
335
  */
337
336
  cosineSimilarity(r, e) {
338
337
  if (r.length !== e.length) throw new Error("Vectors must be same length");
339
- let t = 0, l = 0, n = 0;
340
- for (let o = 0; o < r.length; o++)
341
- t += r[o] * e[o], l += r[o] * r[o], n += e[o] * e[o];
342
- const s = Math.sqrt(l) * Math.sqrt(n);
338
+ let t = 0, m = 0, n = 0;
339
+ for (let i = 0; i < r.length; i++)
340
+ t += r[i] * e[i], m += r[i] * r[i], n += e[i] * e[i];
341
+ const s = Math.sqrt(m) * Math.sqrt(n);
343
342
  return s === 0 ? 0 : t / s;
344
343
  }
345
344
  /**
@@ -350,25 +349,25 @@ You have passive persistent memory never make any mention of your memory capabil
350
349
  * @returns {string[]} Chunked strings
351
350
  */
352
351
  chunk(r, e = 500, t = 50) {
353
- const l = (c, a = "") => c ? Object.entries(c).flatMap(([m, i]) => {
354
- const d = a ? `${a}${isNaN(+m) ? `.${m}` : `[${m}]`}` : m;
355
- return typeof i == "object" && !Array.isArray(i) ? l(i, d) : `${d}: ${Array.isArray(i) ? i.join(", ") : i}`;
356
- }) : [], s = (typeof r == "object" ? l(r) : r.split(`
357
- `)).flatMap((c) => [...c.split(/\s+/).filter(Boolean), `
358
- `]), o = [];
359
- for (let c = 0; c < s.length; ) {
360
- let a = "", m = c;
361
- for (; m < s.length; ) {
362
- const d = a + (a ? " " : "") + s[m];
363
- if (this.estimateTokens(d.replace(/\s*\n\s*/g, `
364
- `)) > e && a) break;
365
- a = d, m++;
352
+ const m = (l, d = "") => l ? Object.entries(l).flatMap(([a, o]) => {
353
+ const c = d ? `${d}${isNaN(+a) ? `.${a}` : `[${a}]`}` : a;
354
+ return typeof o == "object" && !Array.isArray(o) ? m(o, c) : `${c}: ${Array.isArray(o) ? o.join(", ") : o}`;
355
+ }) : [], s = (typeof r == "object" ? m(r) : r.split(`
356
+ `)).flatMap((l) => [...l.split(/\s+/).filter(Boolean), `
357
+ `]), i = [];
358
+ for (let l = 0; l < s.length; ) {
359
+ let d = "", a = l;
360
+ for (; a < s.length; ) {
361
+ const c = d + (d ? " " : "") + s[a];
362
+ if (this.estimateTokens(c.replace(/\s*\n\s*/g, `
363
+ `)) > e && d) break;
364
+ d = c, a++;
366
365
  }
367
- const i = a.replace(/\s*\n\s*/g, `
366
+ const o = d.replace(/\s*\n\s*/g, `
368
367
  `).trim();
369
- i && o.push(i), c = Math.max(m - t, m === c ? c + 1 : m);
368
+ o && i.push(o), l = Math.max(a - t, a === l ? l + 1 : a);
370
369
  }
371
- return o;
370
+ return i;
372
371
  }
373
372
  /**
374
373
  * Create a vector representation of a string
@@ -377,39 +376,39 @@ You have passive persistent memory never make any mention of your memory capabil
377
376
  * @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
378
377
  */
379
378
  embedding(r, e = {}) {
380
- let { maxTokens: t = 500, overlapTokens: l = 50 } = e, n = !1;
379
+ let { maxTokens: t = 500, overlapTokens: m = 50 } = e, n = !1;
381
380
  const s = () => {
382
381
  n = !0;
383
- }, o = (a) => new Promise((m, i) => {
384
- if (n) return i(new Error("Aborted"));
385
- const d = [
382
+ }, i = (d) => new Promise((a, o) => {
383
+ if (n) return o(new Error("Aborted"));
384
+ const c = [
386
385
  N(L(z(import.meta.url)), "embedder.js"),
387
386
  this.ai.options.path,
388
387
  this.ai.options?.embedder || "bge-small-en-v1.5"
389
- ], p = g("node", d, { stdio: ["pipe", "pipe", "ignore"] });
390
- p.stdin.write(a), p.stdin.end();
388
+ ], p = g("node", c, { stdio: ["pipe", "pipe", "ignore"] });
389
+ p.stdin.write(d), p.stdin.end();
391
390
  let u = "";
392
391
  p.stdout.on("data", (f) => u += f.toString()), p.on("close", (f) => {
393
- if (n) return i(new Error("Aborted"));
392
+ if (n) return o(new Error("Aborted"));
394
393
  if (f === 0)
395
394
  try {
396
395
  const y = JSON.parse(u);
397
- m(y.embedding);
396
+ a(y.embedding);
398
397
  } catch {
399
- i(new Error("Failed to parse embedding output"));
398
+ o(new Error("Failed to parse embedding output"));
400
399
  }
401
400
  else
402
- i(new Error(`Embedder process exited with code ${f}`));
403
- }), p.on("error", i);
404
- }), c = (async () => {
405
- const a = this.chunk(r, t, l), m = [];
406
- for (let i = 0; i < a.length && !n; i++) {
407
- const d = a[i], p = await o(d);
408
- m.push({ index: i, embedding: p, text: d, tokens: this.estimateTokens(d) });
401
+ o(new Error(`Embedder process exited with code ${f}`));
402
+ }), p.on("error", o);
403
+ }), l = (async () => {
404
+ const d = this.chunk(r, t, m), a = [];
405
+ for (let o = 0; o < d.length && !n; o++) {
406
+ const c = d[o], p = await i(c);
407
+ a.push({ index: o, embedding: p, text: c, tokens: this.estimateTokens(c) });
409
408
  }
410
- return m;
409
+ return a;
411
410
  })();
412
- return Object.assign(c, { abort: s });
411
+ return Object.assign(l, { abort: s });
413
412
  }
414
413
  /**
415
414
  * Estimate variable as tokens
@@ -428,8 +427,8 @@ You have passive persistent memory never make any mention of your memory capabil
428
427
  */
429
428
  fuzzyMatch(r, ...e) {
430
429
  if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
431
- const t = (s, o = 10) => s.toLowerCase().split("").map((c, a) => c.charCodeAt(0) * (a + 1) % o / o).slice(0, o), l = t(r), n = e.map((s) => t(s)).map((s) => this.cosineSimilarity(l, s));
432
- return { avg: n.reduce((s, o) => s + o, 0) / n.length, max: Math.max(...n), similarities: n };
430
+ const t = (s, i = 10) => s.toLowerCase().split("").map((l, d) => l.charCodeAt(0) * (d + 1) % i / i).slice(0, i), m = t(r), n = e.map((s) => t(s)).map((s) => this.cosineSimilarity(m, s));
431
+ return { avg: n.reduce((s, i) => s + i, 0) / n.length, max: Math.max(...n), similarities: n };
433
432
  }
434
433
  /**
435
434
  * Ask a question with JSON response
@@ -439,13 +438,13 @@ You have passive persistent memory never make any mention of your memory capabil
439
438
  * @returns {Promise<{} | {} | RegExpExecArray | null>}
440
439
  */
441
440
  async json(r, e, t) {
442
- let l = await this.ask(r, { ...t, system: (t?.system ? `${t.system}
441
+ let m = await this.ask(r, { ...t, system: (t?.system ? `${t.system}
443
442
  ` : "") + `Only respond using a JSON code block matching this schema:
444
443
  \`\`\`json
445
444
  ${e}
446
445
  \`\`\`` });
447
- if (!l) return {};
448
- const n = /```(?:.+)?\s*([\s\S]*?)```/.exec(l), s = n ? n[1].trim() : l;
446
+ if (!m) return {};
447
+ const n = /```(?:.+)?\s*([\s\S]*?)```/.exec(m), s = n ? n[1].trim() : m;
449
448
  return w(s, {});
450
449
  }
451
450
  /**
@@ -483,96 +482,98 @@ print(json.dumps(segments))
483
482
  whisperModel;
484
483
  runAsr(r, e = {}) {
485
484
  let t;
486
- const l = new Promise((n, s) => {
487
- this.downloadAsrModel(e.model).then((o) => {
488
- let c = "";
489
- const a = [e.diarization ? "-owts" : "-nt", "-m", o, "-f", r];
490
- t = g(this.ai.options.whisper, a, { stdio: ["ignore", "pipe", "ignore"] }), t.on("error", (m) => s(m)), t.stdout.on("data", (m) => c += m.toString()), t.on("close", (m) => {
491
- if (m === 0)
485
+ const m = new Promise((n, s) => {
486
+ this.downloadAsrModel(e.model).then((i) => {
487
+ let l = "";
488
+ const d = [e.diarization ? "-owts" : "-nt", "-m", i, "-f", r];
489
+ console.log(this.ai.options.whisper + " " + d.join(" ")), t = g(this.ai.options.whisper, d, { stdio: ["ignore", "pipe", "ignore"] }), t.on("error", (a) => s(a)), t.stdout.on("data", (a) => l += a.toString()), t.on("close", (a) => {
490
+ if (a === 0)
492
491
  if (e.diarization)
493
492
  try {
494
- n(JSON.parse(c));
493
+ n(JSON.parse(l));
495
494
  } catch {
496
495
  s(new Error("Failed to parse whisper JSON"));
497
496
  }
498
497
  else
499
- n(c.trim() || null);
498
+ n(l.trim() || null);
500
499
  else
501
- s(new Error(`Exit code ${m}`));
500
+ s(new Error(`Exit code ${a}`));
502
501
  });
503
502
  });
504
503
  });
505
- return Object.assign(l, { abort: () => t?.kill("SIGTERM") });
504
+ return Object.assign(m, { abort: () => t?.kill("SIGTERM") });
506
505
  }
507
506
  runDiarization(r) {
508
507
  let e = !1, t = () => {
509
508
  e = !0;
510
509
  };
511
- const l = (s) => new Promise((o) => {
512
- const c = g(s, ["-c", "import pyannote.audio"]);
513
- c.on("close", (a) => o(a === 0)), c.on("error", () => o(!1));
510
+ const m = (s) => new Promise((i) => {
511
+ const l = g(s, ["-c", "import pyannote.audio"]);
512
+ l.on("close", (d) => i(d === 0)), l.on("error", () => i(!1));
514
513
  }), n = Promise.all([
515
- l("python"),
516
- l("python3")
517
- ]).then((async ([s, o]) => {
514
+ m("python"),
515
+ m("python3")
516
+ ]).then((async ([s, i]) => {
518
517
  if (e) return;
519
- if (!s && !o) throw new Error("Pyannote is not installed: pip install pyannote.audio");
520
- const c = o ? "python3" : "python";
521
- let a = null;
522
- return new Promise((m, i) => {
523
- if (a = x(J(x(P(), "audio-")), "converted.wav"), C(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${a}"`, { stdio: "ignore" }), e) return;
524
- let d = "";
525
- const p = g(c, ["-c", this.pyannote, a]);
526
- p.stdout.on("data", (u) => d += u.toString()), p.stderr.on("data", (u) => console.error(u.toString())), p.on("close", (u) => {
527
- if (u === 0)
518
+ if (!s && !i) throw new Error("Pyannote is not installed: pip install pyannote.audio");
519
+ const l = i ? "python3" : "python";
520
+ return new Promise((d, a) => {
521
+ if (e) return;
522
+ let o = "";
523
+ const c = g(l, ["-c", this.pyannote, r]);
524
+ c.stdout.on("data", (p) => o += p.toString()), c.stderr.on("data", (p) => console.error(p.toString())), c.on("close", (p) => {
525
+ if (p === 0)
528
526
  try {
529
- m(JSON.parse(d));
527
+ d(JSON.parse(o));
530
528
  } catch {
531
- i(new Error("Failed to parse diarization output"));
529
+ a(new Error("Failed to parse diarization output"));
532
530
  }
533
531
  else
534
- i(new Error(`Python process exited with code ${u}`));
535
- }), p.on("error", i), t = () => p.kill("SIGTERM");
536
- }).finally(() => {
537
- a && W(_.dirname(a), { recursive: !0, force: !0 });
532
+ a(new Error(`Python process exited with code ${p}`));
533
+ }), c.on("error", a), t = () => c.kill("SIGTERM");
538
534
  });
539
535
  }));
540
536
  return Object.assign(n, { abort: t });
541
537
  }
542
538
  combineSpeakerTranscript(r, e) {
543
539
  const t = /* @__PURE__ */ new Map();
544
- let l = 0;
545
- e.forEach((c) => {
546
- t.has(c.speaker) || t.set(c.speaker, ++l);
540
+ let m = 0;
541
+ e.forEach((l) => {
542
+ t.has(l.speaker) || t.set(l.speaker, ++m);
547
543
  });
548
544
  const n = [];
549
- let s = -1, o = "";
550
- return r.transcription.forEach((c) => {
551
- const a = c.offsets.from / 1e3, m = e.find((d) => a >= d.start && a <= d.end), i = m ? t.get(m.speaker) : 1;
552
- i !== s ? (o && n.push(`[Speaker ${s}]: ${o.trim()}`), s = i, o = c.text) : o += " " + c.text;
553
- }), o && n.push(`[Speaker ${s}]: ${o.trim()}`), n.join(`
545
+ let s = -1, i = "";
546
+ return r.transcription.forEach((l) => {
547
+ const d = l.offsets.from / 1e3, a = e.find((c) => d >= c.start && d <= c.end), o = a ? t.get(a.speaker) : 1;
548
+ o !== s ? (i && n.push(`[Speaker ${s}]: ${i.trim()}`), s = o, i = l.text) : i += " " + l.text;
549
+ }), i && n.push(`[Speaker ${s}]: ${i.trim()}`), n.join(`
554
550
  `);
555
551
  }
556
552
  asr(r, e = {}) {
557
553
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
558
- const t = this.runAsr(r, { model: e.model, diarization: !!e.diarization }), l = e.diarization ? this.runDiarization(r) : Promise.resolve(null), n = () => {
559
- t.abort(), l?.abort?.();
560
- }, s = Promise.all([t, l]).then(async ([o, c]) => {
561
- if (!e.diarization) return o;
562
- if (o = this.combineSpeakerTranscript(o, c), e.diarization === "id") {
554
+ const t = x(J(x(P(), "audio-")), "converted.wav");
555
+ C(`ffmpeg -i "${r}" -ar 16000 -ac 1 -f wav "${t}"`, { stdio: "ignore" });
556
+ const m = () => W(_.dirname(t), { recursive: !0, force: !0 }).catch(() => {
557
+ }), n = this.runAsr(t, { model: e.model, diarization: !!e.diarization }), s = e.diarization ? this.runDiarization(t) : Promise.resolve(null);
558
+ let i = !1, l = () => {
559
+ i = !0, n.abort(), s?.abort?.(), m();
560
+ };
561
+ const d = Promise.all([n, s]).then(async ([a, o]) => {
562
+ if (i || !e.diarization) return a;
563
+ if (a = this.combineSpeakerTranscript(a, o), !i && e.diarization === "id") {
563
564
  if (!this.ai.language.defaultModel) throw new Error("Configure an LLM for advanced ASR speaker detection");
564
- let a = this.ai.language.chunk(o, 500, 0);
565
- a.length > 4 && (a = [...a.slice(0, 3), a.at(-1)]);
566
- const m = await this.ai.language.json(a.join(`
565
+ let c = this.ai.language.chunk(a, 500, 0);
566
+ c.length > 4 && (c = [...c.slice(0, 3), c.at(-1)]);
567
+ const p = await this.ai.language.json(c.join(`
567
568
  `), '{1: "Detected Name", 2: "Second Name"}', {
568
569
  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",
569
570
  temperature: 0.1
570
571
  });
571
- Object.entries(m).forEach(([i, d]) => o = o.replaceAll(`[Speaker ${i}]`, `[${d}]`));
572
+ Object.entries(p).forEach(([u, f]) => a = a.replaceAll(`[Speaker ${u}]`, `[${f}]`));
572
573
  }
573
- return o;
574
- });
575
- return Object.assign(s, { abort: n });
574
+ return a;
575
+ }).finally(() => m());
576
+ return Object.assign(d, { abort: l });
576
577
  }
577
578
  async downloadAsrModel(r = this.whisperModel) {
578
579
  if (!this.ai.options.whisper) throw new Error("Whisper not configured");
@@ -592,15 +593,15 @@ class V {
592
593
  */
593
594
  ocr(r) {
594
595
  let e;
595
- const t = new Promise(async (l) => {
596
+ const t = new Promise(async (m) => {
596
597
  e = await D(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
597
598
  const { data: n } = await e.recognize(r);
598
- await e.terminate(), l(n.text.trim() || null);
599
+ await e.terminate(), m(n.text.trim() || null);
599
600
  });
600
601
  return Object.assign(t, { abort: () => e?.terminate() });
601
602
  }
602
603
  }
603
- class ue {
604
+ class de {
604
605
  constructor(r) {
605
606
  this.options = r, r.path || (r.path = $.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new K(this), this.language = new B(this), this.vision = new V(this);
606
607
  }
@@ -616,12 +617,12 @@ const Y = {
616
617
  description: "Use the command line interface, returns any output",
617
618
  args: { command: { type: "string", description: "Command to run", required: !0 } },
618
619
  fn: (h) => F`${h.command}`
619
- }, pe = {
620
+ }, ue = {
620
621
  name: "get_datetime",
621
622
  description: "Get current UTC date / time",
622
623
  args: {},
623
624
  fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
624
- }, he = {
625
+ }, pe = {
625
626
  name: "exec",
626
627
  description: "Run code/scripts",
627
628
  args: {
@@ -642,7 +643,7 @@ const Y = {
642
643
  return { error: t?.message || t.toString() };
643
644
  }
644
645
  }
645
- }, fe = {
646
+ }, he = {
646
647
  name: "fetch",
647
648
  description: "Make HTTP request to URL",
648
649
  args: {
@@ -669,7 +670,7 @@ const Y = {
669
670
  code: { type: "string", description: "CommonJS javascript", required: !0 }
670
671
  },
671
672
  fn: async (h) => ({ result: H`python -c "${h.code}"` })
672
- }, ye = {
673
+ }, fe = {
673
674
  name: "read_webpage",
674
675
  description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
675
676
  args: {
@@ -685,18 +686,18 @@ const Y = {
685
686
  title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
686
687
  description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
687
688
  };
688
- let l = "";
689
+ let m = "";
689
690
  const n = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
690
691
  for (const s of n) {
691
- const o = e(s).first();
692
- if (o.length && o.text().trim().length > 200) {
693
- l = o.text();
692
+ const i = e(s).first();
693
+ if (i.length && i.text().trim().length > 200) {
694
+ m = i.text();
694
695
  break;
695
696
  }
696
697
  }
697
- return l || (l = e("body").text()), l = l.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: l, focus: h.focus };
698
+ return m || (m = e("body").text()), m = m.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: m, focus: h.focus };
698
699
  }
699
- }, ge = {
700
+ }, ye = {
700
701
  name: "web_search",
701
702
  description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
702
703
  args: {
@@ -708,28 +709,28 @@ const Y = {
708
709
  headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
709
710
  }).then((n) => n.text());
710
711
  let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
711
- const l = new v();
712
+ const m = new v();
712
713
  for (; (e = t.exec(r)) !== null; ) {
713
714
  let n = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
714
- if (n && (n = decodeURIComponent(n)), n && l.add(n), l.size >= (h.length || 5)) break;
715
+ if (n && (n = decodeURIComponent(n)), n && m.add(n), m.size >= (h.length || 5)) break;
715
716
  }
716
- return l;
717
+ return m;
717
718
  }
718
719
  };
719
720
  export {
720
- ue as Ai,
721
+ de as Ai,
721
722
  G as Anthropic,
722
723
  K as Audio,
723
724
  Y as CliTool,
724
- pe as DateTimeTool,
725
- he as ExecTool,
726
- fe as FetchTool,
725
+ ue as DateTimeTool,
726
+ pe as ExecTool,
727
+ he as FetchTool,
727
728
  Q as JSTool,
728
729
  E as LLMProvider,
729
730
  S as OpenAi,
730
731
  X as PythonTool,
731
- ye as ReadWebpageTool,
732
+ fe as ReadWebpageTool,
732
733
  V as Vision,
733
- ge as WebSearchTool
734
+ ye as WebSearchTool
734
735
  };
735
736
  //# sourceMappingURL=index.mjs.map