@ztimson/ai-utils 0.7.0 → 0.7.1
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.js +17 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +151 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as $ from "node:os";
|
|
2
|
-
import { objectMap as
|
|
2
|
+
import { objectMap as k, JSONAttemptParse as g, findByProp as _, JSONSanitize as b, clean as M, Http as E, consoleInterceptor as P, fn as A, ASet as O } from "@ztimson/utils";
|
|
3
3
|
import { Anthropic as v } from "@anthropic-ai/sdk";
|
|
4
4
|
import { OpenAI as U } from "openai";
|
|
5
5
|
import { Worker as x } from "worker_threads";
|
|
@@ -19,19 +19,19 @@ class W extends q {
|
|
|
19
19
|
client;
|
|
20
20
|
toStandard(s) {
|
|
21
21
|
const e = Date.now(), t = [];
|
|
22
|
-
for (let
|
|
23
|
-
if (typeof
|
|
24
|
-
t.push({ timestamp: e, ...
|
|
22
|
+
for (let m of s)
|
|
23
|
+
if (typeof m.content == "string")
|
|
24
|
+
t.push({ timestamp: e, ...m });
|
|
25
25
|
else {
|
|
26
|
-
const r =
|
|
26
|
+
const r = m.content?.filter((n) => n.type == "text").map((n) => n.text).join(`
|
|
27
27
|
|
|
28
28
|
`);
|
|
29
|
-
r && t.push({ timestamp: e, role:
|
|
29
|
+
r && t.push({ timestamp: e, role: m.role, content: r }), m.content.forEach((n) => {
|
|
30
30
|
if (n.type == "tool_use")
|
|
31
31
|
t.push({ timestamp: e, role: "tool", id: n.id, name: n.name, args: n.input, content: void 0 });
|
|
32
32
|
else if (n.type == "tool_result") {
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const a = t.findLast((i) => i.id == n.tool_use_id);
|
|
34
|
+
a && (a[n.is_error ? "error" : "content"] = n.content);
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -52,9 +52,9 @@ class W extends q {
|
|
|
52
52
|
}
|
|
53
53
|
ask(s, e = {}) {
|
|
54
54
|
const t = new AbortController();
|
|
55
|
-
return Object.assign(new Promise(async (
|
|
55
|
+
return Object.assign(new Promise(async (m) => {
|
|
56
56
|
let r = this.fromStandard([...e.history || [], { role: "user", content: s, timestamp: Date.now() }]);
|
|
57
|
-
const n = e.tools || this.ai.options.llm?.tools || [],
|
|
57
|
+
const n = e.tools || this.ai.options.llm?.tools || [], a = {
|
|
58
58
|
model: e.model || this.model,
|
|
59
59
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
60
60
|
system: e.system || this.ai.options.llm?.system || "",
|
|
@@ -64,7 +64,7 @@ class W extends q {
|
|
|
64
64
|
description: d.description,
|
|
65
65
|
input_schema: {
|
|
66
66
|
type: "object",
|
|
67
|
-
properties: d.args ?
|
|
67
|
+
properties: d.args ? k(d.args, (c, l) => ({ ...l, required: void 0 })) : {},
|
|
68
68
|
required: d.args ? Object.entries(d.args).filter((c) => c[1].required).map((c) => c[0]) : []
|
|
69
69
|
},
|
|
70
70
|
fn: void 0
|
|
@@ -72,58 +72,58 @@ class W extends q {
|
|
|
72
72
|
messages: r,
|
|
73
73
|
stream: !!e.stream
|
|
74
74
|
};
|
|
75
|
-
let
|
|
75
|
+
let i, o = !0;
|
|
76
76
|
do {
|
|
77
|
-
if (
|
|
77
|
+
if (i = await this.client.messages.create(a).catch((c) => {
|
|
78
78
|
throw c.message += `
|
|
79
79
|
|
|
80
80
|
Messages:
|
|
81
81
|
${JSON.stringify(r, null, 2)}`, c;
|
|
82
82
|
}), e.stream) {
|
|
83
|
-
|
|
83
|
+
o ? o = !1 : e.stream({ text: `
|
|
84
84
|
|
|
85
|
-
` }),
|
|
86
|
-
for await (const c of
|
|
85
|
+
` }), i.content = [];
|
|
86
|
+
for await (const c of i) {
|
|
87
87
|
if (t.signal.aborted) break;
|
|
88
88
|
if (c.type === "content_block_start")
|
|
89
|
-
c.content_block.type === "text" ?
|
|
89
|
+
c.content_block.type === "text" ? i.content.push({ type: "text", text: "" }) : c.content_block.type === "tool_use" && i.content.push({ type: "tool_use", id: c.content_block.id, name: c.content_block.name, input: "" });
|
|
90
90
|
else if (c.type === "content_block_delta")
|
|
91
91
|
if (c.delta.type === "text_delta") {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
} else c.delta.type === "input_json_delta" && (
|
|
92
|
+
const l = c.delta.text;
|
|
93
|
+
i.content.at(-1).text += l, e.stream({ text: l });
|
|
94
|
+
} else c.delta.type === "input_json_delta" && (i.content.at(-1).input += c.delta.partial_json);
|
|
95
95
|
else if (c.type === "content_block_stop") {
|
|
96
|
-
const
|
|
97
|
-
|
|
96
|
+
const l = i.content.at(-1);
|
|
97
|
+
l.input != null && (l.input = l.input ? g(l.input, {}) : {});
|
|
98
98
|
} else if (c.type === "message_stop")
|
|
99
99
|
break;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
const d =
|
|
102
|
+
const d = i.content.filter((c) => c.type === "tool_use");
|
|
103
103
|
if (d.length && !t.signal.aborted) {
|
|
104
|
-
r.push({ role: "assistant", content:
|
|
105
|
-
const c = await Promise.all(d.map(async (
|
|
106
|
-
const p = n.find(
|
|
107
|
-
if (e.stream && e.stream({ tool:
|
|
104
|
+
r.push({ role: "assistant", content: i.content });
|
|
105
|
+
const c = await Promise.all(d.map(async (l) => {
|
|
106
|
+
const p = n.find(_("name", l.name));
|
|
107
|
+
if (e.stream && e.stream({ tool: l.name }), !p) return { tool_use_id: l.id, is_error: !0, content: "Tool not found" };
|
|
108
108
|
try {
|
|
109
|
-
const u = await p.fn(
|
|
110
|
-
return { type: "tool_result", tool_use_id:
|
|
109
|
+
const u = await p.fn(l.input, e?.stream, this.ai);
|
|
110
|
+
return { type: "tool_result", tool_use_id: l.id, content: b(u) };
|
|
111
111
|
} catch (u) {
|
|
112
|
-
return { type: "tool_result", tool_use_id:
|
|
112
|
+
return { type: "tool_result", tool_use_id: l.id, is_error: !0, content: u?.message || u?.toString() || "Unknown" };
|
|
113
113
|
}
|
|
114
114
|
}));
|
|
115
|
-
r.push({ role: "user", content: c }),
|
|
115
|
+
r.push({ role: "user", content: c }), a.messages = r;
|
|
116
116
|
}
|
|
117
|
-
} while (!t.signal.aborted &&
|
|
118
|
-
r.push({ role: "assistant", content:
|
|
117
|
+
} while (!t.signal.aborted && i.content.some((d) => d.type === "tool_use"));
|
|
118
|
+
r.push({ role: "assistant", content: i.content.filter((d) => d.type == "text").map((d) => d.text).join(`
|
|
119
119
|
|
|
120
|
-
`) }), r = this.toStandard(r), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...r),
|
|
120
|
+
`) }), r = this.toStandard(r), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...r), m(r.at(-1)?.content);
|
|
121
121
|
}), { abort: () => t.abort() });
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
class w extends q {
|
|
125
|
-
constructor(s, e, t,
|
|
126
|
-
super(), this.ai = s, this.host = e, this.token = t, this.model =
|
|
125
|
+
constructor(s, e, t, m) {
|
|
126
|
+
super(), this.ai = s, this.host = e, this.token = t, this.model = m, this.client = new U(M({
|
|
127
127
|
baseURL: e,
|
|
128
128
|
apiKey: t
|
|
129
129
|
}));
|
|
@@ -133,17 +133,17 @@ class w extends q {
|
|
|
133
133
|
for (let e = 0; e < s.length; e++) {
|
|
134
134
|
const t = s[e];
|
|
135
135
|
if (t.role === "assistant" && t.tool_calls) {
|
|
136
|
-
const
|
|
136
|
+
const m = t.tool_calls.map((r) => ({
|
|
137
137
|
role: "tool",
|
|
138
138
|
id: r.id,
|
|
139
139
|
name: r.function.name,
|
|
140
140
|
args: g(r.function.arguments, {}),
|
|
141
141
|
timestamp: t.timestamp
|
|
142
142
|
}));
|
|
143
|
-
s.splice(e, 1, ...
|
|
143
|
+
s.splice(e, 1, ...m), e += m.length - 1;
|
|
144
144
|
} else if (t.role === "tool" && t.content) {
|
|
145
|
-
const
|
|
146
|
-
|
|
145
|
+
const m = s.find((r) => t.tool_call_id == r.id);
|
|
146
|
+
m && (t.content.includes('"error":') ? m.error = t.content : m.content = t.content), s.splice(e, 1), e--;
|
|
147
147
|
}
|
|
148
148
|
s[e]?.timestamp || (s[e].timestamp = Date.now());
|
|
149
149
|
}
|
|
@@ -164,7 +164,7 @@ class w extends q {
|
|
|
164
164
|
content: t.error || t.content
|
|
165
165
|
});
|
|
166
166
|
else {
|
|
167
|
-
const { timestamp:
|
|
167
|
+
const { timestamp: m, ...r } = t;
|
|
168
168
|
e.push(r);
|
|
169
169
|
}
|
|
170
170
|
return e;
|
|
@@ -172,49 +172,49 @@ class w extends q {
|
|
|
172
172
|
}
|
|
173
173
|
ask(s, e = {}) {
|
|
174
174
|
const t = new AbortController();
|
|
175
|
-
return Object.assign(new Promise(async (
|
|
175
|
+
return Object.assign(new Promise(async (m, r) => {
|
|
176
176
|
e.system && e.history?.[0]?.role != "system" && e.history?.splice(0, 0, { role: "system", content: e.system, timestamp: Date.now() });
|
|
177
177
|
let n = this.fromStandard([...e.history || [], { role: "user", content: s, timestamp: Date.now() }]);
|
|
178
|
-
const
|
|
178
|
+
const a = e.tools || this.ai.options.llm?.tools || [], i = {
|
|
179
179
|
model: e.model || this.model,
|
|
180
180
|
messages: n,
|
|
181
181
|
stream: !!e.stream,
|
|
182
182
|
max_tokens: e.max_tokens || this.ai.options.llm?.max_tokens || 4096,
|
|
183
183
|
temperature: e.temperature || this.ai.options.llm?.temperature || 0.7,
|
|
184
|
-
tools:
|
|
184
|
+
tools: a.map((c) => ({
|
|
185
185
|
type: "function",
|
|
186
186
|
function: {
|
|
187
187
|
name: c.name,
|
|
188
188
|
description: c.description,
|
|
189
189
|
parameters: {
|
|
190
190
|
type: "object",
|
|
191
|
-
properties: c.args ?
|
|
192
|
-
required: c.args ? Object.entries(c.args).filter((
|
|
191
|
+
properties: c.args ? k(c.args, (l, p) => ({ ...p, required: void 0 })) : {},
|
|
192
|
+
required: c.args ? Object.entries(c.args).filter((l) => l[1].required).map((l) => l[0]) : []
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
}))
|
|
196
196
|
};
|
|
197
|
-
let
|
|
197
|
+
let o, d = !0;
|
|
198
198
|
do {
|
|
199
|
-
if (
|
|
200
|
-
throw
|
|
199
|
+
if (o = await this.client.chat.completions.create(i).catch((l) => {
|
|
200
|
+
throw l.message += `
|
|
201
201
|
|
|
202
202
|
Messages:
|
|
203
|
-
${JSON.stringify(n, null, 2)}`,
|
|
203
|
+
${JSON.stringify(n, null, 2)}`, l;
|
|
204
204
|
}), e.stream) {
|
|
205
205
|
d ? d = !1 : e.stream({ text: `
|
|
206
206
|
|
|
207
|
-
` }),
|
|
208
|
-
for await (const
|
|
207
|
+
` }), o.choices = [{ message: { content: "", tool_calls: [] } }];
|
|
208
|
+
for await (const l of o) {
|
|
209
209
|
if (t.signal.aborted) break;
|
|
210
|
-
|
|
210
|
+
l.choices[0].delta.content && (o.choices[0].message.content += l.choices[0].delta.content, e.stream({ text: l.choices[0].delta.content })), l.choices[0].delta.tool_calls && (o.choices[0].message.tool_calls = l.choices[0].delta.tool_calls);
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
const c =
|
|
213
|
+
const c = o.choices[0].message.tool_calls || [];
|
|
214
214
|
if (c.length && !t.signal.aborted) {
|
|
215
|
-
n.push(
|
|
216
|
-
const
|
|
217
|
-
const u =
|
|
215
|
+
n.push(o.choices[0].message);
|
|
216
|
+
const l = await Promise.all(c.map(async (p) => {
|
|
217
|
+
const u = a?.find(_("name", p.function.name));
|
|
218
218
|
if (e.stream && e.stream({ tool: p.function.name }), !u) return { role: "tool", tool_call_id: p.id, content: '{"error": "Tool not found"}' };
|
|
219
219
|
try {
|
|
220
220
|
const f = g(p.function.arguments, {}), y = await u.fn(f, e.stream, this.ai);
|
|
@@ -223,10 +223,10 @@ ${JSON.stringify(n, null, 2)}`, m;
|
|
|
223
223
|
return { role: "tool", tool_call_id: p.id, content: b({ error: f?.message || f?.toString() || "Unknown" }) };
|
|
224
224
|
}
|
|
225
225
|
}));
|
|
226
|
-
n.push(...
|
|
226
|
+
n.push(...l), i.messages = n;
|
|
227
227
|
}
|
|
228
|
-
} while (!t.signal.aborted &&
|
|
229
|
-
n.push({ role: "assistant", content:
|
|
228
|
+
} while (!t.signal.aborted && o.choices?.[0]?.message?.tool_calls?.length);
|
|
229
|
+
n.push({ role: "assistant", content: o.choices[0].message.content || "" }), n = this.toStandard(n), e.stream && e.stream({ done: !0 }), e.history && e.history.splice(0, e.history.length, ...n), m(n.at(-1)?.content);
|
|
230
230
|
}), { abort: () => t.abort() });
|
|
231
231
|
}
|
|
232
232
|
}
|
|
@@ -247,22 +247,22 @@ class z {
|
|
|
247
247
|
ask(s, e = {}) {
|
|
248
248
|
const t = e.model || this.defaultModel;
|
|
249
249
|
if (!this.models[t]) throw new Error(`Model does not exist: ${t}`);
|
|
250
|
-
let
|
|
250
|
+
let m = () => {
|
|
251
251
|
};
|
|
252
252
|
return Object.assign(new Promise(async (r) => {
|
|
253
253
|
if (e.history || (e.history = []), e.memory) {
|
|
254
254
|
e.system = (e.system || "") + `
|
|
255
255
|
You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
|
|
256
256
|
`;
|
|
257
|
-
const
|
|
258
|
-
const [
|
|
257
|
+
const a = async (o, d, c = 50) => {
|
|
258
|
+
const [l, p] = await Promise.all([
|
|
259
259
|
d ? this.embedding(d) : Promise.resolve(null),
|
|
260
|
-
|
|
260
|
+
o ? this.embedding(o) : Promise.resolve(null)
|
|
261
261
|
]);
|
|
262
|
-
return (e.memory || []).map((u) => ({ ...u, score:
|
|
263
|
-
},
|
|
264
|
-
|
|
265
|
-
` +
|
|
262
|
+
return (e.memory || []).map((u) => ({ ...u, score: l ? this.cosineSimilarity(u.embeddings[0], l[0].embedding) : 1 })).filter((u) => u.score >= 0.8).map((u) => ({ ...u, score: p ? this.cosineSimilarity(u.embeddings[1], p[0].embedding) : u.score })).filter((u) => u.score >= 0.2).toSorted((u, f) => u.score - f.score).slice(0, c);
|
|
263
|
+
}, i = await a(s);
|
|
264
|
+
i.length && e.history.push({ role: "assistant", content: `Things I remembered:
|
|
265
|
+
` + i.map((o) => `${o.owner}: ${o.fact}`).join(`
|
|
266
266
|
`) }), e.tools = [...e.tools || [], {
|
|
267
267
|
name: "read_memory",
|
|
268
268
|
description: "Check your long-term memory for more information",
|
|
@@ -271,32 +271,32 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
271
271
|
query: { type: "string", description: "Search memory based on a query, can be used with or without subject argument" },
|
|
272
272
|
limit: { type: "number", description: "Result limit, default 5" }
|
|
273
273
|
},
|
|
274
|
-
fn: (
|
|
275
|
-
if (!
|
|
276
|
-
return
|
|
274
|
+
fn: (o) => {
|
|
275
|
+
if (!o.subject && !o.query) throw new Error("Either a subject or query argument is required");
|
|
276
|
+
return a(o.query, o.subject, o.limit || 5);
|
|
277
277
|
}
|
|
278
278
|
}];
|
|
279
279
|
}
|
|
280
280
|
const n = await this.models[t].ask(s, e);
|
|
281
281
|
if (e.memory) {
|
|
282
|
-
const
|
|
283
|
-
|
|
282
|
+
const a = e.history?.findIndex((i) => i.role == "assistant" && i.content.startsWith("Things I remembered:"));
|
|
283
|
+
a != null && a >= 0 && e.history?.splice(a, 1);
|
|
284
284
|
}
|
|
285
285
|
if (e.compress || e.memory) {
|
|
286
|
-
let
|
|
286
|
+
let a = null;
|
|
287
287
|
if (e.compress)
|
|
288
|
-
|
|
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);
|
|
289
289
|
else {
|
|
290
|
-
const
|
|
291
|
-
|
|
290
|
+
const i = e.history?.findLastIndex((o) => o.role == "user") ?? -1;
|
|
291
|
+
a = await this.ai.language.compressHistory(i != -1 ? e.history.slice(i) : e.history, 0, 0, e);
|
|
292
292
|
}
|
|
293
293
|
if (e.memory) {
|
|
294
|
-
const
|
|
295
|
-
e.memory.splice(0, e.memory.length, ...
|
|
294
|
+
const i = e.memory.filter((o) => !a.memory.some((d) => this.cosineSimilarity(o.embeddings[1], d.embeddings[1]) > 0.8)).concat(a.memory);
|
|
295
|
+
e.memory.splice(0, e.memory.length, ...i);
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
return r(n);
|
|
299
|
-
}), { abort:
|
|
299
|
+
}), { abort: m });
|
|
300
300
|
}
|
|
301
301
|
/**
|
|
302
302
|
* Compress chat history to reduce context size
|
|
@@ -306,24 +306,24 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
306
306
|
* @param {LLMRequest} options LLM options
|
|
307
307
|
* @returns {Promise<LLMMessage[]>} New chat history will summary at index 0
|
|
308
308
|
*/
|
|
309
|
-
async compressHistory(s, e, t,
|
|
309
|
+
async compressHistory(s, e, t, m) {
|
|
310
310
|
if (this.estimateTokens(s) < e) return { history: s, memory: [] };
|
|
311
311
|
let r = 0, n = 0;
|
|
312
312
|
for (let u of s.toReversed())
|
|
313
313
|
if (n += this.estimateTokens(u.content), n < t) r++;
|
|
314
314
|
else break;
|
|
315
315
|
if (s.length <= r) return { history: s, memory: [] };
|
|
316
|
-
const
|
|
316
|
+
const a = s[0].role == "system" ? s[0] : null, i = r == 0 ? [] : s.slice(-r), o = (r == 0 ? s : s.slice(0, -r)).filter((u) => u.role === "assistant" || u.role === "user"), d = await this.json(o.map((u) => `${u.role}: ${u.content}`).join(`
|
|
317
317
|
|
|
318
318
|
`), "{summary: string, facts: [[subject, fact]]}", {
|
|
319
319
|
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:
|
|
321
|
-
temperature:
|
|
322
|
-
}), c = /* @__PURE__ */ new Date(),
|
|
320
|
+
model: m?.model,
|
|
321
|
+
temperature: m?.temperature || 0.3
|
|
322
|
+
}), c = /* @__PURE__ */ new Date(), l = await Promise.all((d?.facts || [])?.map(async ([u, f]) => {
|
|
323
323
|
const y = await Promise.all([this.embedding(u), this.embedding(`${u}: ${f}`)]);
|
|
324
324
|
return { owner: u, fact: f, embeddings: [y[0][0].embedding, y[1][0].embedding], timestamp: c };
|
|
325
|
-
})), p = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...
|
|
326
|
-
return
|
|
325
|
+
})), p = [{ role: "assistant", content: `Conversation Summary: ${d?.summary}`, timestamp: Date.now() }, ...i];
|
|
326
|
+
return a && p.splice(0, 0, a), { history: p, memory: l };
|
|
327
327
|
}
|
|
328
328
|
/**
|
|
329
329
|
* Compare the difference between embeddings (calculates the angle between two vectors)
|
|
@@ -333,10 +333,10 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
333
333
|
*/
|
|
334
334
|
cosineSimilarity(s, e) {
|
|
335
335
|
if (s.length !== e.length) throw new Error("Vectors must be same length");
|
|
336
|
-
let t = 0,
|
|
337
|
-
for (let
|
|
338
|
-
t += s[
|
|
339
|
-
const n = Math.sqrt(
|
|
336
|
+
let t = 0, m = 0, r = 0;
|
|
337
|
+
for (let a = 0; a < s.length; a++)
|
|
338
|
+
t += s[a] * e[a], m += s[a] * s[a], r += e[a] * e[a];
|
|
339
|
+
const n = Math.sqrt(m) * Math.sqrt(r);
|
|
340
340
|
return n === 0 ? 0 : t / n;
|
|
341
341
|
}
|
|
342
342
|
/**
|
|
@@ -347,25 +347,25 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
347
347
|
* @returns {string[]} Chunked strings
|
|
348
348
|
*/
|
|
349
349
|
chunk(s, e = 500, t = 50) {
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
return typeof c == "object" && !Array.isArray(c) ?
|
|
353
|
-
}) : [], n = (typeof s == "object" ?
|
|
354
|
-
`)).flatMap((
|
|
355
|
-
`]),
|
|
356
|
-
for (let
|
|
357
|
-
let
|
|
350
|
+
const m = (i, o = "") => i ? Object.entries(i).flatMap(([d, c]) => {
|
|
351
|
+
const l = o ? `${o}${isNaN(+d) ? `.${d}` : `[${d}]`}` : d;
|
|
352
|
+
return typeof c == "object" && !Array.isArray(c) ? m(c, l) : `${l}: ${Array.isArray(c) ? c.join(", ") : c}`;
|
|
353
|
+
}) : [], n = (typeof s == "object" ? m(s) : s.split(`
|
|
354
|
+
`)).flatMap((i) => [...i.split(/\s+/).filter(Boolean), `
|
|
355
|
+
`]), a = [];
|
|
356
|
+
for (let i = 0; i < n.length; ) {
|
|
357
|
+
let o = "", d = i;
|
|
358
358
|
for (; d < n.length; ) {
|
|
359
|
-
const
|
|
360
|
-
if (this.estimateTokens(
|
|
361
|
-
`)) > e &&
|
|
362
|
-
|
|
359
|
+
const l = o + (o ? " " : "") + n[d];
|
|
360
|
+
if (this.estimateTokens(l.replace(/\s*\n\s*/g, `
|
|
361
|
+
`)) > e && o) break;
|
|
362
|
+
o = l, d++;
|
|
363
363
|
}
|
|
364
|
-
const c =
|
|
364
|
+
const c = o.replace(/\s*\n\s*/g, `
|
|
365
365
|
`).trim();
|
|
366
|
-
c &&
|
|
366
|
+
c && a.push(c), i = Math.max(d - t, d === i ? i + 1 : d);
|
|
367
367
|
}
|
|
368
|
-
return
|
|
368
|
+
return a;
|
|
369
369
|
}
|
|
370
370
|
/**
|
|
371
371
|
* Create a vector representation of a string
|
|
@@ -375,19 +375,19 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
375
375
|
* @returns {Promise<Awaited<{index: number, embedding: number[], text: string, tokens: number}>[]>} Chunked embeddings
|
|
376
376
|
*/
|
|
377
377
|
embedding(s, e = 500, t = 50) {
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
}, c = (
|
|
382
|
-
|
|
378
|
+
const m = (n) => new Promise((a, i) => {
|
|
379
|
+
const o = new x(S(T(j(import.meta.url)), "embedder.js")), d = ({ embedding: l }) => {
|
|
380
|
+
o.terminate(), a(l);
|
|
381
|
+
}, c = (l) => {
|
|
382
|
+
o.terminate(), i(l);
|
|
383
383
|
};
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}),
|
|
384
|
+
o.on("message", d), o.on("error", c), o.on("exit", (l) => {
|
|
385
|
+
l !== 0 && i(new Error(`Worker exited with code ${l}`));
|
|
386
|
+
}), o.postMessage({ text: n, model: this.ai.options?.embedder || "bge-small-en-v1.5", modelDir: this.ai.options.path });
|
|
387
387
|
}), r = this.chunk(s, e, t);
|
|
388
|
-
return Promise.all(r.map(async (n,
|
|
389
|
-
index:
|
|
390
|
-
embedding: await
|
|
388
|
+
return Promise.all(r.map(async (n, a) => ({
|
|
389
|
+
index: a,
|
|
390
|
+
embedding: await m(n),
|
|
391
391
|
text: n,
|
|
392
392
|
tokens: this.estimateTokens(n)
|
|
393
393
|
})));
|
|
@@ -409,8 +409,8 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
409
409
|
*/
|
|
410
410
|
fuzzyMatch(s, ...e) {
|
|
411
411
|
if (e.length < 2) throw new Error("Requires at least 2 strings to compare");
|
|
412
|
-
const t = (n,
|
|
413
|
-
return { avg: r.reduce((n,
|
|
412
|
+
const t = (n, a = 10) => n.toLowerCase().split("").map((i, o) => i.charCodeAt(0) * (o + 1) % a / a).slice(0, a), m = t(s), r = e.map((n) => t(n)).map((n) => this.cosineSimilarity(m, n));
|
|
413
|
+
return { avg: r.reduce((n, a) => n + a, 0) / r.length, max: Math.max(...r), similarities: r };
|
|
414
414
|
}
|
|
415
415
|
/**
|
|
416
416
|
* Ask a question with JSON response
|
|
@@ -420,13 +420,13 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
420
420
|
* @returns {Promise<{} | {} | RegExpExecArray | null>}
|
|
421
421
|
*/
|
|
422
422
|
async json(s, e, t) {
|
|
423
|
-
let
|
|
423
|
+
let m = await this.ask(s, { ...t, system: (t?.system ? `${t.system}
|
|
424
424
|
` : "") + `Only respond using a JSON code block matching this schema:
|
|
425
425
|
\`\`\`json
|
|
426
426
|
${e}
|
|
427
427
|
\`\`\`` });
|
|
428
|
-
if (!
|
|
429
|
-
const r = /```(?:.+)?\s*([\s\S]*?)```/.exec(
|
|
428
|
+
if (!m) return {};
|
|
429
|
+
const r = /```(?:.+)?\s*([\s\S]*?)```/.exec(m), n = r ? r[1].trim() : m;
|
|
430
430
|
return g(n, {});
|
|
431
431
|
}
|
|
432
432
|
/**
|
|
@@ -445,35 +445,38 @@ class I {
|
|
|
445
445
|
this.ai = s;
|
|
446
446
|
}
|
|
447
447
|
asr(s, e = {}) {
|
|
448
|
-
const { model: t = this.ai.options.asr || "whisper-base", speaker:
|
|
448
|
+
const { model: t = this.ai.options.asr || "whisper-base", speaker: m = !1 } = e;
|
|
449
449
|
let r = !1;
|
|
450
450
|
const n = () => {
|
|
451
451
|
r = !0;
|
|
452
452
|
};
|
|
453
|
-
let
|
|
453
|
+
let a = new Promise((i, o) => {
|
|
454
454
|
const d = new x(S(T(j(import.meta.url)), "asr.js")), c = ({ text: p, warning: u, error: f }) => {
|
|
455
|
-
d.terminate(), !r && (f ?
|
|
456
|
-
},
|
|
457
|
-
d.terminate(), r ||
|
|
455
|
+
d.terminate(), !r && (f ? o(new Error(f)) : (u && console.warn(u), i(p)));
|
|
456
|
+
}, l = (p) => {
|
|
457
|
+
d.terminate(), r || o(p);
|
|
458
458
|
};
|
|
459
|
-
d.on("message", c), d.on("error",
|
|
460
|
-
p !== 0 && !r &&
|
|
461
|
-
}), d.postMessage({ file: s, model: t, speaker:
|
|
459
|
+
d.on("message", c), d.on("error", l), d.on("exit", (p) => {
|
|
460
|
+
p !== 0 && !r && o(new Error(`Worker exited with code ${p}`));
|
|
461
|
+
}), d.postMessage({ file: s, model: t, speaker: m, modelDir: this.ai.options.path, token: this.ai.options.hfToken });
|
|
462
462
|
});
|
|
463
463
|
if (e.speaker == "id") {
|
|
464
464
|
if (!this.ai.language.defaultModel) throw new Error("Configure an LLM for advanced ASR speaker detection");
|
|
465
|
-
|
|
466
|
-
if (!
|
|
467
|
-
|
|
465
|
+
a = a.then(async (i) => {
|
|
466
|
+
if (!i) return i;
|
|
467
|
+
let o = this.ai.language.chunk(i, 500, 0);
|
|
468
|
+
o.length > 4 && (o = [...o.slice(0, 3), o.at(-1)]);
|
|
469
|
+
const d = await this.ai.language.json(o.join(`
|
|
470
|
+
`), '{1: "Detected Name"}', {
|
|
468
471
|
system: "Use this following transcript to identify speakers. Only identify speakers you are sure about",
|
|
469
|
-
temperature: 0.
|
|
472
|
+
temperature: 0.1
|
|
470
473
|
});
|
|
471
|
-
return Object.entries(
|
|
472
|
-
|
|
473
|
-
}),
|
|
474
|
+
return Object.entries(d).forEach(([c, l]) => {
|
|
475
|
+
i = i.replaceAll(`[Speaker ${c}]`, `[${l}]`);
|
|
476
|
+
}), i;
|
|
474
477
|
});
|
|
475
478
|
}
|
|
476
|
-
return Object.assign(
|
|
479
|
+
return Object.assign(a, { abort: n });
|
|
477
480
|
}
|
|
478
481
|
canDiarization = R;
|
|
479
482
|
}
|
|
@@ -488,10 +491,10 @@ class J {
|
|
|
488
491
|
*/
|
|
489
492
|
ocr(s) {
|
|
490
493
|
let e;
|
|
491
|
-
const t = new Promise(async (
|
|
494
|
+
const t = new Promise(async (m) => {
|
|
492
495
|
e = await L(this.ai.options.ocr || "eng", 2, { cachePath: this.ai.options.path });
|
|
493
496
|
const { data: r } = await e.recognize(s);
|
|
494
|
-
await e.terminate(),
|
|
497
|
+
await e.terminate(), m(r.text.trim() || null);
|
|
495
498
|
});
|
|
496
499
|
return Object.assign(t, { abort: () => e?.terminate() });
|
|
497
500
|
}
|
|
@@ -581,16 +584,16 @@ const H = {
|
|
|
581
584
|
title: e('meta[property="og:title"]').attr("content") || e("title").text() || "",
|
|
582
585
|
description: e('meta[name="description"]').attr("content") || e('meta[property="og:description"]').attr("content") || ""
|
|
583
586
|
};
|
|
584
|
-
let
|
|
587
|
+
let m = "";
|
|
585
588
|
const r = ["article", "main", '[role="main"]', ".content", ".post", ".entry", "body"];
|
|
586
589
|
for (const n of r) {
|
|
587
|
-
const
|
|
588
|
-
if (
|
|
589
|
-
|
|
590
|
+
const a = e(n).first();
|
|
591
|
+
if (a.length && a.text().trim().length > 200) {
|
|
592
|
+
m = a.text();
|
|
590
593
|
break;
|
|
591
594
|
}
|
|
592
595
|
}
|
|
593
|
-
return
|
|
596
|
+
return m || (m = e("body").text()), m = m.replace(/\s+/g, " ").trim().slice(0, 8e3), { url: h.url, title: t.title.trim(), description: t.description.trim(), content: m, focus: h.focus };
|
|
594
597
|
}
|
|
595
598
|
}, ce = {
|
|
596
599
|
name: "web_search",
|
|
@@ -604,12 +607,12 @@ const H = {
|
|
|
604
607
|
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept-Language": "en-US,en;q=0.9" }
|
|
605
608
|
}).then((r) => r.text());
|
|
606
609
|
let e, t = /<a .*?href="(.+?)".+?<\/a>/g;
|
|
607
|
-
const
|
|
610
|
+
const m = new O();
|
|
608
611
|
for (; (e = t.exec(s)) !== null; ) {
|
|
609
612
|
let r = /uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];
|
|
610
|
-
if (r && (r = decodeURIComponent(r)), r &&
|
|
613
|
+
if (r && (r = decodeURIComponent(r)), r && m.add(r), m.size >= (h.length || 5)) break;
|
|
611
614
|
}
|
|
612
|
-
return
|
|
615
|
+
return m;
|
|
613
616
|
}
|
|
614
617
|
};
|
|
615
618
|
export {
|