@ztimson/ai-utils 0.5.4 → 0.5.6

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