@ztimson/ai-utils 0.6.4 → 0.6.6

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,38 @@
1
- import * as j from "node:os";
2
- import { objectMap as _, JSONAttemptParse as g, findByProp as x, JSONSanitize as b, clean as T, Http as P, consoleInterceptor as q, fn as $, ASet as M } from "@ztimson/utils";
1
+ import * as S from "node:os";
2
+ import { objectMap as _, JSONAttemptParse as g, findByProp as k, JSONSanitize as b, clean as T, Http as q, consoleInterceptor as M, fn as P, ASet as $ } from "@ztimson/utils";
3
3
  import { Anthropic as E } from "@anthropic-ai/sdk";
4
- import { OpenAI as O } from "openai";
5
- import { Worker as v } from "worker_threads";
6
- import { fileURLToPath as A } from "url";
7
- import { join as R, dirname as U } from "path";
8
- import { spawn as w } from "node:child_process";
9
- import { pipeline as z } from "@xenova/transformers";
10
- import * as D from "node:fs";
11
- import * as L from "wavefile";
4
+ import { OpenAI as A } from "openai";
5
+ import { Worker as x } from "worker_threads";
6
+ import { fileURLToPath as v } from "url";
7
+ import { join as O, dirname as U } from "path";
8
+ import R from "node:path";
9
+ import { canDiarization as L } from "./asr.mjs";
12
10
  import { createWorker as N } from "tesseract.js";
13
11
  import "./embedder.mjs";
14
- import * as W from "cheerio";
15
- import { $ as C, $Sync as I } from "@ztimson/node-utils";
16
- class S {
12
+ import * as C from "cheerio";
13
+ import { $ as W, $Sync as D } from "@ztimson/node-utils";
14
+ class j {
17
15
  }
18
- class H extends S {
16
+ class z extends j {
19
17
  constructor(r, e, t) {
20
18
  super(), this.ai = r, this.apiToken = e, this.model = t, this.client = new E({ apiKey: e });
21
19
  }
22
20
  client;
23
21
  toStandard(r) {
24
22
  const e = Date.now(), t = [];
25
- for (let i of r)
26
- if (typeof i.content == "string")
27
- t.push({ timestamp: e, ...i });
23
+ for (let o of r)
24
+ if (typeof o.content == "string")
25
+ t.push({ timestamp: e, ...o });
28
26
  else {
29
- const n = i.content?.filter((s) => s.type == "text").map((s) => s.text).join(`
27
+ const s = o.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
30
28
 
31
29
  `);
32
- n && t.push({ timestamp: e, role: i.role, content: n }), i.content.forEach((s) => {
33
- if (s.type == "tool_use")
34
- t.push({ timestamp: e, role: "tool", id: s.id, name: s.name, args: s.input, content: void 0 });
35
- else if (s.type == "tool_result") {
36
- const o = t.findLast((a) => a.id == s.tool_use_id);
37
- o && (o[s.is_error ? "error" : "content"] = s.content);
30
+ s && t.push({ timestamp: e, role: o.role, content: s }), o.content.forEach((n) => {
31
+ if (n.type == "tool_use")
32
+ t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
33
+ else if (n.type == "tool_result") {
34
+ const i = t.findLast((m) => m.id == n.tool_use_id);
35
+ i && (i[n.is_error ? "error" : "content"] = n.content);
38
36
  }
39
37
  });
40
38
  }
@@ -55,78 +53,78 @@ class H extends S {
55
53
  }
56
54
  ask(r, e = {}) {
57
55
  const t = new AbortController();
58
- return Object.assign(new Promise(async (i) => {
59
- let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
60
- const s = e.tools || this.ai.options.llm?.tools || [], o = {
56
+ return Object.assign(new Promise(async (o) => {
57
+ let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
58
+ const n = e.tools || this.ai.options.llm?.tools || [], i = {
61
59
  model: e.model || this.model,
62
60
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
63
61
  system: e.system || this.ai.options.llm?.system || "",
64
62
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
65
- tools: s.map((d) => ({
63
+ tools: n.map((d) => ({
66
64
  name: d.name,
67
65
  description: d.description,
68
66
  input_schema: {
69
67
  type: "object",
70
- properties: d.args ? _(d.args, (c, m) => ({ ...m, required: void 0 })) : {},
68
+ properties: d.args ? _(d.args, (c, l) => ({ ...l, required: void 0 })) : {},
71
69
  required: d.args ? Object.entries(d.args).filter((c) => c[1].required).map((c) => c[0]) : []
72
70
  },
73
71
  fn: void 0
74
72
  })),
75
- messages: n,
73
+ messages: s,
76
74
  stream: !!e.stream
77
75
  };
78
- let a, l = !0;
76
+ let m, a = !0;
79
77
  do {
80
- if (a = await this.client.messages.create(o).catch((c) => {
78
+ if (m = await this.client.messages.create(i).catch((c) => {
81
79
  throw c.message += `
82
80
 
83
81
  Messages:
84
- ${JSON.stringify(n, null, 2)}`, c;
82
+ ${JSON.stringify(s, null, 2)}`, c;
85
83
  }), e.stream) {
86
- l ? l = !1 : e.stream({ text: `
84
+ a ? a = !1 : e.stream({ text: `
87
85
 
88
- ` }), a.content = [];
89
- for await (const c of a) {
86
+ ` }), m.content = [];
87
+ for await (const c of m) {
90
88
  if (t.signal.aborted) break;
91
89
  if (c.type === "content_block_start")
92
- c.content_block.type === "text" ? a.content.push({ type: "text", text: "" }) : c.content_block.type === "tool_use" && a.content.push({ type: "tool_use", id: c.content_block.id, name: c.content_block.name, input: "" });
90
+ c.content_block.type === "text" ? m.content.push({ type: "text", text: "" }) : c.content_block.type === "tool_use" && m.content.push({ type: "tool_use", id: c.content_block.id, name: c.content_block.name, input: "" });
93
91
  else if (c.type === "content_block_delta")
94
92
  if (c.delta.type === "text_delta") {
95
- const m = c.delta.text;
96
- a.content.at(-1).text += m, e.stream({ text: m });
97
- } else c.delta.type === "input_json_delta" && (a.content.at(-1).input += c.delta.partial_json);
93
+ const l = c.delta.text;
94
+ m.content.at(-1).text += l, e.stream({ text: l });
95
+ } else c.delta.type === "input_json_delta" && (m.content.at(-1).input += c.delta.partial_json);
98
96
  else if (c.type === "content_block_stop") {
99
- const m = a.content.at(-1);
100
- m.input != null && (m.input = m.input ? g(m.input, {}) : {});
97
+ const l = m.content.at(-1);
98
+ l.input != null && (l.input = l.input ? g(l.input, {}) : {});
101
99
  } else if (c.type === "message_stop")
102
100
  break;
103
101
  }
104
102
  }
105
- const d = a.content.filter((c) => c.type === "tool_use");
103
+ const d = m.content.filter((c) => c.type === "tool_use");
106
104
  if (d.length && !t.signal.aborted) {
107
- n.push({ role: "assistant", content: a.content });
108
- const c = await Promise.all(d.map(async (m) => {
109
- const h = s.find(x("name", m.name));
110
- if (e.stream && e.stream({ tool: m.name }), !h) return { tool_use_id: m.id, is_error: !0, content: "Tool not found" };
105
+ s.push({ role: "assistant", content: m.content });
106
+ const c = await Promise.all(d.map(async (l) => {
107
+ const p = n.find(k("name", l.name));
108
+ if (e.stream && e.stream({ tool: l.name }), !p) return { tool_use_id: l.id, is_error: !0, content: "Tool not found" };
111
109
  try {
112
- const u = await h.fn(m.input, e?.stream, this.ai);
113
- return { type: "tool_result", tool_use_id: m.id, content: b(u) };
110
+ const u = await p.fn(l.input, e?.stream, this.ai);
111
+ return { type: "tool_result", tool_use_id: l.id, content: b(u) };
114
112
  } catch (u) {
115
- return { type: "tool_result", tool_use_id: m.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
113
+ return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
116
114
  }
117
115
  }));
118
- n.push({ role: "user", content: c }), o.messages = n;
116
+ s.push({ role: "user", content: c }), i.messages = s;
119
117
  }
120
- } while (!t.signal.aborted && a.content.some((d) => d.type === "tool_use"));
121
- n.push({ role: "assistant", content: a.content.filter((d) => d.type == "text").map((d) => d.text).join(`
118
+ } while (!t.signal.aborted && m.content.some((d) => d.type === "tool_use"));
119
+ s.push({ role: "assistant", content: m.content.filter((d) => d.type == "text").map((d) => d.text).join(`
122
120
 
123
- `) }), 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);
121
+ `) }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), o(s.at(-1)?.content);
124
122
  }), { abort: () => t.abort() });
125
123
  }
126
124
  }
127
- class k extends S {
128
- constructor(r, e, t, i) {
129
- super(), this.ai = r, this.host = e, this.token = t, this.model = i, this.client = new O(T({
125
+ class w extends j {
126
+ constructor(r, e, t, o) {
127
+ super(), this.ai = r, this.host = e, this.token = t, this.model = o, this.client = new A(T({
130
128
  baseURL: e,
131
129
  apiKey: t
132
130
  }));
@@ -136,17 +134,17 @@ class k extends S {
136
134
  for (let e = 0; e < r.length; e++) {
137
135
  const t = r[e];
138
136
  if (t.role === "assistant" && t.tool_calls) {
139
- const i = t.tool_calls.map((n) => ({
137
+ const o = t.tool_calls.map((s) => ({
140
138
  role: "tool",
141
- id: n.id,
142
- name: n.function.name,
143
- args: g(n.function.arguments, {}),
139
+ id: s.id,
140
+ name: s.function.name,
141
+ args: g(s.function.arguments, {}),
144
142
  timestamp: t.timestamp
145
143
  }));
146
- r.splice(e, 1, ...i), e += i.length - 1;
144
+ r.splice(e, 1, ...o), e += o.length - 1;
147
145
  } else if (t.role === "tool" && t.content) {
148
- const i = r.find((n) => t.tool_call_id == n.id);
149
- i && (t.content.includes('"error":') ? i.error = t.content : i.content = t.content), r.splice(e, 1), e--;
146
+ const o = r.find((s) => t.tool_call_id == s.id);
147
+ o && (t.content.includes('"error":') ? o.error = t.content : o.content = t.content), r.splice(e, 1), e--;
150
148
  }
151
149
  r[e]?.timestamp || (r[e].timestamp = Date.now());
152
150
  }
@@ -167,84 +165,78 @@ class k extends S {
167
165
  content: t.error || t.content
168
166
  });
169
167
  else {
170
- const { timestamp: i, ...n } = t;
171
- e.push(n);
168
+ const { timestamp: o, ...s } = t;
169
+ e.push(s);
172
170
  }
173
171
  return e;
174
172
  }, []);
175
173
  }
176
174
  ask(r, e = {}) {
177
175
  const t = new AbortController();
178
- return Object.assign(new Promise(async (i, n) => {
176
+ return Object.assign(new Promise(async (o, s) => {
179
177
  e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
180
- let s = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
181
- const o = e.tools || this.ai.options.llm?.tools || [], a = {
178
+ let n = this.fromStandard([...e.history || [], { role: "user", content: r, timestamp: Date.now() }]);
179
+ const i = e.tools || this.ai.options.llm?.tools || [], m = {
182
180
  model: e.model || this.model,
183
- messages: s,
181
+ messages: n,
184
182
  stream: !!e.stream,
185
183
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
186
184
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
187
- tools: o.map((c) => ({
185
+ tools: i.map((c) => ({
188
186
  type: "function",
189
187
  function: {
190
188
  name: c.name,
191
189
  description: c.description,
192
190
  parameters: {
193
191
  type: "object",
194
- properties: c.args ? _(c.args, (m, h) => ({ ...h, required: void 0 })) : {},
195
- required: c.args ? Object.entries(c.args).filter((m) => m[1].required).map((m) => m[0]) : []
192
+ properties: c.args ? _(c.args, (l, p) => ({ ...p, required: void 0 })) : {},
193
+ required: c.args ? Object.entries(c.args).filter((l) => l[1].required).map((l) => l[0]) : []
196
194
  }
197
195
  }
198
196
  }))
199
197
  };
200
- let l, d = !0;
198
+ let a, d = !0;
201
199
  do {
202
- if (l = await this.client.chat.completions.create(a).catch((m) => {
203
- throw m.message += `
200
+ if (a = await this.client.chat.completions.create(m).catch((l) => {
201
+ throw l.message += `
204
202
 
205
203
  Messages:
206
- ${JSON.stringify(s, null, 2)}`, m;
204
+ ${JSON.stringify(n, null, 2)}`, l;
207
205
  }), e.stream) {
208
206
  d ? d = !1 : e.stream({ text: `
209
207
 
210
- ` }), l.choices = [{ message: { content: "", tool_calls: [] } }];
211
- for await (const m of l) {
208
+ ` }), a.choices = [{ message: { content: "", tool_calls: [] } }];
209
+ for await (const l of a) {
212
210
  if (t.signal.aborted) break;
213
- m.choices[0].delta.content && (l.choices[0].message.content += m.choices[0].delta.content, e.stream({ text: m.choices[0].delta.content })), m.choices[0].delta.tool_calls && (l.choices[0].message.tool_calls = m.choices[0].delta.tool_calls);
211
+ l.choices[0].delta.content && (a.choices[0].message.content += l.choices[0].delta.content, e.stream({ text: l.choices[0].delta.content })), l.choices[0].delta.tool_calls && (a.choices[0].message.tool_calls = l.choices[0].delta.tool_calls);
214
212
  }
215
213
  }
216
- const c = l.choices[0].message.tool_calls || [];
214
+ const c = a.choices[0].message.tool_calls || [];
217
215
  if (c.length && !t.signal.aborted) {
218
- s.push(l.choices[0].message);
219
- const m = await Promise.all(c.map(async (h) => {
220
- const u = o?.find(x("name", h.function.name));
221
- if (e.stream && e.stream({ tool: h.function.name }), !u) return { role: "tool", tool_call_id: h.id, content: '{"error": "Tool not found"}' };
216
+ n.push(a.choices[0].message);
217
+ const l = await Promise.all(c.map(async (p) => {
218
+ const u = i?.find(k("name", p.function.name));
219
+ if (e.stream && e.stream({ tool: p.function.name }), !u) return { role: "tool", tool_call_id: p.id, content: '{"error": "Tool not found"}' };
222
220
  try {
223
- const f = g(h.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
224
- return { role: "tool", tool_call_id: h.id, content: b(y) };
221
+ const f = g(p.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
222
+ return { role: "tool", tool_call_id: p.id, content: b(y) };
225
223
  } catch (f) {
226
- return { role: "tool", tool_call_id: h.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
224
+ return { role: "tool", tool_call_id: p.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
227
225
  }
228
226
  }));
229
- s.push(...m), a.messages = s;
227
+ n.push(...l), m.messages = n;
230
228
  }
231
- } while (!t.signal.aborted && l.choices?.[0]?.message?.tool_calls?.length);
232
- s.push({ role: "assistant", content: l.choices[0].message.content || "" }), s = this.toStandard(s), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...s), i(s.at(-1)?.content);
229
+ } while (!t.signal.aborted && a.choices?.[0]?.message?.tool_calls?.length);
230
+ 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), o(n.at(-1)?.content);
233
231
  }), { abort: () => t.abort() });
234
232
  }
235
233
  }
236
- class J {
234
+ class H {
237
235
  constructor(r) {
238
- this.ai = r, this.embedWorker = new v(R(U(A(import.meta.url)), "embedder.js")), this.embedWorker.on("message", ({ id: e, embedding: t }) => {
239
- const i = this.embedQueue.get(e);
240
- i && (i.resolve(t), this.embedQueue.delete(e));
241
- }), r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
242
- this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new H(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new k(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new k(this.ai, t.host || null, t.token, e));
236
+ this.ai = r, r.options.llm?.models && Object.entries(r.options.llm.models).forEach(([e, t]) => {
237
+ this.defaultModel || (this.defaultModel = e), t.proto == "anthropic" ? this.models[e] = new z(this.ai, t.token, e) : t.proto == "ollama" ? this.models[e] = new w(this.ai, t.host, "not-needed", e) : t.proto == "openai" && (this.models[e] = new w(this.ai, t.host || null, t.token, e));
243
238
  });
244
239
  }
245
- embedWorker = null;
246
- embedQueue = /* @__PURE__ */ new Map();
247
- embedId = 0;
248
240
  models = {};
249
241
  defaultModel;
250
242
  /**
@@ -256,22 +248,22 @@ class J {
256
248
  ask(r, e = {}) {
257
249
  const t = e.model || this.defaultModel;
258
250
  if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
259
- let i = () => {
251
+ let o = () => {
260
252
  };
261
- return Object.assign(new Promise(async (n) => {
253
+ return Object.assign(new Promise(async (s) => {
262
254
  if (e.history || (e.history = []), e.memory) {
263
255
  e.system = (e.system || "") + `
264
256
  You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
265
257
  `;
266
- const o = async (l, d, c = 50) => {
267
- const [m, h] = await Promise.all([
258
+ const i = async (a, d, c = 50) => {
259
+ const [l, p] = await Promise.all([
268
260
  d ? this.embedding(d) : Promise.resolve(null),
269
- l ? this.embedding(l) : Promise.resolve(null)
261
+ a ? this.embedding(a) : Promise.resolve(null)
270
262
  ]);
271
- return (e.memory || []).map((u) => ({ ...u, score: m ? this.cosineSimilarity(u.embeddings[0], m[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: h ? this.cosineSimilarity(u.embeddings[1], h[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, c);
272
- }, a = await o(r);
273
- a.length && e.history.push({ role: "assistant", content: `Things I remembered:
274
- ` + a.map((l) => `${l.owner}: ${l.fact}`).join(`
263
+ return (e.memory || []).map((u) => ({ ...u, score: l ? this.cosineSimilarity(u.embeddings[0], l[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, c);
264
+ }, m = await i(r);
265
+ m.length && e.history.push({ role: "assistant", content: `Things I remembered:
266
+ ` + m.map((a) => `${a.owner}: ${a.fact}`).join(`
275
267
  `) }), e.tools = [...e.tools || [], {
276
268
  name: "read_memory",
277
269
  description: "Check your long-term memory for more information",
@@ -280,32 +272,32 @@ You have passive persistent memory never make any mention of your memory capabil
280
272
  query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
281
273
  limit: { type: "number", description: "Result limit, default 5" }
282
274
  },
283
- fn: (l) => {
284
- if (!l.subject && !l.query) throw new Error("Either a subject or query argument is required");
285
- return o(l.query, l.subject, l.limit || 5);
275
+ fn: (a) => {
276
+ if (!a.subject && !a.query) throw new Error("Either a subject or query argument is required");
277
+ return i(a.query, a.subject, a.limit || 5);
286
278
  }
287
279
  }];
288
280
  }
289
- const s = await this.models[t].ask(r, e);
281
+ const n = await this.models[t].ask(r, e);
290
282
  if (e.memory) {
291
- const o = e.history?.findIndex((a) => a.role == "assistant" && a.content.startsWith("Things I remembered:"));
292
- o != null && o >= 0 && e.history?.splice(o, 1);
283
+ const i = e.history?.findIndex((m) => m.role == "assistant" && m.content.startsWith("Things I remembered:"));
284
+ i != null && i >= 0 && e.history?.splice(i, 1);
293
285
  }
294
286
  if (e.compress || e.memory) {
295
- let o = null;
287
+ let i = null;
296
288
  if (e.compress)
297
- o = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...o.history);
289
+ i = await this.ai.language.compressHistory(e.history, e.compress.max, e.compress.min, e), e.history.splice(0, e.history.length, ...i.history);
298
290
  else {
299
- const a = e.history?.findLastIndex((l) => l.role == "user") ?? -1;
300
- o = await this.ai.language.compressHistory(a != -1 ? e.history.slice(a) : e.history, 0, 0, e);
291
+ const m = e.history?.findLastIndex((a) => a.role == "user") ?? -1;
292
+ i = await this.ai.language.compressHistory(m != -1 ? e.history.slice(m) : e.history, 0, 0, e);
301
293
  }
302
294
  if (e.memory) {
303
- const a = e.memory.filter((l) => !o.memory.some((d) => this.cosineSimilarity(l.embeddings[1], d.embeddings[1]) > 0.8)).concat(o.memory);
304
- e.memory.splice(0, e.memory.length, ...a);
295
+ const m = e.memory.filter((a) => !i.memory.some((d) => this.cosineSimilarity(a.embeddings[1], d.embeddings[1]) > 0.8)).concat(i.memory);
296
+ e.memory.splice(0, e.memory.length, ...m);
305
297
  }
306
298
  }
307
- return n(s);
308
- }), { abort: i });
299
+ return s(n);
300
+ }), { abort: o });
309
301
  }
310
302
  /**
311
303
  * Compress chat history to reduce context size
@@ -315,22 +307,22 @@ You have passive persistent memory never make any mention of your memory capabil
315
307
  * @param {LLMRequest} options LLM options
316
308
  * @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
317
309
  */
318
- async compressHistory(r, e, t, i) {
310
+ async compressHistory(r, e, t, o) {
319
311
  if (this.estimateTokens(r) < e) return { history: r, memory: [] };
320
- let n = 0, s = 0;
312
+ let s = 0, n = 0;
321
313
  for (let u of r.toReversed())
322
- if (s += this.estimateTokens(u.content), s < t) n++;
314
+ if (n += this.estimateTokens(u.content), n < t) s++;
323
315
  else break;
324
- if (r.length <= n) return { history: r, memory: [] };
325
- const o = r[0].role == "system" ? r[0] : null, a = n == 0 ? [] : r.slice(-n), l = (n == 0 ? r : r.slice(0, -n)).filter((u) => u.role === "assistant" || u.role === "user"), d = await this.json(`Create the smallest summary possible, no more than 500 tokens. Create a list of NEW facts (split by subject [pro]noun and fact) about what you learned from this conversation that you didn't already know or get from a tool call or system prompt. Focus only on new information about people, topics, or facts. Avoid generating facts about the AI. Match this format: {summary: string, facts: [[subject, fact]]}
316
+ if (r.length <= s) return { history: r, memory: [] };
317
+ const i = r[0].role == "system" ? r[0] : null, m = s == 0 ? [] : r.slice(-s), a = (s == 0 ? r : r.slice(0, -s)).filter((u) => u.role === "assistant" || u.role === "user"), d = await this.json(`Create the smallest summary possible, no more than 500 tokens. Create a list of NEW facts (split by subject [pro]noun and fact) about what you learned from this conversation that you didn't already know or get from a tool call or system prompt. Focus only on new information about people, topics, or facts. Avoid generating facts about the AI. Match this format: {summary: string, facts: [[subject, fact]]}
326
318
 
327
- ${l.map((u) => `${u.role}: ${u.content}`).join(`
319
+ ${a.map((u) => `${u.role}: ${u.content}`).join(`
328
320
 
329
- `)}`, { model: i?.model, temperature: i?.temperature || 0.3 }), c = /* @__PURE__ */ new Date(), m = await Promise.all((d?.facts || [])?.map(async ([u, f]) => {
321
+ `)}`, { model: o?.model, temperature: o?.temperature || 0.3 }), c = /* @__PURE__ */ new Date(), l = await Promise.all((d?.facts || [])?.map(async ([u, f]) => {
330
322
  const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
331
323
  return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: c };
332
- })), h = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...a];
333
- return o && h.splice(0, 0, o), { history: h, memory: m };
324
+ })), p = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...m];
325
+ return i && p.splice(0, 0, i), { history: p, memory: l };
334
326
  }
335
327
  /**
336
328
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -340,11 +332,11 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
340
332
  */
341
333
  cosineSimilarity(r, e) {
342
334
  if (r.length !== e.length) throw new Error("Vectors must be same length");
343
- let t = 0, i = 0, n = 0;
344
- for (let o = 0; o < r.length; o++)
345
- t += r[o] * e[o], i += r[o] * r[o], n += e[o] * e[o];
346
- const s = Math.sqrt(i) * Math.sqrt(n);
347
- return s === 0 ? 0 : t / s;
335
+ let t = 0, o = 0, s = 0;
336
+ for (let i = 0; i < r.length; i++)
337
+ t += r[i] * e[i], o += r[i] * r[i], s += e[i] * e[i];
338
+ const n = Math.sqrt(o) * Math.sqrt(s);
339
+ return n === 0 ? 0 : t / n;
348
340
  }
349
341
  /**
350
342
  * Chunk text into parts for AI digestion
@@ -354,25 +346,25 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
354
346
  * @returns {string[]} Chunked strings
355
347
  */
356
348
  chunk(r, e = 500, t = 50) {
357
- const i = (a, l = "") => a ? Object.entries(a).flatMap(([d, c]) => {
358
- const m = l ? `${l}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
359
- return typeof c == "object" && !Array.isArray(c) ? i(c, m) : `${m}: ${Array.isArray(c) ? c.join(", ") : c}`;
360
- }) : [], s = (typeof r == "object" ? i(r) : r.split(`
361
- `)).flatMap((a) => [...a.split(/\s+/).filter(Boolean), `
362
- `]), o = [];
363
- for (let a = 0; a < s.length; ) {
364
- let l = "", d = a;
365
- for (; d < s.length; ) {
366
- const m = l + (l ? " " : "") + s[d];
367
- if (this.estimateTokens(m.replace(/\s*\n\s*/g, `
368
- `)) > e && l) break;
369
- l = m, d++;
349
+ const o = (m, a = "") => m ? Object.entries(m).flatMap(([d, c]) => {
350
+ const l = a ? `${a}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
351
+ return typeof c == "object" && !Array.isArray(c) ? o(c, l) : `${l}: ${Array.isArray(c) ? c.join(", ") : c}`;
352
+ }) : [], n = (typeof r == "object" ? o(r) : r.split(`
353
+ `)).flatMap((m) => [...m.split(/\s+/).filter(Boolean), `
354
+ `]), i = [];
355
+ for (let m = 0; m < n.length; ) {
356
+ let a = "", d = m;
357
+ for (; d < n.length; ) {
358
+ const l = a + (a ? " " : "") + n[d];
359
+ if (this.estimateTokens(l.replace(/\s*\n\s*/g, `
360
+ `)) > e && a) break;
361
+ a = l, d++;
370
362
  }
371
- const c = l.replace(/\s*\n\s*/g, `
363
+ const c = a.replace(/\s*\n\s*/g, `
372
364
  `).trim();
373
- c && o.push(c), a = Math.max(d - t, d === a ? a + 1 : d);
365
+ c && i.push(c), m = Math.max(d - t, d === m ? m + 1 : d);
374
366
  }
375
- return o;
367
+ return i;
376
368
  }
377
369
  /**
378
370
  * Create a vector representation of a string
@@ -382,20 +374,21 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
382
374
  * @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
383
375
  */
384
376
  embedding(r, e = 500, t = 50) {
385
- const i = (s) => new Promise((o, a) => {
386
- const l = this.embedId++;
387
- this.embedQueue.set(l, { resolve: o, reject: a }), this.embedWorker?.postMessage({
388
- id: l,
389
- text: s,
390
- model: this.ai.options?.embedder || "bge-small-en-v1.5",
391
- path: this.ai.options.path
392
- });
393
- }), n = this.chunk(r, e, t);
394
- return Promise.all(n.map(async (s, o) => ({
395
- index: o,
396
- embedding: await i(s),
397
- text: s,
398
- tokens: this.estimateTokens(s)
377
+ const o = (n) => new Promise((i, m) => {
378
+ const a = new x(O(U(v(import.meta.url)), "embedder.js")), d = ({ embedding: l }) => {
379
+ a.terminate(), i(l);
380
+ }, c = (l) => {
381
+ a.terminate(), m(l);
382
+ };
383
+ a.on("message", d), a.on("error", c), a.on("exit", (l) => {
384
+ l !== 0 && m(new Error(`Worker exited with code ${l}`));
385
+ }), a.postMessage({ text: n, model: this.ai.options?.embedder || "bge-small-en-v1.5", path: this.ai.options.path });
386
+ }), s = this.chunk(r, e, t);
387
+ return Promise.all(s.map(async (n, i) => ({
388
+ index: i,
389
+ embedding: await o(n),
390
+ text: n,
391
+ tokens: this.estimateTokens(n)
399
392
  })));
400
393
  }
401
394
  /**
@@ -415,8 +408,8 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
415
408
  */
416
409
  fuzzyMatch(r, ...e) {
417
410
  if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
418
- const t = (s, o = 10) => s.toLowerCase().split("").map((a, l) => a.charCodeAt(0) * (l + 1) % o / o).slice(0, o), i = t(r), n = e.map((s) => t(s)).map((s) => this.cosineSimilarity(i, s));
419
- return { avg: n.reduce((s, o) => s + o, 0) / n.length, max: Math.max(...n), similarities: n };
411
+ const t = (n, i = 10) => n.toLowerCase().split("").map((m, a) => m.charCodeAt(0) * (a + 1) % i / i).slice(0, i), o = t(r), s = e.map((n) => t(n)).map((n) => this.cosineSimilarity(o, n));
412
+ return { avg: s.reduce((n, i) => n + i, 0) / s.length, max: Math.max(...s), similarities: s };
420
413
  }
421
414
  /**
422
415
  * Ask a question with JSON response
@@ -427,8 +420,8 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
427
420
  async json(r, e) {
428
421
  let t = await this.ask(r, { system: "Respond using a JSON blob matching any provided examples", ...e });
429
422
  if (!t) return {};
430
- const i = /```(?:.+)?\s*([\s\S]*?)```/.exec(t), n = i ? i[1].trim() : t;
431
- return g(n, {});
423
+ const o = /```(?:.+)?\s*([\s\S]*?)```/.exec(t), s = o ? o[1].trim() : t;
424
+ return g(s, {});
432
425
  }
433
426
  /**
434
427
  * Create a summary of some text
@@ -441,92 +434,30 @@ ${l.map((u) => `${u.role}: ${u.content}`).join(`
441
434
  return this.ask(r, { system: `Generate a brief summary <= ${e} tokens. Output nothing else`, temperature: 0.3, ...t });
442
435
  }
443
436
  }
444
- class F {
437
+ class I {
445
438
  constructor(r) {
446
439
  this.ai = r;
447
440
  }
448
- whisperPipeline;
449
- combineSpeakerTranscript(r, e) {
450
- const t = /* @__PURE__ */ new Map();
451
- let i = 0;
452
- e.forEach((a) => {
453
- t.has(a.speaker) || t.set(a.speaker, ++i);
454
- });
455
- const n = [];
456
- let s = -1, o = "";
457
- return r.forEach((a) => {
458
- const l = a.timestamp[0], d = e.find((m) => l >= m.start && l <= m.end), c = d ? t.get(d.speaker) : 1;
459
- c !== s ? (o && n.push(`[speaker ${s}]: ${o.trim()}`), s = c, o = a.text) : o += a.text;
460
- }), o && n.push(`[speaker ${s}]: ${o.trim()}`), n.join(`
461
- `);
462
- }
463
- async canDiarization() {
464
- return new Promise((r) => {
465
- const e = w("python3", ["-c", "import pyannote.audio"]);
466
- e.on("close", (t) => r(t === 0)), e.on("error", () => r(!1));
467
- });
468
- }
469
- async runDiarization(r) {
470
- if (!await this.canDiarization()) throw new Error("Pyannote is not installed: pip install pyannote.audio");
471
- const e = `
472
- import sys
473
- import json
474
- from pyannote.audio import Pipeline
475
-
476
- os.environ['TORCH_HOME'] = "${this.ai.options.path}"
477
- pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1")
478
- diarization = pipeline(sys.argv[1])
479
-
480
- segments = []
481
- for turn, _, speaker in diarization.itertracks(yield_label=True):
482
- segments.append({
483
- "start": turn.start,
484
- "end": turn.end,
485
- "speaker": speaker
486
- })
487
-
488
- print(json.dumps(segments))
489
- `;
490
- return new Promise((t, i) => {
491
- let n = "";
492
- const s = w("python3", ["-c", e, r]);
493
- s.stdout.on("data", (o) => n += o.toString()), s.stderr.on("data", (o) => console.error(o.toString())), s.on("close", (o) => {
494
- if (o === 0)
495
- try {
496
- t(JSON.parse(n));
497
- } catch {
498
- i(new Error("Failed to parse diarization output"));
499
- }
500
- else
501
- i(new Error(`Python process exited with code ${o}`));
502
- }), s.on("error", i);
503
- });
504
- }
505
441
  asr(r, e = {}) {
506
- const { model: t = this.ai.options.asr || "whisper-base", speaker: i = !1 } = e;
507
- let n = !1;
508
- const s = () => {
509
- n = !0;
510
- }, o = new Promise(async (a, l) => {
511
- try {
512
- if (n || (this.whisperPipeline || (this.whisperPipeline = await z("automatic-speech-recognition", `Xenova/${t}`, { cache_dir: this.ai.options.path, quantized: !0 })), n)) return a(null);
513
- const d = D.readFileSync(r), c = new L.WaveFile(d);
514
- c.toBitDepth("32f"), c.toSampleRate(16e3);
515
- const m = c.getSamples(), h = await this.whisperPipeline(m, { return_timestamps: i ? "word" : !1, chunk_length_s: 30 });
516
- if (!i) return a(h.text?.trim() || null);
517
- if (n) return a(null);
518
- const u = await this.runDiarization(r);
519
- if (n) return a(null);
520
- const f = this.combineSpeakerTranscript(h.chunks || [], u);
521
- a(f);
522
- } catch (d) {
523
- l(d);
524
- }
442
+ const { model: t = this.ai.options.asr || "whisper-base", speaker: o = !1 } = e;
443
+ let s = !1;
444
+ const n = () => {
445
+ s = !0;
446
+ }, i = new Promise((m, a) => {
447
+ const d = new x(R.join(import.meta.dirname, "asr.js")), c = ({ text: p, warning: u, error: f }) => {
448
+ d.terminate(), !s && (f ? a(new Error(f)) : (u && console.warn(u), m(p)));
449
+ }, l = (p) => {
450
+ d.terminate(), s || a(p);
451
+ };
452
+ d.on("message", c), d.on("error", l), d.on("exit", (p) => {
453
+ p !== 0 && !s && a(new Error(`Worker exited with code ${p}`));
454
+ }), d.postMessage({ path: r, model: t, speaker: o, torchHome: this.ai.options.path });
525
455
  });
526
- return Object.assign(o, { abort: s });
456
+ return Object.assign(i, { abort: n });
527
457
  }
458
+ canDiarization = L;
528
459
  }
529
- class B {
460
+ class J {
530
461
  constructor(r) {
531
462
  this.ai = r;
532
463
  }
@@ -537,17 +468,17 @@ class B {
537
468
  */
538
469
  ocr(r) {
539
470
  let e;
540
- const t = new Promise(async (i) => {
471
+ const t = new Promise(async (o) => {
541
472
  e = await N(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
542
- const { data: n } = await e.recognize(r);
543
- await e.terminate(), i(n.text.trim() || null);
473
+ const { data: s } = await e.recognize(r);
474
+ await e.terminate(), o(s.text.trim() || null);
544
475
  });
545
476
  return Object.assign(t, { abort: () => e?.terminate() });
546
477
  }
547
478
  }
548
- class ae {
479
+ class oe {
549
480
  constructor(r) {
550
- this.options = r, r.path || (r.path = j.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new F(this), this.language = new J(this), this.vision = new B(this);
481
+ this.options = r, r.path || (r.path = S.tmpdir()), process.env.TRANSFORMERS_CACHE = r.path, this.audio = new I(this), this.language = new H(this), this.vision = new J(this);
551
482
  }
552
483
  /** Audio processing AI */
553
484
  audio;
@@ -556,38 +487,38 @@ class ae {
556
487
  /** Vision processing AI */
557
488
  vision;
558
489
  }
559
- const G = {
490
+ const F = {
560
491
  name: "cli",
561
492
  description: "Use the command line interface, returns any output",
562
493
  args: { command: { type: "string", description: "Command to run", required: !0 } },
563
- fn: (p) => C`${p.command}`
564
- }, ce = {
494
+ fn: (h) => W`${h.command}`
495
+ }, ie = {
565
496
  name: "get_datetime",
566
497
  description: "Get current UTC date / time",
567
498
  args: {},
568
499
  fn: async () => (/* @__PURE__ */ new Date()).toUTCString()
569
- }, le = {
500
+ }, ae = {
570
501
  name: "exec",
571
502
  description: "Run code/scripts",
572
503
  args: {
573
504
  language: { type: "string", description: "Execution language", enum: ["cli", "node", "python"], required: !0 },
574
505
  code: { type: "string", description: "Code to execute", required: !0 }
575
506
  },
576
- fn: async (p, r, e) => {
507
+ fn: async (h, r, e) => {
577
508
  try {
578
- switch (p.type) {
509
+ switch (h.type) {
579
510
  case "bash":
580
- return await G.fn({ command: p.code }, r, e);
511
+ return await F.fn({ command: h.code }, r, e);
581
512
  case "node":
582
- return await Q.fn({ code: p.code }, r, e);
513
+ return await G.fn({ code: h.code }, r, e);
583
514
  case "python":
584
- return await K.fn({ code: p.code }, r, e);
515
+ return await B.fn({ code: h.code }, r, e);
585
516
  }
586
517
  } catch (t) {
587
518
  return { error: t?.message || t.toString() };
588
519
  }
589
520
  }
590
- }, me = {
521
+ }, ce = {
591
522
  name: "fetch",
592
523
  description: "Make HTTP request to URL",
593
524
  args: {
@@ -596,85 +527,86 @@ const G = {
596
527
  headers: { type: "object", description: "HTTP headers to send", default: {} },
597
528
  body: { type: "object", description: "HTTP body to send" }
598
529
  },
599
- fn: (p) => new P({ url: p.url, headers: p.headers }).request({ method: p.method || "GET", body: p.body })
600
- }, Q = {
530
+ fn: (h) => new q({ url: h.url, headers: h.headers }).request({ method: h.method || "GET", body: h.body })
531
+ }, G = {
601
532
  name: "exec_javascript",
602
533
  description: "Execute commonjs javascript",
603
534
  args: {
604
535
  code: { type: "string", description: "CommonJS javascript", required: !0 }
605
536
  },
606
- fn: async (p) => {
607
- const r = q(null), e = await $({ console: r }, p.code, !0).catch((t) => r.output.error.push(t));
537
+ fn: async (h) => {
538
+ const r = M(null), e = await P({ console: r }, h.code, !0).catch((t) => r.output.error.push(t));
608
539
  return { ...r.output, return: e, stdout: void 0, stderr: void 0 };
609
540
  }
610
- }, K = {
541
+ }, B = {
611
542
  name: "exec_javascript",
612
543
  description: "Execute commonjs javascript",
613
544
  args: {
614
545
  code: { type: "string", description: "CommonJS javascript", required: !0 }
615
546
  },
616
- fn: async (p) => ({ result: I`python -c "${p.code}"` })
617
- }, de = {
547
+ fn: async (h) => ({ result: D`python -c "${h.code}"` })
548
+ }, le = {
618
549
  name: "read_webpage",
619
550
  description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
620
551
  args: {
621
552
  url: { type: "string", description: "URL to extract content from", required: !0 },
622
553
  focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
623
554
  },
624
- fn: async (p) => {
625
- const r = await fetch(p.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((s) => s.text()).catch((s) => {
626
- throw new Error(`Failed to fetch: ${s.message}`);
627
- }), e = W.load(r);
555
+ fn: async (h) => {
556
+ const r = await fetch(h.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((n) => n.text()).catch((n) => {
557
+ throw new Error(`Failed to fetch: ${n.message}`);
558
+ }), e = C.load(r);
628
559
  e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
629
560
  const t = {
630
561
  title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
631
562
  description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
632
563
  };
633
- let i = "";
634
- const n = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
635
- for (const s of n) {
636
- const o = e(s).first();
637
- if (o.length && o.text().trim().length > 200) {
638
- i = o.text();
564
+ let o = "";
565
+ const s = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
566
+ for (const n of s) {
567
+ const i = e(n).first();
568
+ if (i.length && i.text().trim().length > 200) {
569
+ o = i.text();
639
570
  break;
640
571
  }
641
572
  }
642
- return i || (i = e("body").text()), i = i.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: p.url, title: t.title.trim(), description: t.description.trim(), content: i, focus: p.focus };
573
+ return o || (o = e("body").text()), o = o.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: o, focus: h.focus };
643
574
  }
644
- }, ue = {
575
+ }, me = {
645
576
  name: "web_search",
646
577
  description: "Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",
647
578
  args: {
648
579
  query: { type: "string", description: "Search string", required: !0 },
649
580
  length: { type: "string", description: "Number of results to return", default: 5 }
650
581
  },
651
- fn: async (p) => {
652
- const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(p.query)}`, {
582
+ fn: async (h) => {
583
+ const r = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(h.query)}`, {
653
584
  headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
654
- }).then((n) => n.text());
585
+ }).then((s) => s.text());
655
586
  let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
656
- const i = new M();
587
+ const o = new $();
657
588
  for (; (e = t.exec(r)) !== null; ) {
658
- let n = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
659
- if (n && (n = decodeURIComponent(n)), n && i.add(n), i.size >= (p.length || 5)) break;
589
+ let s = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
590
+ if (s && (s = decodeURIComponent(s)), s && o.add(s), o.size >= (h.length || 5)) break;
660
591
  }
661
- return i;
592
+ return o;
662
593
  }
663
594
  };
664
595
  export {
665
- ae as Ai,
666
- H as Anthropic,
667
- F as Audio,
668
- G as CliTool,
669
- ce as DateTimeTool,
670
- le as ExecTool,
671
- me as FetchTool,
672
- Q as JSTool,
673
- S as LLMProvider,
674
- k as OpenAi,
675
- K as PythonTool,
676
- de as ReadWebpageTool,
677
- B as Vision,
678
- ue as WebSearchTool
596
+ oe as Ai,
597
+ z as Anthropic,
598
+ I as Audio,
599
+ F as CliTool,
600
+ ie as DateTimeTool,
601
+ ae as ExecTool,
602
+ ce as FetchTool,
603
+ G as JSTool,
604
+ j as LLMProvider,
605
+ w as OpenAi,
606
+ B as PythonTool,
607
+ le as ReadWebpageTool,
608
+ J as Vision,
609
+ me as WebSearchTool,
610
+ L as canDiarization
679
611
  };
680
612
  //# sourceMappingURL=index.mjs.map