@ztimson/ai-utils 0.7.7 → 0.7.8

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