@ztimson/ai-utils 0.7.7 → 0.7.9

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