@ztimson/ai-utils 0.5.5 → 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,77 +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
- const f = await h.fn(u.input, e?.stream, this.ai);
105
- return { type: "tool_result", tool_use_id: u.id, content: b(f) };
106
- } catch (f) {
107
- 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" };
108
115
  }
109
116
  }));
110
- o.push({ role: "user", content: i }), m.messages = o;
117
+ n.push({ role: "user", content: l }), i.messages = n;
111
118
  }
112
- } while (!s.signal.aborted && n.content.some((l) => l.type === "tool_use"));
113
- 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(`
114
121
 
115
- `) }), 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);
116
123
  }), { abort: () => s.abort() });
117
124
  }
118
125
  }
119
126
  class _ extends j {
120
- constructor(t, e, s, r) {
121
- 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({
122
129
  baseURL: e,
123
130
  apiKey: s
124
131
  }));
@@ -128,17 +135,17 @@ class _ extends j {
128
135
  for (let e = 0; e < t.length; e++) {
129
136
  const s = t[e];
130
137
  if (s.role === "assistant" && s.tool_calls) {
131
- const r = s.tool_calls.map((c) => ({
138
+ const o = s.tool_calls.map((n) => ({
132
139
  role: "tool",
133
- id: c.id,
134
- name: c.function.name,
135
- args: y(c.function.arguments, {}),
140
+ id: n.id,
141
+ name: n.function.name,
142
+ args: y(n.function.arguments, {}),
136
143
  timestamp: s.timestamp
137
144
  }));
138
- t.splice(e, 1, ...r), e += r.length - 1;
145
+ t.splice(e, 1, ...o), e += o.length - 1;
139
146
  } else if (s.role === "tool" && s.content) {
140
- const r = t.find((c) => s.tool_call_id == c.id);
141
- 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--;
142
149
  }
143
150
  t[e]?.timestamp || (t[e].timestamp = Date.now());
144
151
  }
@@ -159,76 +166,77 @@ class _ extends j {
159
166
  content: s.error || s.content
160
167
  });
161
168
  else {
162
- const { timestamp: r, ...c } = s;
163
- e.push(c);
169
+ const { timestamp: o, ...n } = s;
170
+ e.push(n);
164
171
  }
165
172
  return e;
166
173
  }, []);
167
174
  }
168
175
  ask(t, e = {}) {
169
176
  const s = new AbortController();
170
- return Object.assign(new Promise(async (r, c) => {
177
+ return Object.assign(new Promise(async (o, n) => {
171
178
  e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
172
- 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 = {
173
181
  model: e.model || this.model,
174
- messages: o,
182
+ messages: r,
175
183
  stream: !!e.stream,
176
184
  max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
177
185
  temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
178
- tools: a.map((l) => ({
186
+ tools: i.map((l) => ({
179
187
  type: "function",
180
188
  function: {
181
189
  name: l.name,
182
190
  description: l.description,
183
191
  parameters: {
184
192
  type: "object",
185
- properties: l.args ? k(l.args, (i, u) => ({ ...u, required: void 0 })) : {},
186
- 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]) : []
187
195
  }
188
196
  }
189
197
  }))
190
198
  };
191
- let n, d = !0;
199
+ let a, m = !0;
192
200
  do {
193
- if (n = await this.client.chat.completions.create(m).catch((i) => {
194
- throw i.message += `
201
+ if (a = await this.client.chat.completions.create(c).catch((d) => {
202
+ throw d.message += `
195
203
 
196
204
  Messages:
197
- ${JSON.stringify(o, null, 2)}`, i;
205
+ ${JSON.stringify(r, null, 2)}`, d;
198
206
  }), e.stream) {
199
- d ? d = !1 : e.stream({ text: `
207
+ m ? m = !1 : e.stream({ text: `
200
208
 
201
- ` }), n.choices = [{ message: { content: "", tool_calls: [] } }];
202
- for await (const i of n) {
209
+ ` }), a.choices = [{ message: { content: "", tool_calls: [] } }];
210
+ for await (const d of a) {
203
211
  if (s.signal.aborted) break;
204
- 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);
205
213
  }
206
214
  }
207
- const l = n.choices[0].message.tool_calls || [];
215
+ const l = a.choices[0].message.tool_calls || [];
208
216
  if (l.length && !s.signal.aborted) {
209
- o.push(n.choices[0].message);
210
- const i = await Promise.all(l.map(async (u) => {
211
- const h = a?.find(x("name", u.function.name));
212
- 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"}' };
213
221
  try {
214
- const f = y(u.function.arguments, {}), g = await h.fn(f, e.stream, this.ai);
215
- 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) };
216
224
  } catch (f) {
217
- 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" }) };
218
226
  }
219
227
  }));
220
- o.push(...i), m.messages = o;
228
+ r.push(...d), c.messages = r;
221
229
  }
222
- } while (!s.signal.aborted && n.choices?.[0]?.message?.tool_calls?.length);
223
- 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);
224
232
  }), { abort: () => s.abort() });
225
233
  }
226
234
  }
227
235
  class H {
228
236
  constructor(t) {
229
237
  this.ai = t, this.embedWorker = new v(R(U(O(import.meta.url)), "embedder.js")), this.embedWorker.on("message", ({ id: e, embedding: s }) => {
230
- const r = this.embedQueue.get(e);
231
- r && (r.resolve(s), this.embedQueue.delete(e));
238
+ const o = this.embedQueue.get(e);
239
+ o && (o.resolve(s), this.embedQueue.delete(e));
232
240
  }), t.options.llm?.models && Object.entries(t.options.llm.models).forEach(([e, s]) => {
233
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));
234
242
  });
@@ -247,22 +255,22 @@ class H {
247
255
  ask(t, e = {}) {
248
256
  const s = e.model || this.defaultModel;
249
257
  if (!this.models[s]) throw new Error(`Model does not exist: ${s}`);
250
- let r = () => {
258
+ let o = () => {
251
259
  };
252
- return Object.assign(new Promise(async (c) => {
260
+ return Object.assign(new Promise(async (n) => {
253
261
  if (e.history || (e.history = []), e.memory) {
254
262
  e.system = (e.system || "") + `
255
263
  You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
256
264
  `;
257
- const a = async (n, d, l = 50) => {
258
- const [i, u] = await Promise.all([
259
- d ? this.embedding(d) : Promise.resolve(null),
260
- 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)
261
269
  ]);
262
- 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);
263
- }, m = await a(t);
264
- m.length && e.history.push({ role: "assistant", content: `Things I remembered:
265
- ` + 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(`
266
274
  `) }), e.tools = [...e.tools || [], {
267
275
  name: "read_memory",
268
276
  description: "Check your long-term memory for more information",
@@ -271,32 +279,32 @@ You have passive persistent memory never make any mention of your memory capabil
271
279
  query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
272
280
  limit: { type: "number", description: "Result limit, default 5" }
273
281
  },
274
- fn: (n) => {
275
- if (!n.subject && !n.query) throw new Error("Either a subject or query argument is required");
276
- 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);
277
285
  }
278
286
  }];
279
287
  }
280
- const o = await this.models[s].ask(t, e);
288
+ const r = await this.models[s].ask(t, e);
281
289
  if (e.memory) {
282
- const a = e.history?.findIndex((m) => m.role == "assistant" && m.content.startsWith("Things I remembered:"));
283
- 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);
284
292
  }
285
293
  if (e.compress || e.memory) {
286
- let a = null;
294
+ let i = null;
287
295
  if (e.compress)
288
- 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);
289
297
  else {
290
- const m = e.history?.findLastIndex((n) => n.role == "user") ?? -1;
291
- 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);
292
300
  }
293
301
  if (e.memory) {
294
- const m = e.memory.filter((n) => !a.memory.some((d) => this.cosineSimilarity(n.embeddings[1], d.embeddings[1]) > 0.8)).concat(a.memory);
295
- 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);
296
304
  }
297
305
  }
298
- return c(o);
299
- }), { abort: r });
306
+ return n(r);
307
+ }), { abort: o });
300
308
  }
301
309
  /**
302
310
  * Compress chat history to reduce context size
@@ -306,22 +314,22 @@ You have passive persistent memory never make any mention of your memory capabil
306
314
  * @param {LLMRequest} options LLM options
307
315
  * @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
308
316
  */
309
- async compressHistory(t, e, s, r) {
317
+ async compressHistory(t, e, s, o) {
310
318
  if (this.estimateTokens(t) < e) return { history: t, memory: [] };
311
- let c = 0, o = 0;
312
- for (let h of t.toReversed())
313
- 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++;
314
322
  else break;
315
- if (t.length <= c) return { history: t, memory: [] };
316
- 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]]}
317
325
 
318
- ${n.map((h) => `${h.role}: ${h.content}`).join(`
326
+ ${a.map((u) => `${u.role}: ${u.content}`).join(`
319
327
 
320
- `)}`, { model: r?.model, temperature: r?.temperature || 0.3 }), l = /* @__PURE__ */ new Date(), i = await Promise.all((d?.facts || [])?.map(async ([h, f]) => {
321
- const g = await Promise.all([this.embedding(h), this.embedding(`${h}: ${f}`)]);
322
- return { owner: h, fact: f, embeddings: [g[0][0].embedding, g[1][0].embedding], timestamp: l };
323
- })), u = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...m];
324
- 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 };
325
333
  }
326
334
  /**
327
335
  * Compare the difference between embeddings (calculates the angle between two vectors)
@@ -331,11 +339,11 @@ ${n.map((h) => `${h.role}: ${h.content}`).join(`
331
339
  */
332
340
  cosineSimilarity(t, e) {
333
341
  if (t.length !== e.length) throw new Error("Vectors must be same length");
334
- let s = 0, r = 0, c = 0;
335
- for (let a = 0; a < t.length; a++)
336
- s += t[a] * e[a], r += t[a] * t[a], c += e[a] * e[a];
337
- const o = Math.sqrt(r) * Math.sqrt(c);
338
- 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;
339
347
  }
340
348
  /**
341
349
  * Chunk text into parts for AI digestion
@@ -345,25 +353,25 @@ ${n.map((h) => `${h.role}: ${h.content}`).join(`
345
353
  * @returns {string[]} Chunked strings
346
354
  */
347
355
  chunk(t, e = 500, s = 50) {
348
- const r = (m, n = "") => m ? Object.entries(m).flatMap(([d, l]) => {
349
- const i = n ? `${n}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
350
- return typeof l == "object" && !Array.isArray(l) ? r(l, i) : `${i}: ${Array.isArray(l) ? l.join(", ") : l}`;
351
- }) : [], o = (typeof t == "object" ? r(t) : t.split(`
352
- `)).flatMap((m) => [...m.split(/\s+/).filter(Boolean), `
353
- `]), a = [];
354
- for (let m = 0; m < o.length; ) {
355
- let n = "", d = m;
356
- for (; d < o.length; ) {
357
- const i = n + (n ? " " : "") + o[d];
358
- if (this.estimateTokens(i.replace(/\s*\n\s*/g, `
359
- `)) > e && n) break;
360
- 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++;
361
369
  }
362
- const l = n.replace(/\s*\n\s*/g, `
370
+ const l = a.replace(/\s*\n\s*/g, `
363
371
  `).trim();
364
- 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);
365
373
  }
366
- return a;
374
+ return i;
367
375
  }
368
376
  /**
369
377
  * Create a vector representation of a string
@@ -373,20 +381,20 @@ ${n.map((h) => `${h.role}: ${h.content}`).join(`
373
381
  * @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
374
382
  */
375
383
  embedding(t, e = 500, s = 50) {
376
- const r = (o) => new Promise((a, m) => {
377
- const n = this.embedId++;
378
- this.embedQueue.set(n, { resolve: a, reject: m }), this.embedWorker?.postMessage({
379
- id: n,
380
- 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,
381
389
  model: this.ai.options?.embedder || "bge-small-en-v1.5",
382
390
  path: this.ai.options.path
383
391
  });
384
- }), c = this.chunk(t, e, s);
385
- return Promise.all(c.map(async (o, a) => ({
386
- index: a,
387
- embedding: await r(o),
388
- text: o,
389
- 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)
390
398
  })));
391
399
  }
392
400
  /**
@@ -406,8 +414,8 @@ ${n.map((h) => `${h.role}: ${h.content}`).join(`
406
414
  */
407
415
  fuzzyMatch(t, ...e) {
408
416
  if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
409
- 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));
410
- 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 };
411
419
  }
412
420
  /**
413
421
  * Ask a question with JSON response
@@ -418,8 +426,8 @@ ${n.map((h) => `${h.role}: ${h.content}`).join(`
418
426
  async json(t, e) {
419
427
  let s = await this.ask(t, { system: "Respond using a JSON blob matching any provided examples", ...e });
420
428
  if (!s) return {};
421
- const r = /```(?:.+)?\s*([\s\S]*?)```/.exec(s), c = r ? r[1].trim() : s;
422
- return y(c, {});
429
+ const o = /```(?:.+)?\s*([\s\S]*?)```/.exec(s), n = o ? o[1].trim() : s;
430
+ return y(n, {});
423
431
  }
424
432
  /**
425
433
  * Create a summary of some text
@@ -442,15 +450,15 @@ class z {
442
450
  if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
443
451
  let s = () => {
444
452
  };
445
- const r = new Promise(async (c, o) => {
446
- const a = await this.downloadAsrModel(e);
447
- let m = "";
448
- const n = W(this.ai.options.whisper?.binary, ["-nt", "-np", "-m", a, "-f", t], { stdio: ["ignore", "pipe", "ignore"] });
449
- s = () => n.kill("SIGTERM"), n.on("error", (d) => o(d)), n.stdout.on("data", (d) => m += d.toString()), n.on("close", (d) => {
450
- 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}`));
451
459
  });
452
460
  });
453
- return Object.assign(r, { abort: s });
461
+ return Object.assign(o, { abort: s });
454
462
  }
455
463
  async downloadAsrModel(t = this.whisperModel) {
456
464
  if (!this.ai.options.whisper?.binary) throw new Error("Whisper not configured");
@@ -470,10 +478,10 @@ class F {
470
478
  */
471
479
  ocr(t) {
472
480
  let e;
473
- const s = new Promise(async (r) => {
474
- e = await I(this.ai.options.tesseract?.model || "eng", 2, { cachePath: this.ai.options.path });
475
- const { data: c } = await e.recognize(t);
476
- 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);
477
485
  });
478
486
  return Object.assign(s, { abort: () => e?.terminate() });
479
487
  }
@@ -493,7 +501,7 @@ const B = {
493
501
  name: "cli",
494
502
  description: "Use the command line interface, returns any output",
495
503
  args: { command: { type: "string", description: "Command to run", required: !0 } },
496
- fn: (p) => C`${p.command}`
504
+ fn: (h) => I`${h.command}`
497
505
  }, ce = {
498
506
  name: "get_datetime",
499
507
  description: "Get current UTC date / time",
@@ -506,15 +514,15 @@ const B = {
506
514
  language: { type: "string", description: "Execution language", enum: ["cli", "node", "python"], required: !0 },
507
515
  code: { type: "string", description: "Code to execute", required: !0 }
508
516
  },
509
- fn: async (p, t, e) => {
517
+ fn: async (h, t, e) => {
510
518
  try {
511
- switch (p.type) {
519
+ switch (h.type) {
512
520
  case "bash":
513
- return await B.fn({ command: p.code }, t, e);
521
+ return await B.fn({ command: h.code }, t, e);
514
522
  case "node":
515
- return await G.fn({ code: p.code }, t, e);
523
+ return await G.fn({ code: h.code }, t, e);
516
524
  case "python":
517
- return await Q.fn({ code: p.code }, t, e);
525
+ return await Q.fn({ code: h.code }, t, e);
518
526
  }
519
527
  } catch (s) {
520
528
  return { error: s?.message || s.toString() };
@@ -529,15 +537,15 @@ const B = {
529
537
  headers: { type: "object", description: "HTTP headers to send", default: {} },
530
538
  body: { type: "object", description: "HTTP body to send" }
531
539
  },
532
- 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 })
533
541
  }, G = {
534
542
  name: "exec_javascript",
535
543
  description: "Execute commonjs javascript",
536
544
  args: {
537
545
  code: { type: "string", description: "CommonJS javascript", required: !0 }
538
546
  },
539
- fn: async (p) => {
540
- 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));
541
549
  return { ...t.output, return: e, stdout: void 0, stderr: void 0 };
542
550
  }
543
551
  }, Q = {
@@ -546,7 +554,7 @@ const B = {
546
554
  args: {
547
555
  code: { type: "string", description: "CommonJS javascript", required: !0 }
548
556
  },
549
- fn: async (p) => ({ result: D`python -c "${p.code}"` })
557
+ fn: async (h) => ({ result: D`python -c "${h.code}"` })
550
558
  }, de = {
551
559
  name: "read_webpage",
552
560
  description: "Extract clean, structured content from a webpage. Use after web_search to read specific URLs",
@@ -554,25 +562,25 @@ const B = {
554
562
  url: { type: "string", description: "URL to extract content from", required: !0 },
555
563
  focus: { type: "string", description: 'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")' }
556
564
  },
557
- fn: async (p) => {
558
- const t = await fetch(p.url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } }).then((o) => o.text()).catch((o) => {
559
- throw new Error(`Failed to fetch: ${o.message}`);
560
- }), 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);
561
569
  e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();
562
570
  const s = {
563
571
  title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
564
572
  description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
565
573
  };
566
- let r = "";
567
- const c = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
568
- for (const o of c) {
569
- const a = e(o).first();
570
- if (a.length && a.text().trim().length > 200) {
571
- 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();
572
580
  break;
573
581
  }
574
582
  }
575
- 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 };
576
584
  }
577
585
  }, ue = {
578
586
  name: "web_search",
@@ -581,17 +589,17 @@ const B = {
581
589
  query: { type: "string", description: "Search string", required: !0 },
582
590
  length: { type: "string", description: "Number of results to return", default: 5 }
583
591
  },
584
- fn: async (p) => {
585
- 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)}`, {
586
594
  headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
587
- }).then((c) => c.text());
595
+ }).then((n) => n.text());
588
596
  let e, s = /<a .*?href="(.+?)".+?<\/a>/g;
589
- const r = new E();
597
+ const o = new $();
590
598
  for (; (e = s.exec(t)) !== null; ) {
591
- let c = /uddg=(.+)&amp?/.exec(decodeURIComponent(e[1]))?.[1];
592
- 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;
593
601
  }
594
- return r;
602
+ return o;
595
603
  }
596
604
  };
597
605
  export {