@elizaos/plugin-knowledge 2.0.0-alpha.7 → 2.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.browser.js +2 -2
- package/dist/browser/index.browser.js.map +3 -3
- package/dist/cjs/index.node.cjs +283 -2907
- package/dist/cjs/index.node.cjs.map +6 -23
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.d.ts.map +1 -1
- package/dist/node/index.node.js +289 -2926
- package/dist/node/index.node.js.map +6 -23
- package/dist/routes.d.ts.map +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/cjs/index.node.cjs
CHANGED
|
@@ -63,6 +63,7 @@ var __export = (target, all) => {
|
|
|
63
63
|
// index.node.ts
|
|
64
64
|
var exports_index_node = {};
|
|
65
65
|
__export(exports_index_node, {
|
|
66
|
+
knowledgeRoutes: () => knowledgeRoutes,
|
|
66
67
|
knowledgeProvider: () => knowledgeProvider,
|
|
67
68
|
knowledgePluginHeadless: () => knowledgePluginHeadless,
|
|
68
69
|
knowledgePluginCore: () => knowledgePluginCore,
|
|
@@ -71,2869 +72,61 @@ __export(exports_index_node, {
|
|
|
71
72
|
createKnowledgePlugin: () => createKnowledgePlugin,
|
|
72
73
|
ModelConfigSchema: () => ModelConfigSchema,
|
|
73
74
|
KnowledgeServiceType: () => KnowledgeServiceType,
|
|
74
|
-
KnowledgeService: () => KnowledgeService
|
|
75
|
+
KnowledgeService: () => import_core5.KnowledgeService
|
|
75
76
|
});
|
|
76
77
|
module.exports = __toCommonJS(exports_index_node);
|
|
77
78
|
|
|
78
|
-
//
|
|
79
|
-
var fs2 = __toESM(require("node:fs"));
|
|
80
|
-
var path2 = __toESM(require("node:path"));
|
|
81
|
-
var import_core5 = require("@elizaos/core");
|
|
82
|
-
|
|
83
|
-
// service.ts
|
|
79
|
+
// index.ts
|
|
84
80
|
var import_core4 = require("@elizaos/core");
|
|
85
81
|
|
|
86
|
-
//
|
|
87
|
-
var
|
|
88
|
-
|
|
89
|
-
// types.ts
|
|
90
|
-
var import_zod = __toESM(require("zod"));
|
|
91
|
-
var ModelConfigSchema = import_zod.default.object({
|
|
92
|
-
EMBEDDING_PROVIDER: import_zod.default.enum(["openai", "google"]).optional(),
|
|
93
|
-
TEXT_PROVIDER: import_zod.default.enum(["openai", "anthropic", "openrouter", "google"]).optional(),
|
|
94
|
-
OPENAI_API_KEY: import_zod.default.string().optional(),
|
|
95
|
-
ANTHROPIC_API_KEY: import_zod.default.string().optional(),
|
|
96
|
-
OPENROUTER_API_KEY: import_zod.default.string().optional(),
|
|
97
|
-
GOOGLE_API_KEY: import_zod.default.string().optional(),
|
|
98
|
-
OPENAI_BASE_URL: import_zod.default.string().optional(),
|
|
99
|
-
ANTHROPIC_BASE_URL: import_zod.default.string().optional(),
|
|
100
|
-
OPENROUTER_BASE_URL: import_zod.default.string().optional(),
|
|
101
|
-
GOOGLE_BASE_URL: import_zod.default.string().optional(),
|
|
102
|
-
TEXT_EMBEDDING_MODEL: import_zod.default.string(),
|
|
103
|
-
TEXT_MODEL: import_zod.default.string().optional(),
|
|
104
|
-
MAX_INPUT_TOKENS: import_zod.default.string().or(import_zod.default.number()).transform((val) => typeof val === "string" ? parseInt(val, 10) : val),
|
|
105
|
-
MAX_OUTPUT_TOKENS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 4096),
|
|
106
|
-
EMBEDDING_DIMENSION: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 1536),
|
|
107
|
-
LOAD_DOCS_ON_STARTUP: import_zod.default.boolean().default(false),
|
|
108
|
-
CTX_KNOWLEDGE_ENABLED: import_zod.default.boolean().default(false),
|
|
109
|
-
RATE_LIMIT_ENABLED: import_zod.default.boolean().default(true),
|
|
110
|
-
MAX_CONCURRENT_REQUESTS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 150),
|
|
111
|
-
REQUESTS_PER_MINUTE: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 300),
|
|
112
|
-
TOKENS_PER_MINUTE: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 750000),
|
|
113
|
-
BATCH_DELAY_MS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 100)
|
|
114
|
-
});
|
|
115
|
-
var KnowledgeServiceType = {
|
|
116
|
-
KNOWLEDGE: "knowledge"
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// config.ts
|
|
120
|
-
var parseBooleanEnv = (value) => {
|
|
121
|
-
if (typeof value === "boolean")
|
|
122
|
-
return value;
|
|
123
|
-
if (typeof value === "number")
|
|
124
|
-
return value !== 0;
|
|
125
|
-
if (typeof value === "string")
|
|
126
|
-
return value.toLowerCase() === "true";
|
|
127
|
-
return false;
|
|
128
|
-
};
|
|
129
|
-
function validateModelConfig(runtime) {
|
|
130
|
-
try {
|
|
131
|
-
const getSetting = (key, defaultValue) => {
|
|
132
|
-
if (runtime) {
|
|
133
|
-
return runtime.getSetting(key) || process.env[key] || defaultValue;
|
|
134
|
-
}
|
|
135
|
-
return process.env[key] || defaultValue;
|
|
136
|
-
};
|
|
137
|
-
const ctxKnowledgeEnabled = parseBooleanEnv(getSetting("CTX_KNOWLEDGE_ENABLED", "false"));
|
|
138
|
-
const embeddingProvider = getSetting("EMBEDDING_PROVIDER");
|
|
139
|
-
const assumePluginOpenAI = !embeddingProvider;
|
|
140
|
-
const textEmbeddingModel = getSetting("TEXT_EMBEDDING_MODEL") || getSetting("OPENAI_EMBEDDING_MODEL") || "text-embedding-3-small";
|
|
141
|
-
const embeddingDimension = getSetting("EMBEDDING_DIMENSION") || getSetting("OPENAI_EMBEDDING_DIMENSIONS") || "1536";
|
|
142
|
-
const openaiApiKey = getSetting("OPENAI_API_KEY");
|
|
143
|
-
const config = ModelConfigSchema.parse({
|
|
144
|
-
EMBEDDING_PROVIDER: embeddingProvider,
|
|
145
|
-
TEXT_PROVIDER: getSetting("TEXT_PROVIDER"),
|
|
146
|
-
OPENAI_API_KEY: openaiApiKey,
|
|
147
|
-
ANTHROPIC_API_KEY: getSetting("ANTHROPIC_API_KEY"),
|
|
148
|
-
OPENROUTER_API_KEY: getSetting("OPENROUTER_API_KEY"),
|
|
149
|
-
GOOGLE_API_KEY: getSetting("GOOGLE_API_KEY"),
|
|
150
|
-
OPENAI_BASE_URL: getSetting("OPENAI_BASE_URL"),
|
|
151
|
-
ANTHROPIC_BASE_URL: getSetting("ANTHROPIC_BASE_URL"),
|
|
152
|
-
OPENROUTER_BASE_URL: getSetting("OPENROUTER_BASE_URL"),
|
|
153
|
-
GOOGLE_BASE_URL: getSetting("GOOGLE_BASE_URL"),
|
|
154
|
-
TEXT_EMBEDDING_MODEL: textEmbeddingModel,
|
|
155
|
-
TEXT_MODEL: getSetting("TEXT_MODEL"),
|
|
156
|
-
MAX_INPUT_TOKENS: getSetting("MAX_INPUT_TOKENS", "4000"),
|
|
157
|
-
MAX_OUTPUT_TOKENS: getSetting("MAX_OUTPUT_TOKENS", "4096"),
|
|
158
|
-
EMBEDDING_DIMENSION: embeddingDimension,
|
|
159
|
-
LOAD_DOCS_ON_STARTUP: parseBooleanEnv(getSetting("LOAD_DOCS_ON_STARTUP")),
|
|
160
|
-
CTX_KNOWLEDGE_ENABLED: ctxKnowledgeEnabled,
|
|
161
|
-
RATE_LIMIT_ENABLED: parseBooleanEnv(getSetting("RATE_LIMIT_ENABLED", "true")),
|
|
162
|
-
MAX_CONCURRENT_REQUESTS: getSetting("MAX_CONCURRENT_REQUESTS", "100"),
|
|
163
|
-
REQUESTS_PER_MINUTE: getSetting("REQUESTS_PER_MINUTE", "500"),
|
|
164
|
-
TOKENS_PER_MINUTE: getSetting("TOKENS_PER_MINUTE", "1000000"),
|
|
165
|
-
BATCH_DELAY_MS: getSetting("BATCH_DELAY_MS", "100")
|
|
166
|
-
});
|
|
167
|
-
validateConfigRequirements(config, assumePluginOpenAI);
|
|
168
|
-
return config;
|
|
169
|
-
} catch (error) {
|
|
170
|
-
if (error instanceof import_zod2.default.ZodError) {
|
|
171
|
-
const issues = error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
172
|
-
throw new Error(`Model configuration validation failed: ${issues}`);
|
|
173
|
-
}
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function validateConfigRequirements(config, assumePluginOpenAI) {
|
|
178
|
-
const embeddingProvider = config.EMBEDDING_PROVIDER;
|
|
179
|
-
if (embeddingProvider === "openai" && !config.OPENAI_API_KEY) {
|
|
180
|
-
throw new Error('OPENAI_API_KEY is required when EMBEDDING_PROVIDER is set to "openai"');
|
|
181
|
-
}
|
|
182
|
-
if (embeddingProvider === "google" && !config.GOOGLE_API_KEY) {
|
|
183
|
-
throw new Error('GOOGLE_API_KEY is required when EMBEDDING_PROVIDER is set to "google"');
|
|
184
|
-
}
|
|
185
|
-
if (assumePluginOpenAI && config.OPENAI_API_KEY && !config.TEXT_EMBEDDING_MODEL) {
|
|
186
|
-
throw new Error("OPENAI_EMBEDDING_MODEL is required when using plugin-openai configuration");
|
|
187
|
-
}
|
|
188
|
-
if (config.CTX_KNOWLEDGE_ENABLED) {
|
|
189
|
-
if (config.TEXT_PROVIDER === "openai" && !config.OPENAI_API_KEY) {
|
|
190
|
-
throw new Error('OPENAI_API_KEY is required when TEXT_PROVIDER is set to "openai"');
|
|
191
|
-
}
|
|
192
|
-
if (config.TEXT_PROVIDER === "anthropic" && !config.ANTHROPIC_API_KEY) {
|
|
193
|
-
throw new Error('ANTHROPIC_API_KEY is required when TEXT_PROVIDER is set to "anthropic"');
|
|
194
|
-
}
|
|
195
|
-
if (config.TEXT_PROVIDER === "openrouter" && !config.OPENROUTER_API_KEY) {
|
|
196
|
-
throw new Error('OPENROUTER_API_KEY is required when TEXT_PROVIDER is set to "openrouter"');
|
|
197
|
-
}
|
|
198
|
-
if (config.TEXT_PROVIDER === "google" && !config.GOOGLE_API_KEY) {
|
|
199
|
-
throw new Error('GOOGLE_API_KEY is required when TEXT_PROVIDER is set to "google"');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
async function getProviderRateLimits(runtime) {
|
|
204
|
-
const config = validateModelConfig(runtime);
|
|
205
|
-
const rateLimitEnabled = config.RATE_LIMIT_ENABLED;
|
|
206
|
-
const maxConcurrentRequests = config.MAX_CONCURRENT_REQUESTS;
|
|
207
|
-
const requestsPerMinute = config.REQUESTS_PER_MINUTE;
|
|
208
|
-
const tokensPerMinute = config.TOKENS_PER_MINUTE;
|
|
209
|
-
const batchDelayMs = config.BATCH_DELAY_MS;
|
|
210
|
-
const primaryProvider = config.TEXT_PROVIDER || config.EMBEDDING_PROVIDER;
|
|
211
|
-
if (!rateLimitEnabled) {
|
|
212
|
-
return {
|
|
213
|
-
maxConcurrentRequests,
|
|
214
|
-
requestsPerMinute: Number.MAX_SAFE_INTEGER,
|
|
215
|
-
tokensPerMinute: Number.MAX_SAFE_INTEGER,
|
|
216
|
-
provider: primaryProvider || "unlimited",
|
|
217
|
-
rateLimitEnabled: false,
|
|
218
|
-
batchDelayMs
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
maxConcurrentRequests,
|
|
223
|
-
requestsPerMinute,
|
|
224
|
-
tokensPerMinute,
|
|
225
|
-
provider: primaryProvider || "unlimited",
|
|
226
|
-
rateLimitEnabled: true,
|
|
227
|
-
batchDelayMs
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// docs-loader.ts
|
|
232
|
-
var fs = __toESM(require("node:fs"));
|
|
233
|
-
var path = __toESM(require("node:path"));
|
|
82
|
+
// routes.ts
|
|
83
|
+
var import_node_fs = __toESM(require("node:fs"));
|
|
84
|
+
var import_node_path = __toESM(require("node:path"));
|
|
234
85
|
var import_core = require("@elizaos/core");
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
var
|
|
239
|
-
var mammoth = __toESM(require("mammoth"));
|
|
240
|
-
var import_unpdf = require("unpdf");
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
var validate_default = validate;
|
|
265
|
-
|
|
266
|
-
// node_modules/uuid/dist-node/stringify.js
|
|
267
|
-
var byteToHex = [];
|
|
268
|
-
for (let i = 0;i < 256; ++i) {
|
|
269
|
-
byteToHex.push((i + 256).toString(16).slice(1));
|
|
270
|
-
}
|
|
271
|
-
function unsafeStringify(arr, offset = 0) {
|
|
272
|
-
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// node_modules/uuid/dist-node/v4.js
|
|
276
|
-
function _v4(options, buf, offset) {
|
|
277
|
-
options = options || {};
|
|
278
|
-
const rnds = options.random ?? options.rng?.() ?? rng();
|
|
279
|
-
if (rnds.length < 16) {
|
|
280
|
-
throw new Error("Random bytes length must be >= 16");
|
|
281
|
-
}
|
|
282
|
-
rnds[6] = rnds[6] & 15 | 64;
|
|
283
|
-
rnds[8] = rnds[8] & 63 | 128;
|
|
284
|
-
if (buf) {
|
|
285
|
-
offset = offset || 0;
|
|
286
|
-
if (offset < 0 || offset + 16 > buf.length) {
|
|
287
|
-
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
|
288
|
-
}
|
|
289
|
-
for (let i = 0;i < 16; ++i) {
|
|
290
|
-
buf[offset + i] = rnds[i];
|
|
291
|
-
}
|
|
292
|
-
return buf;
|
|
293
|
-
}
|
|
294
|
-
return unsafeStringify(rnds);
|
|
295
|
-
}
|
|
296
|
-
function v4(options, buf, offset) {
|
|
297
|
-
if (native_default.randomUUID && !buf && !options) {
|
|
298
|
-
return native_default.randomUUID();
|
|
299
|
-
}
|
|
300
|
-
return _v4(options, buf, offset);
|
|
301
|
-
}
|
|
302
|
-
var v4_default = v4;
|
|
303
|
-
// node_modules/uuid/dist-node/sha1.js
|
|
304
|
-
var import_node_crypto3 = require("node:crypto");
|
|
305
|
-
function sha1(bytes) {
|
|
306
|
-
if (Array.isArray(bytes)) {
|
|
307
|
-
bytes = Buffer.from(bytes);
|
|
308
|
-
} else if (typeof bytes === "string") {
|
|
309
|
-
bytes = Buffer.from(bytes, "utf8");
|
|
310
|
-
}
|
|
311
|
-
return import_node_crypto3.createHash("sha1").update(bytes).digest();
|
|
312
|
-
}
|
|
313
|
-
var sha1_default = sha1;
|
|
314
|
-
|
|
315
|
-
// node_modules/uuid/dist-node/parse.js
|
|
316
|
-
function parse(uuid) {
|
|
317
|
-
if (!validate_default(uuid)) {
|
|
318
|
-
throw TypeError("Invalid UUID");
|
|
319
|
-
}
|
|
320
|
-
let v;
|
|
321
|
-
return Uint8Array.of((v = parseInt(uuid.slice(0, 8), 16)) >>> 24, v >>> 16 & 255, v >>> 8 & 255, v & 255, (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(24, 36), 16)) / 1099511627776 & 255, v / 4294967296 & 255, v >>> 24 & 255, v >>> 16 & 255, v >>> 8 & 255, v & 255);
|
|
322
|
-
}
|
|
323
|
-
var parse_default = parse;
|
|
324
|
-
|
|
325
|
-
// node_modules/uuid/dist-node/v35.js
|
|
326
|
-
function stringToBytes(str) {
|
|
327
|
-
str = unescape(encodeURIComponent(str));
|
|
328
|
-
const bytes = new Uint8Array(str.length);
|
|
329
|
-
for (let i = 0;i < str.length; ++i) {
|
|
330
|
-
bytes[i] = str.charCodeAt(i);
|
|
331
|
-
}
|
|
332
|
-
return bytes;
|
|
333
|
-
}
|
|
334
|
-
var DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
|
|
335
|
-
var URL2 = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
|
|
336
|
-
function v35(version, hash, value, namespace, buf, offset) {
|
|
337
|
-
const valueBytes = typeof value === "string" ? stringToBytes(value) : value;
|
|
338
|
-
const namespaceBytes = typeof namespace === "string" ? parse_default(namespace) : namespace;
|
|
339
|
-
if (typeof namespace === "string") {
|
|
340
|
-
namespace = parse_default(namespace);
|
|
341
|
-
}
|
|
342
|
-
if (namespace?.length !== 16) {
|
|
343
|
-
throw TypeError("Namespace must be array-like (16 iterable integer values, 0-255)");
|
|
344
|
-
}
|
|
345
|
-
let bytes = new Uint8Array(16 + valueBytes.length);
|
|
346
|
-
bytes.set(namespaceBytes);
|
|
347
|
-
bytes.set(valueBytes, namespaceBytes.length);
|
|
348
|
-
bytes = hash(bytes);
|
|
349
|
-
bytes[6] = bytes[6] & 15 | version;
|
|
350
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
351
|
-
if (buf) {
|
|
352
|
-
offset = offset || 0;
|
|
353
|
-
for (let i = 0;i < 16; ++i) {
|
|
354
|
-
buf[offset + i] = bytes[i];
|
|
355
|
-
}
|
|
356
|
-
return buf;
|
|
357
|
-
}
|
|
358
|
-
return unsafeStringify(bytes);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// node_modules/uuid/dist-node/v5.js
|
|
362
|
-
function v5(value, namespace, buf, offset) {
|
|
363
|
-
return v35(80, sha1_default, value, namespace, buf, offset);
|
|
364
|
-
}
|
|
365
|
-
v5.DNS = DNS;
|
|
366
|
-
v5.URL = URL2;
|
|
367
|
-
var v5_default = v5;
|
|
368
|
-
// utils.ts
|
|
369
|
-
var PLAIN_TEXT_CONTENT_TYPES = [
|
|
370
|
-
"application/typescript",
|
|
371
|
-
"text/typescript",
|
|
372
|
-
"text/x-python",
|
|
373
|
-
"application/x-python-code",
|
|
374
|
-
"application/yaml",
|
|
375
|
-
"text/yaml",
|
|
376
|
-
"application/x-yaml",
|
|
377
|
-
"application/json",
|
|
378
|
-
"text/markdown",
|
|
379
|
-
"text/csv"
|
|
380
|
-
];
|
|
381
|
-
var MAX_FALLBACK_SIZE_BYTES = 5 * 1024 * 1024;
|
|
382
|
-
var BINARY_CHECK_BYTES = 1024;
|
|
383
|
-
async function extractTextFromFileBuffer(fileBuffer, contentType, originalFilename) {
|
|
384
|
-
const lowerContentType = contentType.toLowerCase();
|
|
385
|
-
if (lowerContentType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
|
|
386
|
-
try {
|
|
387
|
-
const result = await mammoth.extractRawText({ buffer: fileBuffer });
|
|
388
|
-
return result.value;
|
|
389
|
-
} catch (docxError) {
|
|
390
|
-
const errorMessage = docxError instanceof Error ? docxError.message : String(docxError);
|
|
391
|
-
throw new Error(`Failed to parse DOCX file ${originalFilename}: ${errorMessage}`);
|
|
392
|
-
}
|
|
393
|
-
} else if (lowerContentType === "application/msword" || originalFilename.toLowerCase().endsWith(".doc")) {
|
|
394
|
-
return `[Microsoft Word Document: ${originalFilename}]
|
|
395
|
-
|
|
396
|
-
This document was indexed for search but cannot be displayed directly in the browser. The original document content is preserved for retrieval purposes.`;
|
|
397
|
-
} else if (lowerContentType.startsWith("text/") || PLAIN_TEXT_CONTENT_TYPES.includes(lowerContentType)) {
|
|
398
|
-
return fileBuffer.toString("utf-8");
|
|
399
|
-
} else {
|
|
400
|
-
if (fileBuffer.length > MAX_FALLBACK_SIZE_BYTES) {
|
|
401
|
-
throw new Error(`File ${originalFilename} exceeds maximum size for fallback (${MAX_FALLBACK_SIZE_BYTES} bytes)`);
|
|
402
|
-
}
|
|
403
|
-
const initialBytes = fileBuffer.subarray(0, Math.min(fileBuffer.length, BINARY_CHECK_BYTES));
|
|
404
|
-
if (initialBytes.includes(0)) {
|
|
405
|
-
throw new Error(`File ${originalFilename} appears to be binary based on initial byte check`);
|
|
406
|
-
}
|
|
407
|
-
try {
|
|
408
|
-
const textContent = fileBuffer.toString("utf-8");
|
|
409
|
-
if (textContent.includes("�")) {
|
|
410
|
-
throw new Error(`File ${originalFilename} seems to be binary or has encoding issues (detected �)`);
|
|
411
|
-
}
|
|
412
|
-
return textContent;
|
|
413
|
-
} catch (_fallbackError) {
|
|
414
|
-
throw new Error(`Unsupported content type: ${contentType} for ${originalFilename}. Fallback to plain text failed`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
async function convertPdfToTextFromBuffer(pdfBuffer, _filename) {
|
|
419
|
-
try {
|
|
420
|
-
const uint8Array = new Uint8Array(pdfBuffer.buffer.slice(pdfBuffer.byteOffset, pdfBuffer.byteOffset + pdfBuffer.byteLength));
|
|
421
|
-
const result = await import_unpdf.extractText(uint8Array, {
|
|
422
|
-
mergePages: true
|
|
423
|
-
});
|
|
424
|
-
if (!result.text || result.text.trim().length === 0) {
|
|
425
|
-
return "";
|
|
426
|
-
}
|
|
427
|
-
const cleanedText = result.text.split(`
|
|
428
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0).join(`
|
|
429
|
-
`).replace(/\n{3,}/g, `
|
|
430
|
-
|
|
431
|
-
`);
|
|
432
|
-
return cleanedText;
|
|
433
|
-
} catch (error) {
|
|
434
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
435
|
-
throw new Error(`Failed to convert PDF to text: ${errorMessage}`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
function isBinaryContentType(contentType, filename) {
|
|
439
|
-
const textContentTypes = [
|
|
440
|
-
"text/",
|
|
441
|
-
"application/json",
|
|
442
|
-
"application/xml",
|
|
443
|
-
"application/javascript",
|
|
444
|
-
"application/typescript",
|
|
445
|
-
"application/x-yaml",
|
|
446
|
-
"application/x-sh"
|
|
447
|
-
];
|
|
448
|
-
const isTextMimeType = textContentTypes.some((type) => contentType.includes(type));
|
|
449
|
-
if (isTextMimeType) {
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
const binaryContentTypes = [
|
|
453
|
-
"application/pdf",
|
|
454
|
-
"application/msword",
|
|
455
|
-
"application/vnd.openxmlformats-officedocument",
|
|
456
|
-
"application/vnd.ms-excel",
|
|
457
|
-
"application/vnd.ms-powerpoint",
|
|
458
|
-
"application/zip",
|
|
459
|
-
"application/x-zip-compressed",
|
|
460
|
-
"application/octet-stream",
|
|
461
|
-
"image/",
|
|
462
|
-
"audio/",
|
|
463
|
-
"video/"
|
|
464
|
-
];
|
|
465
|
-
const isBinaryMimeType = binaryContentTypes.some((type) => contentType.includes(type));
|
|
466
|
-
if (isBinaryMimeType) {
|
|
467
|
-
return true;
|
|
468
|
-
}
|
|
469
|
-
const fileExt = filename.split(".").pop()?.toLowerCase() || "";
|
|
470
|
-
const textExtensions = [
|
|
471
|
-
"txt",
|
|
472
|
-
"md",
|
|
473
|
-
"markdown",
|
|
474
|
-
"json",
|
|
475
|
-
"xml",
|
|
476
|
-
"html",
|
|
477
|
-
"htm",
|
|
478
|
-
"css",
|
|
479
|
-
"js",
|
|
480
|
-
"ts",
|
|
481
|
-
"jsx",
|
|
482
|
-
"tsx",
|
|
483
|
-
"yaml",
|
|
484
|
-
"yml",
|
|
485
|
-
"toml",
|
|
486
|
-
"ini",
|
|
487
|
-
"cfg",
|
|
488
|
-
"conf",
|
|
489
|
-
"sh",
|
|
490
|
-
"bash",
|
|
491
|
-
"zsh",
|
|
492
|
-
"fish",
|
|
493
|
-
"py",
|
|
494
|
-
"rb",
|
|
495
|
-
"go",
|
|
496
|
-
"rs",
|
|
497
|
-
"java",
|
|
498
|
-
"c",
|
|
499
|
-
"cpp",
|
|
500
|
-
"h",
|
|
501
|
-
"hpp",
|
|
502
|
-
"cs",
|
|
503
|
-
"php",
|
|
504
|
-
"sql",
|
|
505
|
-
"r",
|
|
506
|
-
"swift",
|
|
507
|
-
"kt",
|
|
508
|
-
"scala",
|
|
509
|
-
"clj",
|
|
510
|
-
"ex",
|
|
511
|
-
"exs",
|
|
512
|
-
"vim",
|
|
513
|
-
"env",
|
|
514
|
-
"gitignore",
|
|
515
|
-
"dockerignore",
|
|
516
|
-
"editorconfig",
|
|
517
|
-
"log",
|
|
518
|
-
"csv",
|
|
519
|
-
"tsv",
|
|
520
|
-
"properties",
|
|
521
|
-
"gradle",
|
|
522
|
-
"sbt",
|
|
523
|
-
"makefile",
|
|
524
|
-
"dockerfile",
|
|
525
|
-
"vagrantfile",
|
|
526
|
-
"gemfile",
|
|
527
|
-
"rakefile",
|
|
528
|
-
"podfile",
|
|
529
|
-
"csproj",
|
|
530
|
-
"vbproj",
|
|
531
|
-
"fsproj",
|
|
532
|
-
"sln",
|
|
533
|
-
"pom"
|
|
534
|
-
];
|
|
535
|
-
if (textExtensions.includes(fileExt)) {
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
const binaryExtensions = [
|
|
539
|
-
"pdf",
|
|
540
|
-
"docx",
|
|
541
|
-
"doc",
|
|
542
|
-
"xls",
|
|
543
|
-
"xlsx",
|
|
544
|
-
"ppt",
|
|
545
|
-
"pptx",
|
|
546
|
-
"zip",
|
|
547
|
-
"rar",
|
|
548
|
-
"7z",
|
|
549
|
-
"tar",
|
|
550
|
-
"gz",
|
|
551
|
-
"bz2",
|
|
552
|
-
"xz",
|
|
553
|
-
"jpg",
|
|
554
|
-
"jpeg",
|
|
555
|
-
"png",
|
|
556
|
-
"gif",
|
|
557
|
-
"bmp",
|
|
558
|
-
"svg",
|
|
559
|
-
"ico",
|
|
560
|
-
"webp",
|
|
561
|
-
"mp3",
|
|
562
|
-
"mp4",
|
|
563
|
-
"avi",
|
|
564
|
-
"mov",
|
|
565
|
-
"wmv",
|
|
566
|
-
"flv",
|
|
567
|
-
"wav",
|
|
568
|
-
"flac",
|
|
569
|
-
"ogg",
|
|
570
|
-
"exe",
|
|
571
|
-
"dll",
|
|
572
|
-
"so",
|
|
573
|
-
"dylib",
|
|
574
|
-
"bin",
|
|
575
|
-
"dat",
|
|
576
|
-
"db",
|
|
577
|
-
"sqlite"
|
|
578
|
-
];
|
|
579
|
-
return binaryExtensions.includes(fileExt);
|
|
580
|
-
}
|
|
581
|
-
function normalizeS3Url(url) {
|
|
582
|
-
try {
|
|
583
|
-
const urlObj = new URL(url);
|
|
584
|
-
return `${urlObj.origin}${urlObj.pathname}`;
|
|
585
|
-
} catch {
|
|
586
|
-
return url;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
async function fetchUrlContent(url) {
|
|
590
|
-
try {
|
|
591
|
-
const controller = new AbortController;
|
|
592
|
-
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
593
|
-
const response = await fetch(url, {
|
|
594
|
-
signal: controller.signal,
|
|
595
|
-
headers: {
|
|
596
|
-
"User-Agent": "Eliza-Knowledge-Plugin/1.0"
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
clearTimeout(timeoutId);
|
|
600
|
-
if (!response.ok) {
|
|
601
|
-
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
|
|
602
|
-
}
|
|
603
|
-
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
604
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
605
|
-
const buffer = import_node_buffer.Buffer.from(arrayBuffer);
|
|
606
|
-
const base64Content = buffer.toString("base64");
|
|
607
|
-
return {
|
|
608
|
-
content: base64Content,
|
|
609
|
-
contentType
|
|
610
|
-
};
|
|
611
|
-
} catch (error) {
|
|
612
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
613
|
-
throw new Error(`Failed to fetch content from URL: ${errorMessage}`);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
function looksLikeBase64(content) {
|
|
617
|
-
if (!content || content.length === 0)
|
|
618
|
-
return false;
|
|
619
|
-
const cleanContent = content.replace(/\s/g, "");
|
|
620
|
-
if (cleanContent.length < 16)
|
|
621
|
-
return false;
|
|
622
|
-
if (cleanContent.length % 4 !== 0)
|
|
623
|
-
return false;
|
|
624
|
-
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
625
|
-
if (!base64Regex.test(cleanContent))
|
|
626
|
-
return false;
|
|
627
|
-
const hasNumbers = /\d/.test(cleanContent);
|
|
628
|
-
const hasUpperCase = /[A-Z]/.test(cleanContent);
|
|
629
|
-
const hasLowerCase = /[a-z]/.test(cleanContent);
|
|
630
|
-
return (hasNumbers || hasUpperCase) && hasLowerCase;
|
|
631
|
-
}
|
|
632
|
-
function generateContentBasedId(content, agentId, options) {
|
|
633
|
-
const { maxChars = 2000, includeFilename, contentType } = options || {};
|
|
634
|
-
let contentForHashing;
|
|
635
|
-
if (looksLikeBase64(content)) {
|
|
636
|
-
try {
|
|
637
|
-
const decoded = import_node_buffer.Buffer.from(content, "base64").toString("utf8");
|
|
638
|
-
if (!decoded.includes("�") || contentType?.includes("pdf")) {
|
|
639
|
-
contentForHashing = content.slice(0, maxChars);
|
|
640
|
-
} else {
|
|
641
|
-
contentForHashing = decoded.slice(0, maxChars);
|
|
642
|
-
}
|
|
643
|
-
} catch {
|
|
644
|
-
contentForHashing = content.slice(0, maxChars);
|
|
645
|
-
}
|
|
646
|
-
} else {
|
|
647
|
-
contentForHashing = content.slice(0, maxChars);
|
|
648
|
-
}
|
|
649
|
-
contentForHashing = contentForHashing.replace(/\r\n/g, `
|
|
650
|
-
`).replace(/\r/g, `
|
|
651
|
-
`).trim();
|
|
652
|
-
const componentsToHash = [agentId, contentForHashing, includeFilename || ""].filter(Boolean).join("::");
|
|
653
|
-
const hash = import_node_crypto4.createHash("sha256").update(componentsToHash).digest("hex");
|
|
654
|
-
const DOCUMENT_NAMESPACE = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
|
|
655
|
-
return v5_default(hash, DOCUMENT_NAMESPACE);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// docs-loader.ts
|
|
659
|
-
function getKnowledgePath(runtimePath) {
|
|
660
|
-
const knowledgePath = runtimePath || process.env.KNOWLEDGE_PATH || path.join(process.cwd(), "docs");
|
|
661
|
-
const resolvedPath = path.resolve(knowledgePath);
|
|
662
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
663
|
-
import_core.logger.warn(`Knowledge path does not exist: ${resolvedPath}`);
|
|
664
|
-
if (runtimePath) {
|
|
665
|
-
import_core.logger.warn("Please create the directory or update KNOWLEDGE_PATH in agent settings");
|
|
666
|
-
} else if (process.env.KNOWLEDGE_PATH) {
|
|
667
|
-
import_core.logger.warn("Please create the directory or update KNOWLEDGE_PATH environment variable");
|
|
668
|
-
} else {
|
|
669
|
-
import_core.logger.info("To use the knowledge plugin, either:");
|
|
670
|
-
import_core.logger.info('1. Create a "docs" folder in your project root');
|
|
671
|
-
import_core.logger.info("2. Set KNOWLEDGE_PATH in agent settings or environment variable");
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return resolvedPath;
|
|
675
|
-
}
|
|
676
|
-
async function loadDocsFromPath(service, agentId, worldId, knowledgePath) {
|
|
677
|
-
const docsPath = getKnowledgePath(knowledgePath);
|
|
678
|
-
if (!fs.existsSync(docsPath)) {
|
|
679
|
-
import_core.logger.warn(`Knowledge path does not exist: ${docsPath}`);
|
|
680
|
-
return { total: 0, successful: 0, failed: 0 };
|
|
681
|
-
}
|
|
682
|
-
import_core.logger.info(`Loading documents from: ${docsPath}`);
|
|
683
|
-
const files = getAllFiles(docsPath);
|
|
684
|
-
if (files.length === 0) {
|
|
685
|
-
import_core.logger.info("No files found in knowledge path");
|
|
686
|
-
return { total: 0, successful: 0, failed: 0 };
|
|
687
|
-
}
|
|
688
|
-
import_core.logger.info(`Found ${files.length} files to process`);
|
|
689
|
-
let successful = 0;
|
|
690
|
-
let failed = 0;
|
|
691
|
-
for (const filePath of files) {
|
|
692
|
-
try {
|
|
693
|
-
const fileName = path.basename(filePath);
|
|
694
|
-
const fileExt = path.extname(filePath).toLowerCase();
|
|
695
|
-
if (fileName.startsWith(".")) {
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
const contentType = getContentType(fileExt);
|
|
699
|
-
if (!contentType) {
|
|
700
|
-
import_core.logger.debug(`Skipping unsupported file type: ${filePath}`);
|
|
701
|
-
continue;
|
|
702
|
-
}
|
|
703
|
-
const fileBuffer = fs.readFileSync(filePath);
|
|
704
|
-
const isBinary = isBinaryContentType(contentType, fileName);
|
|
705
|
-
const content = isBinary ? fileBuffer.toString("base64") : fileBuffer.toString("utf-8");
|
|
706
|
-
const knowledgeOptions = {
|
|
707
|
-
clientDocumentId: "",
|
|
708
|
-
contentType,
|
|
709
|
-
originalFilename: fileName,
|
|
710
|
-
worldId: worldId || agentId,
|
|
711
|
-
content,
|
|
712
|
-
roomId: agentId,
|
|
713
|
-
entityId: agentId
|
|
714
|
-
};
|
|
715
|
-
import_core.logger.debug(`Processing document: ${fileName}`);
|
|
716
|
-
const result = await service.addKnowledge(knowledgeOptions);
|
|
717
|
-
import_core.logger.info(`✅ "${fileName}": ${result.fragmentCount} fragments created`);
|
|
718
|
-
successful++;
|
|
719
|
-
} catch (error) {
|
|
720
|
-
import_core.logger.error({ error }, `Failed to process file ${filePath}`);
|
|
721
|
-
failed++;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
import_core.logger.info(`Document loading complete: ${successful} successful, ${failed} failed out of ${files.length} total`);
|
|
725
|
-
return {
|
|
726
|
-
total: files.length,
|
|
727
|
-
successful,
|
|
728
|
-
failed
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
function getAllFiles(dirPath, files = []) {
|
|
732
|
-
try {
|
|
733
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
734
|
-
for (const entry of entries) {
|
|
735
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
736
|
-
if (entry.isDirectory()) {
|
|
737
|
-
if (!["node_modules", ".git", ".vscode", "dist", "build"].includes(entry.name)) {
|
|
738
|
-
getAllFiles(fullPath, files);
|
|
739
|
-
}
|
|
740
|
-
} else if (entry.isFile()) {
|
|
741
|
-
files.push(fullPath);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
} catch (error) {
|
|
745
|
-
import_core.logger.error({ error }, `Error reading directory ${dirPath}`);
|
|
746
|
-
}
|
|
747
|
-
return files;
|
|
748
|
-
}
|
|
749
|
-
function getContentType(extension) {
|
|
750
|
-
const contentTypes = {
|
|
751
|
-
".txt": "text/plain",
|
|
752
|
-
".md": "text/markdown",
|
|
753
|
-
".markdown": "text/markdown",
|
|
754
|
-
".tson": "text/plain",
|
|
755
|
-
".xml": "application/xml",
|
|
756
|
-
".csv": "text/csv",
|
|
757
|
-
".tsv": "text/tab-separated-values",
|
|
758
|
-
".log": "text/plain",
|
|
759
|
-
".html": "text/html",
|
|
760
|
-
".htm": "text/html",
|
|
761
|
-
".css": "text/css",
|
|
762
|
-
".scss": "text/x-scss",
|
|
763
|
-
".sass": "text/x-sass",
|
|
764
|
-
".less": "text/x-less",
|
|
765
|
-
".js": "text/javascript",
|
|
766
|
-
".jsx": "text/javascript",
|
|
767
|
-
".ts": "text/typescript",
|
|
768
|
-
".tsx": "text/typescript",
|
|
769
|
-
".mjs": "text/javascript",
|
|
770
|
-
".cjs": "text/javascript",
|
|
771
|
-
".vue": "text/x-vue",
|
|
772
|
-
".svelte": "text/x-svelte",
|
|
773
|
-
".astro": "text/x-astro",
|
|
774
|
-
".py": "text/x-python",
|
|
775
|
-
".pyw": "text/x-python",
|
|
776
|
-
".pyi": "text/x-python",
|
|
777
|
-
".java": "text/x-java",
|
|
778
|
-
".kt": "text/x-kotlin",
|
|
779
|
-
".kts": "text/x-kotlin",
|
|
780
|
-
".scala": "text/x-scala",
|
|
781
|
-
".c": "text/x-c",
|
|
782
|
-
".cpp": "text/x-c++",
|
|
783
|
-
".cc": "text/x-c++",
|
|
784
|
-
".cxx": "text/x-c++",
|
|
785
|
-
".h": "text/x-c",
|
|
786
|
-
".hpp": "text/x-c++",
|
|
787
|
-
".cs": "text/x-csharp",
|
|
788
|
-
".php": "text/x-php",
|
|
789
|
-
".rb": "text/x-ruby",
|
|
790
|
-
".go": "text/x-go",
|
|
791
|
-
".rs": "text/x-rust",
|
|
792
|
-
".swift": "text/x-swift",
|
|
793
|
-
".r": "text/x-r",
|
|
794
|
-
".R": "text/x-r",
|
|
795
|
-
".m": "text/x-objectivec",
|
|
796
|
-
".mm": "text/x-objectivec",
|
|
797
|
-
".clj": "text/x-clojure",
|
|
798
|
-
".cljs": "text/x-clojure",
|
|
799
|
-
".ex": "text/x-elixir",
|
|
800
|
-
".exs": "text/x-elixir",
|
|
801
|
-
".lua": "text/x-lua",
|
|
802
|
-
".pl": "text/x-perl",
|
|
803
|
-
".pm": "text/x-perl",
|
|
804
|
-
".dart": "text/x-dart",
|
|
805
|
-
".hs": "text/x-haskell",
|
|
806
|
-
".elm": "text/x-elm",
|
|
807
|
-
".ml": "text/x-ocaml",
|
|
808
|
-
".fs": "text/x-fsharp",
|
|
809
|
-
".fsx": "text/x-fsharp",
|
|
810
|
-
".vb": "text/x-vb",
|
|
811
|
-
".pas": "text/x-pascal",
|
|
812
|
-
".d": "text/x-d",
|
|
813
|
-
".nim": "text/x-nim",
|
|
814
|
-
".zig": "text/x-zig",
|
|
815
|
-
".jl": "text/x-julia",
|
|
816
|
-
".tcl": "text/x-tcl",
|
|
817
|
-
".awk": "text/x-awk",
|
|
818
|
-
".sed": "text/x-sed",
|
|
819
|
-
".sh": "text/x-sh",
|
|
820
|
-
".bash": "text/x-sh",
|
|
821
|
-
".zsh": "text/x-sh",
|
|
822
|
-
".fish": "text/x-fish",
|
|
823
|
-
".ps1": "text/x-powershell",
|
|
824
|
-
".bat": "text/x-batch",
|
|
825
|
-
".cmd": "text/x-batch",
|
|
826
|
-
".json": "application/json",
|
|
827
|
-
".yaml": "text/x-yaml",
|
|
828
|
-
".yml": "text/x-yaml",
|
|
829
|
-
".toml": "text/x-toml",
|
|
830
|
-
".ini": "text/x-ini",
|
|
831
|
-
".cfg": "text/x-ini",
|
|
832
|
-
".conf": "text/x-ini",
|
|
833
|
-
".env": "text/plain",
|
|
834
|
-
".gitignore": "text/plain",
|
|
835
|
-
".dockerignore": "text/plain",
|
|
836
|
-
".editorconfig": "text/plain",
|
|
837
|
-
".properties": "text/x-properties",
|
|
838
|
-
".sql": "text/x-sql",
|
|
839
|
-
".pdf": "application/pdf",
|
|
840
|
-
".doc": "application/msword",
|
|
841
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
842
|
-
};
|
|
843
|
-
return contentTypes[extension] || null;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// document-processor.ts
|
|
847
|
-
var import_core3 = require("@elizaos/core");
|
|
848
|
-
|
|
849
|
-
// ctx-embeddings.ts
|
|
850
|
-
var DEFAULT_CHUNK_TOKEN_SIZE = 500;
|
|
851
|
-
var DEFAULT_CHUNK_OVERLAP_TOKENS = 100;
|
|
852
|
-
var DEFAULT_CHARS_PER_TOKEN = 3.5;
|
|
853
|
-
var CONTEXT_TARGETS = {
|
|
854
|
-
DEFAULT: {
|
|
855
|
-
MIN_TOKENS: 60,
|
|
856
|
-
MAX_TOKENS: 120
|
|
857
|
-
},
|
|
858
|
-
PDF: {
|
|
859
|
-
MIN_TOKENS: 80,
|
|
860
|
-
MAX_TOKENS: 150
|
|
861
|
-
},
|
|
862
|
-
MATH_PDF: {
|
|
863
|
-
MIN_TOKENS: 100,
|
|
864
|
-
MAX_TOKENS: 180
|
|
865
|
-
},
|
|
866
|
-
CODE: {
|
|
867
|
-
MIN_TOKENS: 100,
|
|
868
|
-
MAX_TOKENS: 200
|
|
869
|
-
},
|
|
870
|
-
TECHNICAL: {
|
|
871
|
-
MIN_TOKENS: 80,
|
|
872
|
-
MAX_TOKENS: 160
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
var SYSTEM_PROMPTS = {
|
|
876
|
-
DEFAULT: "You are a precision text augmentation tool. Your task is to expand a given text chunk with its direct context from a larger document. You must: 1) Keep the original chunk intact; 2) Add critical context from surrounding text; 3) Never summarize or rephrase the original chunk; 4) Create contextually rich output for improved semantic retrieval.",
|
|
877
|
-
CODE: "You are a precision code augmentation tool. Your task is to expand a given code chunk with necessary context from the larger codebase. You must: 1) Keep the original code chunk intact with exact syntax and indentation; 2) Add relevant imports, function signatures, or class definitions; 3) Include critical surrounding code context; 4) Create contextually rich output that maintains correct syntax.",
|
|
878
|
-
PDF: "You are a precision document augmentation tool. Your task is to expand a given PDF text chunk with its direct context from the larger document. You must: 1) Keep the original chunk intact; 2) Add section headings, references, or figure captions; 3) Include text that immediately precedes and follows the chunk; 4) Create contextually rich output that maintains the document's original structure.",
|
|
879
|
-
MATH_PDF: "You are a precision mathematical content augmentation tool. Your task is to expand a given mathematical text chunk with essential context. You must: 1) Keep original mathematical notations and expressions exactly as they appear; 2) Add relevant definitions, theorems, or equations from elsewhere in the document; 3) Preserve all LaTeX or mathematical formatting; 4) Create contextually rich output for improved mathematical comprehension.",
|
|
880
|
-
TECHNICAL: "You are a precision technical documentation augmentation tool. Your task is to expand a technical document chunk with critical context. You must: 1) Keep the original chunk intact including all technical terminology; 2) Add relevant configuration examples, parameter definitions, or API references; 3) Include any prerequisite information; 4) Create contextually rich output that maintains technical accuracy."
|
|
881
|
-
};
|
|
882
|
-
var CONTEXTUAL_CHUNK_ENRICHMENT_PROMPT_TEMPLATE = `
|
|
883
|
-
<document>
|
|
884
|
-
{doc_content}
|
|
885
|
-
</document>
|
|
886
|
-
|
|
887
|
-
Here is the chunk we want to situate within the whole document:
|
|
888
|
-
<chunk>
|
|
889
|
-
{chunk_content}
|
|
890
|
-
</chunk>
|
|
891
|
-
|
|
892
|
-
Create an enriched version of this chunk by adding critical surrounding context. Follow these guidelines:
|
|
893
|
-
|
|
894
|
-
1. Identify the document's main topic and key information relevant to understanding this chunk
|
|
895
|
-
2. Include 2-3 sentences before the chunk that provide essential context
|
|
896
|
-
3. Include 2-3 sentences after the chunk that complete thoughts or provide resolution
|
|
897
|
-
4. For technical documents, include any definitions or explanations of terms used in the chunk
|
|
898
|
-
5. For narrative content, include character or setting information needed to understand the chunk
|
|
899
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
900
|
-
7. Do not use phrases like "this chunk discusses" - directly present the context
|
|
901
|
-
8. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
902
|
-
9. Format the response as a single coherent paragraph
|
|
903
|
-
|
|
904
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
905
|
-
var CACHED_CHUNK_PROMPT_TEMPLATE = `
|
|
906
|
-
Here is the chunk we want to situate within the whole document:
|
|
907
|
-
<chunk>
|
|
908
|
-
{chunk_content}
|
|
909
|
-
</chunk>
|
|
910
|
-
|
|
911
|
-
Create an enriched version of this chunk by adding critical surrounding context. Follow these guidelines:
|
|
912
|
-
|
|
913
|
-
1. Identify the document's main topic and key information relevant to understanding this chunk
|
|
914
|
-
2. Include 2-3 sentences before the chunk that provide essential context
|
|
915
|
-
3. Include 2-3 sentences after the chunk that complete thoughts or provide resolution
|
|
916
|
-
4. For technical documents, include any definitions or explanations of terms used in the chunk
|
|
917
|
-
5. For narrative content, include character or setting information needed to understand the chunk
|
|
918
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
919
|
-
7. Do not use phrases like "this chunk discusses" - directly present the context
|
|
920
|
-
8. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
921
|
-
9. Format the response as a single coherent paragraph
|
|
922
|
-
|
|
923
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
924
|
-
var CACHED_CODE_CHUNK_PROMPT_TEMPLATE = `
|
|
925
|
-
Here is the chunk of code we want to situate within the whole document:
|
|
926
|
-
<chunk>
|
|
927
|
-
{chunk_content}
|
|
928
|
-
</chunk>
|
|
929
|
-
|
|
930
|
-
Create an enriched version of this code chunk by adding critical surrounding context. Follow these guidelines:
|
|
931
|
-
|
|
932
|
-
1. Preserve ALL code syntax, indentation, and comments exactly as they appear
|
|
933
|
-
2. Include any import statements, function definitions, or class declarations that this code depends on
|
|
934
|
-
3. Add necessary type definitions or interfaces that are referenced in this chunk
|
|
935
|
-
4. Include any crucial comments from elsewhere in the document that explain this code
|
|
936
|
-
5. If there are key variable declarations or initializations earlier in the document, include those
|
|
937
|
-
6. Keep the original chunk COMPLETELY INTACT and UNCHANGED in your response
|
|
938
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
939
|
-
8. Do NOT include implementation details for functions that are only called but not defined in this chunk
|
|
940
|
-
|
|
941
|
-
Provide ONLY the enriched code chunk in your response:`;
|
|
942
|
-
var CACHED_MATH_PDF_PROMPT_TEMPLATE = `
|
|
943
|
-
Here is the chunk we want to situate within the whole document:
|
|
944
|
-
<chunk>
|
|
945
|
-
{chunk_content}
|
|
946
|
-
</chunk>
|
|
947
|
-
|
|
948
|
-
Create an enriched version of this chunk by adding critical surrounding context. This document contains mathematical content that requires special handling. Follow these guidelines:
|
|
949
|
-
|
|
950
|
-
1. Preserve ALL mathematical notation exactly as it appears in the chunk
|
|
951
|
-
2. Include any defining equations, variables, or parameters mentioned earlier in the document that relate to this chunk
|
|
952
|
-
3. Add section/subsection names or figure references if they help situate the chunk
|
|
953
|
-
4. If variables or symbols are defined elsewhere in the document, include these definitions
|
|
954
|
-
5. If mathematical expressions appear corrupted, try to infer their meaning from context
|
|
955
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
956
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
957
|
-
8. Format the response as a coherent mathematical explanation
|
|
958
|
-
|
|
959
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
960
|
-
var CACHED_TECHNICAL_PROMPT_TEMPLATE = `
|
|
961
|
-
Here is the chunk we want to situate within the whole document:
|
|
962
|
-
<chunk>
|
|
963
|
-
{chunk_content}
|
|
964
|
-
</chunk>
|
|
965
|
-
|
|
966
|
-
Create an enriched version of this chunk by adding critical surrounding context. This appears to be technical documentation that requires special handling. Follow these guidelines:
|
|
967
|
-
|
|
968
|
-
1. Preserve ALL technical terminology, product names, and version numbers exactly as they appear
|
|
969
|
-
2. Include any prerequisite information or requirements mentioned earlier in the document
|
|
970
|
-
3. Add section/subsection headings or navigation path to situate this chunk within the document structure
|
|
971
|
-
4. Include any definitions of technical terms, acronyms, or jargon used in this chunk
|
|
972
|
-
5. If this chunk references specific configurations, include relevant parameter explanations
|
|
973
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
974
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
975
|
-
8. Format the response maintaining any hierarchical structure present in the original
|
|
976
|
-
|
|
977
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
978
|
-
var MATH_PDF_PROMPT_TEMPLATE = `
|
|
979
|
-
<document>
|
|
980
|
-
{doc_content}
|
|
981
|
-
</document>
|
|
982
|
-
|
|
983
|
-
Here is the chunk we want to situate within the whole document:
|
|
984
|
-
<chunk>
|
|
985
|
-
{chunk_content}
|
|
986
|
-
</chunk>
|
|
987
|
-
|
|
988
|
-
Create an enriched version of this chunk by adding critical surrounding context. This document contains mathematical content that requires special handling. Follow these guidelines:
|
|
989
|
-
|
|
990
|
-
1. Preserve ALL mathematical notation exactly as it appears in the chunk
|
|
991
|
-
2. Include any defining equations, variables, or parameters mentioned earlier in the document that relate to this chunk
|
|
992
|
-
3. Add section/subsection names or figure references if they help situate the chunk
|
|
993
|
-
4. If variables or symbols are defined elsewhere in the document, include these definitions
|
|
994
|
-
5. If mathematical expressions appear corrupted, try to infer their meaning from context
|
|
995
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
996
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
997
|
-
8. Format the response as a coherent mathematical explanation
|
|
998
|
-
|
|
999
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
1000
|
-
var CODE_PROMPT_TEMPLATE = `
|
|
1001
|
-
<document>
|
|
1002
|
-
{doc_content}
|
|
1003
|
-
</document>
|
|
1004
|
-
|
|
1005
|
-
Here is the chunk of code we want to situate within the whole document:
|
|
1006
|
-
<chunk>
|
|
1007
|
-
{chunk_content}
|
|
1008
|
-
</chunk>
|
|
1009
|
-
|
|
1010
|
-
Create an enriched version of this code chunk by adding critical surrounding context. Follow these guidelines:
|
|
1011
|
-
|
|
1012
|
-
1. Preserve ALL code syntax, indentation, and comments exactly as they appear
|
|
1013
|
-
2. Include any import statements, function definitions, or class declarations that this code depends on
|
|
1014
|
-
3. Add necessary type definitions or interfaces that are referenced in this chunk
|
|
1015
|
-
4. Include any crucial comments from elsewhere in the document that explain this code
|
|
1016
|
-
5. If there are key variable declarations or initializations earlier in the document, include those
|
|
1017
|
-
6. Keep the original chunk COMPLETELY INTACT and UNCHANGED in your response
|
|
1018
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
1019
|
-
8. Do NOT include implementation details for functions that are only called but not defined in this chunk
|
|
1020
|
-
|
|
1021
|
-
Provide ONLY the enriched code chunk in your response:`;
|
|
1022
|
-
var TECHNICAL_PROMPT_TEMPLATE = `
|
|
1023
|
-
<document>
|
|
1024
|
-
{doc_content}
|
|
1025
|
-
</document>
|
|
1026
|
-
|
|
1027
|
-
Here is the chunk we want to situate within the whole document:
|
|
1028
|
-
<chunk>
|
|
1029
|
-
{chunk_content}
|
|
1030
|
-
</chunk>
|
|
1031
|
-
|
|
1032
|
-
Create an enriched version of this chunk by adding critical surrounding context. This appears to be technical documentation that requires special handling. Follow these guidelines:
|
|
1033
|
-
|
|
1034
|
-
1. Preserve ALL technical terminology, product names, and version numbers exactly as they appear
|
|
1035
|
-
2. Include any prerequisite information or requirements mentioned earlier in the document
|
|
1036
|
-
3. Add section/subsection headings or navigation path to situate this chunk within the document structure
|
|
1037
|
-
4. Include any definitions of technical terms, acronyms, or jargon used in this chunk
|
|
1038
|
-
5. If this chunk references specific configurations, include relevant parameter explanations
|
|
1039
|
-
6. Keep the original chunk text COMPLETELY INTACT and UNCHANGED in your response
|
|
1040
|
-
7. The total length should be between {min_tokens} and {max_tokens} tokens
|
|
1041
|
-
8. Format the response maintaining any hierarchical structure present in the original
|
|
1042
|
-
|
|
1043
|
-
Provide ONLY the enriched chunk text in your response:`;
|
|
1044
|
-
function getContextualizationPrompt(docContent, chunkContent, minTokens = CONTEXT_TARGETS.DEFAULT.MIN_TOKENS, maxTokens = CONTEXT_TARGETS.DEFAULT.MAX_TOKENS, promptTemplate = CONTEXTUAL_CHUNK_ENRICHMENT_PROMPT_TEMPLATE) {
|
|
1045
|
-
if (!docContent || !chunkContent) {
|
|
1046
|
-
return "Error: Document or chunk content missing.";
|
|
1047
|
-
}
|
|
1048
|
-
const chunkTokens = Math.ceil(chunkContent.length / DEFAULT_CHARS_PER_TOKEN);
|
|
1049
|
-
if (chunkTokens > maxTokens * 0.7) {
|
|
1050
|
-
maxTokens = Math.ceil(chunkTokens * 1.3);
|
|
1051
|
-
minTokens = chunkTokens;
|
|
1052
|
-
}
|
|
1053
|
-
return promptTemplate.replace("{doc_content}", docContent).replace("{chunk_content}", chunkContent).replace("{min_tokens}", minTokens.toString()).replace("{max_tokens}", maxTokens.toString());
|
|
1054
|
-
}
|
|
1055
|
-
function getCachingContextualizationPrompt(chunkContent, contentType, minTokens = CONTEXT_TARGETS.DEFAULT.MIN_TOKENS, maxTokens = CONTEXT_TARGETS.DEFAULT.MAX_TOKENS) {
|
|
1056
|
-
if (!chunkContent) {
|
|
1057
|
-
return {
|
|
1058
|
-
prompt: "Error: Chunk content missing.",
|
|
1059
|
-
systemPrompt: SYSTEM_PROMPTS.DEFAULT
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
const chunkTokens = Math.ceil(chunkContent.length / DEFAULT_CHARS_PER_TOKEN);
|
|
1063
|
-
if (chunkTokens > maxTokens * 0.7) {
|
|
1064
|
-
maxTokens = Math.ceil(chunkTokens * 1.3);
|
|
1065
|
-
minTokens = chunkTokens;
|
|
1066
|
-
}
|
|
1067
|
-
let promptTemplate = CACHED_CHUNK_PROMPT_TEMPLATE;
|
|
1068
|
-
let systemPrompt = SYSTEM_PROMPTS.DEFAULT;
|
|
1069
|
-
if (contentType) {
|
|
1070
|
-
if (contentType.includes("javascript") || contentType.includes("typescript") || contentType.includes("python") || contentType.includes("java") || contentType.includes("c++") || contentType.includes("code")) {
|
|
1071
|
-
promptTemplate = CACHED_CODE_CHUNK_PROMPT_TEMPLATE;
|
|
1072
|
-
systemPrompt = SYSTEM_PROMPTS.CODE;
|
|
1073
|
-
} else if (contentType.includes("pdf")) {
|
|
1074
|
-
if (containsMathematicalContent(chunkContent)) {
|
|
1075
|
-
promptTemplate = CACHED_MATH_PDF_PROMPT_TEMPLATE;
|
|
1076
|
-
systemPrompt = SYSTEM_PROMPTS.MATH_PDF;
|
|
1077
|
-
} else {
|
|
1078
|
-
systemPrompt = SYSTEM_PROMPTS.PDF;
|
|
1079
|
-
}
|
|
1080
|
-
} else if (contentType.includes("markdown") || contentType.includes("text/html") || isTechnicalDocumentation(chunkContent)) {
|
|
1081
|
-
promptTemplate = CACHED_TECHNICAL_PROMPT_TEMPLATE;
|
|
1082
|
-
systemPrompt = SYSTEM_PROMPTS.TECHNICAL;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
const formattedPrompt = promptTemplate.replace("{chunk_content}", chunkContent).replace("{min_tokens}", minTokens.toString()).replace("{max_tokens}", maxTokens.toString());
|
|
1086
|
-
return {
|
|
1087
|
-
prompt: formattedPrompt,
|
|
1088
|
-
systemPrompt
|
|
1089
|
-
};
|
|
1090
|
-
}
|
|
1091
|
-
function getPromptForMimeType(mimeType, docContent, chunkContent) {
|
|
1092
|
-
let minTokens = CONTEXT_TARGETS.DEFAULT.MIN_TOKENS;
|
|
1093
|
-
let maxTokens = CONTEXT_TARGETS.DEFAULT.MAX_TOKENS;
|
|
1094
|
-
let promptTemplate = CONTEXTUAL_CHUNK_ENRICHMENT_PROMPT_TEMPLATE;
|
|
1095
|
-
if (mimeType.includes("pdf")) {
|
|
1096
|
-
if (containsMathematicalContent(docContent)) {
|
|
1097
|
-
minTokens = CONTEXT_TARGETS.MATH_PDF.MIN_TOKENS;
|
|
1098
|
-
maxTokens = CONTEXT_TARGETS.MATH_PDF.MAX_TOKENS;
|
|
1099
|
-
promptTemplate = MATH_PDF_PROMPT_TEMPLATE;
|
|
1100
|
-
} else {
|
|
1101
|
-
minTokens = CONTEXT_TARGETS.PDF.MIN_TOKENS;
|
|
1102
|
-
maxTokens = CONTEXT_TARGETS.PDF.MAX_TOKENS;
|
|
1103
|
-
}
|
|
1104
|
-
} else if (mimeType.includes("javascript") || mimeType.includes("typescript") || mimeType.includes("python") || mimeType.includes("java") || mimeType.includes("c++") || mimeType.includes("code")) {
|
|
1105
|
-
minTokens = CONTEXT_TARGETS.CODE.MIN_TOKENS;
|
|
1106
|
-
maxTokens = CONTEXT_TARGETS.CODE.MAX_TOKENS;
|
|
1107
|
-
promptTemplate = CODE_PROMPT_TEMPLATE;
|
|
1108
|
-
} else if (isTechnicalDocumentation(docContent) || mimeType.includes("markdown") || mimeType.includes("text/html")) {
|
|
1109
|
-
minTokens = CONTEXT_TARGETS.TECHNICAL.MIN_TOKENS;
|
|
1110
|
-
maxTokens = CONTEXT_TARGETS.TECHNICAL.MAX_TOKENS;
|
|
1111
|
-
promptTemplate = TECHNICAL_PROMPT_TEMPLATE;
|
|
1112
|
-
}
|
|
1113
|
-
return getContextualizationPrompt(docContent, chunkContent, minTokens, maxTokens, promptTemplate);
|
|
1114
|
-
}
|
|
1115
|
-
function getCachingPromptForMimeType(mimeType, chunkContent) {
|
|
1116
|
-
let minTokens = CONTEXT_TARGETS.DEFAULT.MIN_TOKENS;
|
|
1117
|
-
let maxTokens = CONTEXT_TARGETS.DEFAULT.MAX_TOKENS;
|
|
1118
|
-
if (mimeType.includes("pdf")) {
|
|
1119
|
-
if (containsMathematicalContent(chunkContent)) {
|
|
1120
|
-
minTokens = CONTEXT_TARGETS.MATH_PDF.MIN_TOKENS;
|
|
1121
|
-
maxTokens = CONTEXT_TARGETS.MATH_PDF.MAX_TOKENS;
|
|
1122
|
-
} else {
|
|
1123
|
-
minTokens = CONTEXT_TARGETS.PDF.MIN_TOKENS;
|
|
1124
|
-
maxTokens = CONTEXT_TARGETS.PDF.MAX_TOKENS;
|
|
1125
|
-
}
|
|
1126
|
-
} else if (mimeType.includes("javascript") || mimeType.includes("typescript") || mimeType.includes("python") || mimeType.includes("java") || mimeType.includes("c++") || mimeType.includes("code")) {
|
|
1127
|
-
minTokens = CONTEXT_TARGETS.CODE.MIN_TOKENS;
|
|
1128
|
-
maxTokens = CONTEXT_TARGETS.CODE.MAX_TOKENS;
|
|
1129
|
-
} else if (isTechnicalDocumentation(chunkContent) || mimeType.includes("markdown") || mimeType.includes("text/html")) {
|
|
1130
|
-
minTokens = CONTEXT_TARGETS.TECHNICAL.MIN_TOKENS;
|
|
1131
|
-
maxTokens = CONTEXT_TARGETS.TECHNICAL.MAX_TOKENS;
|
|
1132
|
-
}
|
|
1133
|
-
return getCachingContextualizationPrompt(chunkContent, mimeType, minTokens, maxTokens);
|
|
1134
|
-
}
|
|
1135
|
-
function containsMathematicalContent(content) {
|
|
1136
|
-
const latexMathPatterns = [
|
|
1137
|
-
/\$\$.+?\$\$/s,
|
|
1138
|
-
/\$.+?\$/g,
|
|
1139
|
-
/\\begin\{equation\}/,
|
|
1140
|
-
/\\begin\{align\}/,
|
|
1141
|
-
/\\sum_/,
|
|
1142
|
-
/\\int/,
|
|
1143
|
-
/\\frac\{/,
|
|
1144
|
-
/\\sqrt\{/,
|
|
1145
|
-
/\\alpha|\\beta|\\gamma|\\delta|\\theta|\\lambda|\\sigma/,
|
|
1146
|
-
/\\nabla|\\partial/
|
|
1147
|
-
];
|
|
1148
|
-
const generalMathPatterns = [
|
|
1149
|
-
/[≠≤≥±∞∫∂∑∏√∈∉⊆⊇⊂⊃∪∩]/,
|
|
1150
|
-
/\b[a-zA-Z]\^[0-9]/,
|
|
1151
|
-
/\(\s*-?\d+(\.\d+)?\s*,\s*-?\d+(\.\d+)?\s*\)/,
|
|
1152
|
-
/\b[xyz]\s*=\s*-?\d+(\.\d+)?/,
|
|
1153
|
-
/\[\s*-?\d+(\.\d+)?\s*,\s*-?\d+(\.\d+)?\s*\]/,
|
|
1154
|
-
/\b\d+\s*×\s*\d+/
|
|
1155
|
-
];
|
|
1156
|
-
for (const pattern of latexMathPatterns) {
|
|
1157
|
-
if (pattern.test(content)) {
|
|
1158
|
-
return true;
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
for (const pattern of generalMathPatterns) {
|
|
1162
|
-
if (pattern.test(content)) {
|
|
1163
|
-
return true;
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
const mathKeywords = [
|
|
1167
|
-
"theorem",
|
|
1168
|
-
"lemma",
|
|
1169
|
-
"proof",
|
|
1170
|
-
"equation",
|
|
1171
|
-
"function",
|
|
1172
|
-
"derivative",
|
|
1173
|
-
"integral",
|
|
1174
|
-
"matrix",
|
|
1175
|
-
"vector",
|
|
1176
|
-
"algorithm",
|
|
1177
|
-
"constraint",
|
|
1178
|
-
"coefficient"
|
|
1179
|
-
];
|
|
1180
|
-
const contentLower = content.toLowerCase();
|
|
1181
|
-
const mathKeywordCount = mathKeywords.filter((keyword) => contentLower.includes(keyword)).length;
|
|
1182
|
-
return mathKeywordCount >= 2;
|
|
1183
|
-
}
|
|
1184
|
-
function isTechnicalDocumentation(content) {
|
|
1185
|
-
const technicalPatterns = [
|
|
1186
|
-
/\b(version|v)\s*\d+\.\d+(\.\d+)?/i,
|
|
1187
|
-
/\b(api|sdk|cli)\b/i,
|
|
1188
|
-
/\b(http|https|ftp):\/\//i,
|
|
1189
|
-
/\b(GET|POST|PUT|DELETE)\b/,
|
|
1190
|
-
/<\/?[a-z][\s\S]*>/i,
|
|
1191
|
-
/\bREADME\b|\bCHANGELOG\b/i,
|
|
1192
|
-
/\b(config|configuration)\b/i,
|
|
1193
|
-
/\b(parameter|param|argument|arg)\b/i
|
|
1194
|
-
];
|
|
1195
|
-
const docHeadings = [
|
|
1196
|
-
/\b(Introduction|Overview|Getting Started|Installation|Usage|API Reference|Troubleshooting)\b/i
|
|
1197
|
-
];
|
|
1198
|
-
for (const pattern of [...technicalPatterns, ...docHeadings]) {
|
|
1199
|
-
if (pattern.test(content)) {
|
|
1200
|
-
return true;
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
const listPatterns = [/\d+\.\s.+\n\d+\.\s.+/, /•\s.+\n•\s.+/, /\*\s.+\n\*\s.+/, /-\s.+\n-\s.+/];
|
|
1204
|
-
for (const pattern of listPatterns) {
|
|
1205
|
-
if (pattern.test(content)) {
|
|
1206
|
-
return true;
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
return false;
|
|
1210
|
-
}
|
|
1211
|
-
function getChunkWithContext(chunkContent, generatedContext) {
|
|
1212
|
-
if (!generatedContext || generatedContext.trim() === "") {
|
|
1213
|
-
return chunkContent;
|
|
1214
|
-
}
|
|
1215
|
-
return generatedContext.trim();
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// llm.ts
|
|
1219
|
-
var import_anthropic = require("@ai-sdk/anthropic");
|
|
1220
|
-
var import_google = require("@ai-sdk/google");
|
|
1221
|
-
var import_openai = require("@ai-sdk/openai");
|
|
1222
|
-
var import_core2 = require("@elizaos/core");
|
|
1223
|
-
var import_ai_sdk_provider = require("@openrouter/ai-sdk-provider");
|
|
1224
|
-
var import_ai = require("ai");
|
|
1225
|
-
async function generateText(runtime, prompt, system, overrideConfig) {
|
|
1226
|
-
const config = validateModelConfig(runtime);
|
|
1227
|
-
const provider = overrideConfig?.provider || config.TEXT_PROVIDER;
|
|
1228
|
-
const modelName = overrideConfig?.modelName || config.TEXT_MODEL;
|
|
1229
|
-
const maxTokens = overrideConfig?.maxTokens || config.MAX_OUTPUT_TOKENS;
|
|
1230
|
-
const autoCacheContextualRetrieval = overrideConfig?.autoCacheContextualRetrieval !== false;
|
|
1231
|
-
if (!modelName) {
|
|
1232
|
-
throw new Error(`No model name configured for provider: ${provider}`);
|
|
1233
|
-
}
|
|
1234
|
-
try {
|
|
1235
|
-
switch (provider) {
|
|
1236
|
-
case "anthropic":
|
|
1237
|
-
return await generateAnthropicText(config, prompt, system, modelName, maxTokens);
|
|
1238
|
-
case "openai":
|
|
1239
|
-
return await generateOpenAIText(config, prompt, system, modelName, maxTokens);
|
|
1240
|
-
case "openrouter":
|
|
1241
|
-
return await generateOpenRouterText(config, prompt, system, modelName, maxTokens, overrideConfig?.cacheDocument, overrideConfig?.cacheOptions, autoCacheContextualRetrieval);
|
|
1242
|
-
case "google":
|
|
1243
|
-
return await generateGoogleText(prompt, system, modelName, maxTokens, config);
|
|
1244
|
-
default:
|
|
1245
|
-
throw new Error(`Unsupported text provider: ${provider}`);
|
|
1246
|
-
}
|
|
1247
|
-
} catch (error) {
|
|
1248
|
-
import_core2.logger.error({ error }, `${provider} ${modelName} error`);
|
|
1249
|
-
throw error;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
async function generateAnthropicText(config, prompt, system, modelName, maxTokens) {
|
|
1253
|
-
const anthropic = import_anthropic.createAnthropic({
|
|
1254
|
-
apiKey: config.ANTHROPIC_API_KEY,
|
|
1255
|
-
baseURL: config.ANTHROPIC_BASE_URL
|
|
1256
|
-
});
|
|
1257
|
-
const modelInstance = anthropic(modelName);
|
|
1258
|
-
const maxRetries = 3;
|
|
1259
|
-
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
1260
|
-
try {
|
|
1261
|
-
return await import_ai.generateText({
|
|
1262
|
-
model: modelInstance,
|
|
1263
|
-
prompt,
|
|
1264
|
-
system,
|
|
1265
|
-
temperature: 0.3,
|
|
1266
|
-
maxOutputTokens: maxTokens
|
|
1267
|
-
});
|
|
1268
|
-
} catch (error) {
|
|
1269
|
-
const errorObj = error;
|
|
1270
|
-
const isRateLimit = errorObj?.status === 429 || errorObj?.message?.includes("rate limit") || errorObj?.message?.includes("429");
|
|
1271
|
-
if (isRateLimit && attempt < maxRetries - 1) {
|
|
1272
|
-
const delay = 2 ** (attempt + 1) * 1000;
|
|
1273
|
-
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
1274
|
-
continue;
|
|
1275
|
-
}
|
|
1276
|
-
throw error;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
throw new Error("Max retries exceeded for Anthropic text generation");
|
|
1280
|
-
}
|
|
1281
|
-
async function generateOpenAIText(config, prompt, system, modelName, maxTokens) {
|
|
1282
|
-
const openai = import_openai.createOpenAI({
|
|
1283
|
-
apiKey: config.OPENAI_API_KEY,
|
|
1284
|
-
baseURL: config.OPENAI_BASE_URL
|
|
1285
|
-
});
|
|
1286
|
-
const modelInstance = openai.chat(modelName);
|
|
1287
|
-
const result = await import_ai.generateText({
|
|
1288
|
-
model: modelInstance,
|
|
1289
|
-
prompt,
|
|
1290
|
-
system,
|
|
1291
|
-
temperature: 0.3,
|
|
1292
|
-
maxOutputTokens: maxTokens
|
|
1293
|
-
});
|
|
1294
|
-
return result;
|
|
1295
|
-
}
|
|
1296
|
-
async function generateGoogleText(prompt, system, modelName, maxTokens, config) {
|
|
1297
|
-
const googleProvider = import_google.google;
|
|
1298
|
-
if (config.GOOGLE_API_KEY) {
|
|
1299
|
-
process.env.GOOGLE_GENERATIVE_AI_API_KEY = config.GOOGLE_API_KEY;
|
|
1300
|
-
}
|
|
1301
|
-
const modelInstance = googleProvider(modelName);
|
|
1302
|
-
const result = await import_ai.generateText({
|
|
1303
|
-
model: modelInstance,
|
|
1304
|
-
prompt,
|
|
1305
|
-
system,
|
|
1306
|
-
temperature: 0.3,
|
|
1307
|
-
maxOutputTokens: maxTokens
|
|
1308
|
-
});
|
|
1309
|
-
return result;
|
|
1310
|
-
}
|
|
1311
|
-
async function generateOpenRouterText(config, prompt, system, modelName, maxTokens, cacheDocument, _cacheOptions, autoCacheContextualRetrieval = true) {
|
|
1312
|
-
const openrouter = import_ai_sdk_provider.createOpenRouter({
|
|
1313
|
-
apiKey: config.OPENROUTER_API_KEY,
|
|
1314
|
-
baseURL: config.OPENROUTER_BASE_URL
|
|
1315
|
-
});
|
|
1316
|
-
const modelInstance = openrouter.chat(modelName);
|
|
1317
|
-
const isClaudeModel = modelName.toLowerCase().includes("claude");
|
|
1318
|
-
const isGeminiModel = modelName.toLowerCase().includes("gemini");
|
|
1319
|
-
const isGemini25Model = modelName.toLowerCase().includes("gemini-2.5");
|
|
1320
|
-
const supportsCaching = isClaudeModel || isGeminiModel;
|
|
1321
|
-
let documentForCaching = cacheDocument;
|
|
1322
|
-
if (!documentForCaching && autoCacheContextualRetrieval && supportsCaching) {
|
|
1323
|
-
const docMatch = prompt.match(/<document>([\s\S]*?)<\/document>/);
|
|
1324
|
-
if (docMatch?.[1]) {
|
|
1325
|
-
documentForCaching = docMatch[1].trim();
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
if (documentForCaching && supportsCaching) {
|
|
1329
|
-
let promptText = prompt;
|
|
1330
|
-
if (promptText.includes("<document>")) {
|
|
1331
|
-
promptText = promptText.replace(/<document>[\s\S]*?<\/document>/, "").trim();
|
|
1332
|
-
}
|
|
1333
|
-
if (isClaudeModel) {
|
|
1334
|
-
return await generateClaudeWithCaching(promptText, system, modelInstance, modelName, maxTokens, documentForCaching);
|
|
1335
|
-
} else if (isGeminiModel) {
|
|
1336
|
-
return await generateGeminiWithCaching(promptText, system, modelInstance, modelName, maxTokens, documentForCaching, isGemini25Model);
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
return await generateStandardOpenRouterText(prompt, system, modelInstance, modelName, maxTokens);
|
|
1340
|
-
}
|
|
1341
|
-
async function generateClaudeWithCaching(promptText, system, modelInstance, modelName, maxTokens, documentForCaching) {
|
|
1342
|
-
const messages = [
|
|
1343
|
-
system ? {
|
|
1344
|
-
role: "system",
|
|
1345
|
-
content: [
|
|
1346
|
-
{
|
|
1347
|
-
type: "text",
|
|
1348
|
-
text: system
|
|
1349
|
-
},
|
|
1350
|
-
{
|
|
1351
|
-
type: "text",
|
|
1352
|
-
text: documentForCaching,
|
|
1353
|
-
cache_control: {
|
|
1354
|
-
type: "ephemeral"
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
]
|
|
1358
|
-
} : {
|
|
1359
|
-
role: "user",
|
|
1360
|
-
content: [
|
|
1361
|
-
{
|
|
1362
|
-
type: "text",
|
|
1363
|
-
text: "Document for context:"
|
|
1364
|
-
},
|
|
1365
|
-
{
|
|
1366
|
-
type: "text",
|
|
1367
|
-
text: documentForCaching,
|
|
1368
|
-
cache_control: {
|
|
1369
|
-
type: "ephemeral"
|
|
1370
|
-
}
|
|
1371
|
-
},
|
|
1372
|
-
{
|
|
1373
|
-
type: "text",
|
|
1374
|
-
text: promptText
|
|
1375
|
-
}
|
|
1376
|
-
]
|
|
1377
|
-
},
|
|
1378
|
-
system ? {
|
|
1379
|
-
role: "user",
|
|
1380
|
-
content: [
|
|
1381
|
-
{
|
|
1382
|
-
type: "text",
|
|
1383
|
-
text: promptText
|
|
1384
|
-
}
|
|
1385
|
-
]
|
|
1386
|
-
} : null
|
|
1387
|
-
].filter(Boolean);
|
|
1388
|
-
const result = await import_ai.generateText({
|
|
1389
|
-
model: modelInstance,
|
|
1390
|
-
messages,
|
|
1391
|
-
temperature: 0.3,
|
|
1392
|
-
maxOutputTokens: maxTokens,
|
|
1393
|
-
providerOptions: {
|
|
1394
|
-
openrouter: {
|
|
1395
|
-
usage: {
|
|
1396
|
-
include: true
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
});
|
|
1401
|
-
logCacheMetrics(result);
|
|
1402
|
-
const totalTokens = (result.usage.inputTokens || 0) + (result.usage.outputTokens || 0);
|
|
1403
|
-
import_core2.logger.debug(`OpenRouter ${modelName}: ${totalTokens} tokens (${result.usage.inputTokens || 0}→${result.usage.outputTokens || 0})`);
|
|
1404
|
-
return result;
|
|
1405
|
-
}
|
|
1406
|
-
async function generateGeminiWithCaching(promptText, system, modelInstance, modelName, maxTokens, documentForCaching, _isGemini25Model) {
|
|
1407
|
-
const geminiSystemPrefix = system ? `${system}
|
|
1408
|
-
|
|
1409
|
-
` : "";
|
|
1410
|
-
const geminiPrompt = `${geminiSystemPrefix}${documentForCaching}
|
|
1411
|
-
|
|
1412
|
-
${promptText}`;
|
|
1413
|
-
const result = await import_ai.generateText({
|
|
1414
|
-
model: modelInstance,
|
|
1415
|
-
prompt: geminiPrompt,
|
|
1416
|
-
temperature: 0.3,
|
|
1417
|
-
maxOutputTokens: maxTokens,
|
|
1418
|
-
providerOptions: {
|
|
1419
|
-
openrouter: {
|
|
1420
|
-
usage: {
|
|
1421
|
-
include: true
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
});
|
|
1426
|
-
logCacheMetrics(result);
|
|
1427
|
-
const totalTokens = (result.usage.inputTokens || 0) + (result.usage.outputTokens || 0);
|
|
1428
|
-
import_core2.logger.debug(`OpenRouter ${modelName}: ${totalTokens} tokens (${result.usage.inputTokens || 0}→${result.usage.outputTokens || 0})`);
|
|
1429
|
-
return result;
|
|
1430
|
-
}
|
|
1431
|
-
async function generateStandardOpenRouterText(prompt, system, modelInstance, modelName, maxTokens) {
|
|
1432
|
-
const result = await import_ai.generateText({
|
|
1433
|
-
model: modelInstance,
|
|
1434
|
-
prompt,
|
|
1435
|
-
system,
|
|
1436
|
-
temperature: 0.3,
|
|
1437
|
-
maxOutputTokens: maxTokens,
|
|
1438
|
-
providerOptions: {
|
|
1439
|
-
openrouter: {
|
|
1440
|
-
usage: {
|
|
1441
|
-
include: true
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
const totalTokens = (result.usage.inputTokens || 0) + (result.usage.outputTokens || 0);
|
|
1447
|
-
import_core2.logger.debug(`OpenRouter ${modelName}: ${totalTokens} tokens (${result.usage.inputTokens || 0}→${result.usage.outputTokens || 0})`);
|
|
1448
|
-
return result;
|
|
1449
|
-
}
|
|
1450
|
-
function logCacheMetrics(_result) {}
|
|
1451
|
-
|
|
1452
|
-
// document-processor.ts
|
|
1453
|
-
function estimateTokens(text) {
|
|
1454
|
-
return Math.ceil(text.length / 4);
|
|
1455
|
-
}
|
|
1456
|
-
function getCtxKnowledgeEnabled(runtime) {
|
|
1457
|
-
let result;
|
|
1458
|
-
let _source;
|
|
1459
|
-
let rawValue;
|
|
1460
|
-
if (runtime) {
|
|
1461
|
-
const settingValue = runtime.getSetting("CTX_KNOWLEDGE_ENABLED");
|
|
1462
|
-
rawValue = typeof settingValue === "string" ? settingValue : settingValue?.toString();
|
|
1463
|
-
const cleanValue = rawValue?.trim().toLowerCase();
|
|
1464
|
-
result = cleanValue === "true";
|
|
1465
|
-
} else {
|
|
1466
|
-
rawValue = process.env.CTX_KNOWLEDGE_ENABLED;
|
|
1467
|
-
const cleanValue = rawValue?.toString().trim().toLowerCase();
|
|
1468
|
-
result = cleanValue === "true";
|
|
1469
|
-
}
|
|
1470
|
-
return result;
|
|
1471
|
-
}
|
|
1472
|
-
function shouldUseCustomLLM() {
|
|
1473
|
-
const textProvider = process.env.TEXT_PROVIDER;
|
|
1474
|
-
const textModel = process.env.TEXT_MODEL;
|
|
1475
|
-
if (!textProvider || !textModel) {
|
|
1476
|
-
return false;
|
|
1477
|
-
}
|
|
1478
|
-
switch (textProvider.toLowerCase()) {
|
|
1479
|
-
case "openrouter":
|
|
1480
|
-
return !!process.env.OPENROUTER_API_KEY;
|
|
1481
|
-
case "openai":
|
|
1482
|
-
return !!process.env.OPENAI_API_KEY;
|
|
1483
|
-
case "anthropic":
|
|
1484
|
-
return !!process.env.ANTHROPIC_API_KEY;
|
|
1485
|
-
case "google":
|
|
1486
|
-
return !!process.env.GOOGLE_API_KEY;
|
|
1487
|
-
default:
|
|
1488
|
-
return false;
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
var useCustomLLM = shouldUseCustomLLM();
|
|
1492
|
-
async function processFragmentsSynchronously({
|
|
1493
|
-
runtime,
|
|
1494
|
-
documentId,
|
|
1495
|
-
fullDocumentText,
|
|
1496
|
-
agentId,
|
|
1497
|
-
contentType,
|
|
1498
|
-
roomId,
|
|
1499
|
-
entityId,
|
|
1500
|
-
worldId,
|
|
1501
|
-
documentTitle
|
|
1502
|
-
}) {
|
|
1503
|
-
if (!fullDocumentText || fullDocumentText.trim() === "") {
|
|
1504
|
-
import_core3.logger.warn(`No text content available for document ${documentId}`);
|
|
1505
|
-
return 0;
|
|
1506
|
-
}
|
|
1507
|
-
const chunks = await splitDocumentIntoChunks(fullDocumentText);
|
|
1508
|
-
if (chunks.length === 0) {
|
|
1509
|
-
import_core3.logger.warn(`No chunks generated for document ${documentId}`);
|
|
1510
|
-
return 0;
|
|
1511
|
-
}
|
|
1512
|
-
import_core3.logger.info(`Split into ${chunks.length} chunks`);
|
|
1513
|
-
const providerLimits = await getProviderRateLimits(runtime);
|
|
1514
|
-
const CONCURRENCY_LIMIT = providerLimits.maxConcurrentRequests || 30;
|
|
1515
|
-
const rateLimiter = createRateLimiter(providerLimits.requestsPerMinute || 60, providerLimits.tokensPerMinute, providerLimits.rateLimitEnabled);
|
|
1516
|
-
const { savedCount, failedCount } = await processAndSaveFragments({
|
|
1517
|
-
runtime,
|
|
1518
|
-
documentId,
|
|
1519
|
-
chunks,
|
|
1520
|
-
fullDocumentText,
|
|
1521
|
-
contentType,
|
|
1522
|
-
agentId,
|
|
1523
|
-
roomId: roomId || agentId,
|
|
1524
|
-
entityId: entityId || agentId,
|
|
1525
|
-
worldId: worldId || agentId,
|
|
1526
|
-
concurrencyLimit: CONCURRENCY_LIMIT,
|
|
1527
|
-
rateLimiter,
|
|
1528
|
-
documentTitle,
|
|
1529
|
-
batchDelayMs: providerLimits.batchDelayMs
|
|
1530
|
-
});
|
|
1531
|
-
if (failedCount > 0) {
|
|
1532
|
-
import_core3.logger.warn(`${failedCount}/${chunks.length} chunks failed processing`);
|
|
1533
|
-
}
|
|
1534
|
-
return savedCount;
|
|
1535
|
-
}
|
|
1536
|
-
async function extractTextFromDocument(fileBuffer, contentType, originalFilename) {
|
|
1537
|
-
if (!fileBuffer || fileBuffer.length === 0) {
|
|
1538
|
-
throw new Error(`Empty file buffer provided for ${originalFilename}`);
|
|
1539
|
-
}
|
|
1540
|
-
try {
|
|
1541
|
-
if (contentType === "application/pdf") {
|
|
1542
|
-
import_core3.logger.debug(`Extracting text from PDF: ${originalFilename}`);
|
|
1543
|
-
return await convertPdfToTextFromBuffer(fileBuffer, originalFilename);
|
|
1544
|
-
} else {
|
|
1545
|
-
if (contentType.includes("text/") || contentType.includes("application/json") || contentType.includes("application/xml")) {
|
|
1546
|
-
try {
|
|
1547
|
-
return fileBuffer.toString("utf8");
|
|
1548
|
-
} catch (_textError) {
|
|
1549
|
-
import_core3.logger.warn(`Failed to decode ${originalFilename} as UTF-8`);
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
return await extractTextFromFileBuffer(fileBuffer, contentType, originalFilename);
|
|
1553
|
-
}
|
|
1554
|
-
} catch (error) {
|
|
1555
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1556
|
-
import_core3.logger.error(`Error extracting text from ${originalFilename}: ${errorMessage}`);
|
|
1557
|
-
throw new Error(`Failed to extract text from ${originalFilename}: ${errorMessage}`);
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
function createDocumentMemory({
|
|
1561
|
-
text,
|
|
1562
|
-
agentId,
|
|
1563
|
-
clientDocumentId,
|
|
1564
|
-
originalFilename,
|
|
1565
|
-
contentType,
|
|
1566
|
-
worldId,
|
|
1567
|
-
fileSize,
|
|
1568
|
-
documentId,
|
|
1569
|
-
customMetadata
|
|
1570
|
-
}) {
|
|
1571
|
-
const fileExt = originalFilename.split(".").pop()?.toLowerCase() || "";
|
|
1572
|
-
const title = originalFilename.replace(`.${fileExt}`, "");
|
|
1573
|
-
const docId = documentId || v4_default();
|
|
1574
|
-
return {
|
|
1575
|
-
id: docId,
|
|
1576
|
-
agentId,
|
|
1577
|
-
roomId: agentId,
|
|
1578
|
-
worldId,
|
|
1579
|
-
entityId: agentId,
|
|
1580
|
-
content: { text },
|
|
1581
|
-
metadata: {
|
|
1582
|
-
type: import_core3.MemoryType.CUSTOM,
|
|
1583
|
-
documentId: clientDocumentId,
|
|
1584
|
-
originalFilename,
|
|
1585
|
-
contentType,
|
|
1586
|
-
title,
|
|
1587
|
-
fileExt,
|
|
1588
|
-
fileSize,
|
|
1589
|
-
source: "rag-service-main-upload",
|
|
1590
|
-
timestamp: Date.now(),
|
|
1591
|
-
...customMetadata || {}
|
|
1592
|
-
}
|
|
1593
|
-
};
|
|
1594
|
-
}
|
|
1595
|
-
async function splitDocumentIntoChunks(documentText) {
|
|
1596
|
-
const tokenChunkSize = DEFAULT_CHUNK_TOKEN_SIZE;
|
|
1597
|
-
const tokenChunkOverlap = DEFAULT_CHUNK_OVERLAP_TOKENS;
|
|
1598
|
-
return await import_core3.splitChunks(documentText, tokenChunkSize, tokenChunkOverlap);
|
|
1599
|
-
}
|
|
1600
|
-
async function processAndSaveFragments({
|
|
1601
|
-
runtime,
|
|
1602
|
-
documentId,
|
|
1603
|
-
chunks,
|
|
1604
|
-
fullDocumentText,
|
|
1605
|
-
contentType,
|
|
1606
|
-
agentId,
|
|
1607
|
-
roomId,
|
|
1608
|
-
entityId,
|
|
1609
|
-
worldId,
|
|
1610
|
-
concurrencyLimit,
|
|
1611
|
-
rateLimiter,
|
|
1612
|
-
documentTitle,
|
|
1613
|
-
batchDelayMs = 500
|
|
1614
|
-
}) {
|
|
1615
|
-
let savedCount = 0;
|
|
1616
|
-
let failedCount = 0;
|
|
1617
|
-
const failedChunks = [];
|
|
1618
|
-
for (let i = 0;i < chunks.length; i += concurrencyLimit) {
|
|
1619
|
-
const batchChunks = chunks.slice(i, i + concurrencyLimit);
|
|
1620
|
-
const batchOriginalIndices = Array.from({ length: batchChunks.length }, (_, k) => i + k);
|
|
1621
|
-
const contextualizedChunks = await getContextualizedChunks(runtime, fullDocumentText, batchChunks, contentType, batchOriginalIndices, documentTitle);
|
|
1622
|
-
const embeddingResults = await generateEmbeddingsForChunks(runtime, contextualizedChunks, rateLimiter);
|
|
1623
|
-
for (const result of embeddingResults) {
|
|
1624
|
-
const originalChunkIndex = result.index;
|
|
1625
|
-
if (!result.success) {
|
|
1626
|
-
failedCount++;
|
|
1627
|
-
failedChunks.push(originalChunkIndex);
|
|
1628
|
-
import_core3.logger.warn(`Failed to process chunk ${originalChunkIndex} for document ${documentId}`);
|
|
1629
|
-
continue;
|
|
1630
|
-
}
|
|
1631
|
-
const contextualizedChunkText = result.text;
|
|
1632
|
-
const embedding = result.embedding;
|
|
1633
|
-
if (!embedding || embedding.length === 0) {
|
|
1634
|
-
failedCount++;
|
|
1635
|
-
failedChunks.push(originalChunkIndex);
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
try {
|
|
1639
|
-
const fragmentMemory = {
|
|
1640
|
-
id: v4_default(),
|
|
1641
|
-
agentId,
|
|
1642
|
-
roomId: roomId || agentId,
|
|
1643
|
-
worldId: worldId || agentId,
|
|
1644
|
-
entityId: entityId || agentId,
|
|
1645
|
-
embedding,
|
|
1646
|
-
content: { text: contextualizedChunkText },
|
|
1647
|
-
metadata: {
|
|
1648
|
-
type: import_core3.MemoryType.FRAGMENT,
|
|
1649
|
-
documentId,
|
|
1650
|
-
position: originalChunkIndex,
|
|
1651
|
-
timestamp: Date.now(),
|
|
1652
|
-
source: "rag-service-fragment-sync"
|
|
1653
|
-
}
|
|
1654
|
-
};
|
|
1655
|
-
await runtime.createMemory(fragmentMemory, "knowledge");
|
|
1656
|
-
savedCount++;
|
|
1657
|
-
} catch (saveError) {
|
|
1658
|
-
const errorMessage = saveError instanceof Error ? saveError.message : String(saveError);
|
|
1659
|
-
import_core3.logger.error(`Error saving chunk ${originalChunkIndex} to database: ${errorMessage}`);
|
|
1660
|
-
failedCount++;
|
|
1661
|
-
failedChunks.push(originalChunkIndex);
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
if (i + concurrencyLimit < chunks.length && batchDelayMs > 0) {
|
|
1665
|
-
await new Promise((resolve2) => setTimeout(resolve2, batchDelayMs));
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
return { savedCount, failedCount, failedChunks };
|
|
1669
|
-
}
|
|
1670
|
-
var EMBEDDING_BATCH_SIZE = 100;
|
|
1671
|
-
async function generateEmbeddingsForChunks(runtime, contextualizedChunks, rateLimiter) {
|
|
1672
|
-
const validChunks = contextualizedChunks.filter((chunk) => chunk.success);
|
|
1673
|
-
const failedChunks = contextualizedChunks.filter((chunk) => !chunk.success);
|
|
1674
|
-
const results = [];
|
|
1675
|
-
for (const chunk of failedChunks) {
|
|
1676
|
-
results.push({
|
|
1677
|
-
success: false,
|
|
1678
|
-
index: chunk.index,
|
|
1679
|
-
error: new Error("Chunk processing failed"),
|
|
1680
|
-
text: chunk.contextualizedText
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
if (validChunks.length === 0) {
|
|
1684
|
-
return results;
|
|
1685
|
-
}
|
|
1686
|
-
const useBatchEmbeddings = shouldUseBatchEmbeddings(runtime);
|
|
1687
|
-
if (useBatchEmbeddings) {
|
|
1688
|
-
return await generateEmbeddingsBatch(runtime, validChunks, rateLimiter, results);
|
|
1689
|
-
} else {
|
|
1690
|
-
return await generateEmbeddingsIndividual(runtime, validChunks, rateLimiter, results);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
function shouldUseBatchEmbeddings(runtime) {
|
|
1694
|
-
const setting = runtime.getSetting("BATCH_EMBEDDINGS") ?? process.env.BATCH_EMBEDDINGS;
|
|
1695
|
-
return setting === "true" || setting === true;
|
|
1696
|
-
}
|
|
1697
|
-
async function generateEmbeddingsBatch(runtime, validChunks, rateLimiter, results) {
|
|
1698
|
-
for (let batchStart = 0;batchStart < validChunks.length; batchStart += EMBEDDING_BATCH_SIZE) {
|
|
1699
|
-
const batchEnd = Math.min(batchStart + EMBEDDING_BATCH_SIZE, validChunks.length);
|
|
1700
|
-
const batch = validChunks.slice(batchStart, batchEnd);
|
|
1701
|
-
const batchTexts = batch.map((c) => c.contextualizedText);
|
|
1702
|
-
const totalTokens = batchTexts.reduce((sum, text) => sum + estimateTokens(text), 0);
|
|
1703
|
-
await rateLimiter(totalTokens);
|
|
1704
|
-
try {
|
|
1705
|
-
const embeddings = await generateBatchEmbeddingsViaRuntime(runtime, batchTexts);
|
|
1706
|
-
for (let i = 0;i < batch.length; i++) {
|
|
1707
|
-
const chunk = batch[i];
|
|
1708
|
-
const embedding = embeddings[i];
|
|
1709
|
-
if (embedding && embedding.length > 0 && embedding[0] !== 0) {
|
|
1710
|
-
results.push({
|
|
1711
|
-
embedding,
|
|
1712
|
-
success: true,
|
|
1713
|
-
index: chunk.index,
|
|
1714
|
-
text: chunk.contextualizedText
|
|
1715
|
-
});
|
|
1716
|
-
} else {
|
|
1717
|
-
results.push({
|
|
1718
|
-
success: false,
|
|
1719
|
-
index: chunk.index,
|
|
1720
|
-
error: new Error("Empty or invalid embedding returned"),
|
|
1721
|
-
text: chunk.contextualizedText
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
} catch (error) {
|
|
1726
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1727
|
-
import_core3.logger.error(`Batch embedding error: ${errorMessage}`);
|
|
1728
|
-
for (const chunk of batch) {
|
|
1729
|
-
try {
|
|
1730
|
-
const result = await generateEmbeddingWithValidation(runtime, chunk.contextualizedText);
|
|
1731
|
-
if (result.success && result.embedding) {
|
|
1732
|
-
results.push({
|
|
1733
|
-
embedding: result.embedding,
|
|
1734
|
-
success: true,
|
|
1735
|
-
index: chunk.index,
|
|
1736
|
-
text: chunk.contextualizedText
|
|
1737
|
-
});
|
|
1738
|
-
} else {
|
|
1739
|
-
results.push({
|
|
1740
|
-
success: false,
|
|
1741
|
-
index: chunk.index,
|
|
1742
|
-
error: result.error instanceof Error ? result.error : new Error("Embedding failed"),
|
|
1743
|
-
text: chunk.contextualizedText
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
} catch (fallbackError) {
|
|
1747
|
-
results.push({
|
|
1748
|
-
success: false,
|
|
1749
|
-
index: chunk.index,
|
|
1750
|
-
error: fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)),
|
|
1751
|
-
text: chunk.contextualizedText
|
|
1752
|
-
});
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
return results;
|
|
1758
|
-
}
|
|
1759
|
-
async function generateBatchEmbeddingsViaRuntime(runtime, texts) {
|
|
1760
|
-
const batchResult = await runtime.useModel(import_core3.ModelType.TEXT_EMBEDDING, { texts });
|
|
1761
|
-
const isEmbeddingBatch = (val) => Array.isArray(val) && val.length > 0 && Array.isArray(val[0]) && typeof val[0][0] === "number";
|
|
1762
|
-
const isEmbeddingVector = (val) => Array.isArray(val) && val.length > 0 && typeof val[0] === "number";
|
|
1763
|
-
if (isEmbeddingBatch(batchResult)) {
|
|
1764
|
-
return batchResult;
|
|
1765
|
-
}
|
|
1766
|
-
if (isEmbeddingVector(batchResult)) {
|
|
1767
|
-
const embeddings = await Promise.all(texts.map(async (text) => {
|
|
1768
|
-
const result = await runtime.useModel(import_core3.ModelType.TEXT_EMBEDDING, { text });
|
|
1769
|
-
if (isEmbeddingVector(result)) {
|
|
1770
|
-
return result;
|
|
1771
|
-
}
|
|
1772
|
-
const embeddingResult = result;
|
|
1773
|
-
return embeddingResult?.embedding ?? [];
|
|
1774
|
-
}));
|
|
1775
|
-
return embeddings;
|
|
1776
|
-
}
|
|
1777
|
-
throw new Error("Unexpected batch embedding result format");
|
|
1778
|
-
}
|
|
1779
|
-
async function generateEmbeddingsIndividual(runtime, validChunks, rateLimiter, results) {
|
|
1780
|
-
for (const chunk of validChunks) {
|
|
1781
|
-
const embeddingTokens = estimateTokens(chunk.contextualizedText);
|
|
1782
|
-
await rateLimiter(embeddingTokens);
|
|
1783
|
-
try {
|
|
1784
|
-
const generateEmbeddingOperation = async () => {
|
|
1785
|
-
return await generateEmbeddingWithValidation(runtime, chunk.contextualizedText);
|
|
1786
|
-
};
|
|
1787
|
-
const { embedding, success, error } = await withRateLimitRetry(generateEmbeddingOperation, `embedding generation for chunk ${chunk.index}`);
|
|
1788
|
-
if (!success) {
|
|
1789
|
-
results.push({
|
|
1790
|
-
success: false,
|
|
1791
|
-
index: chunk.index,
|
|
1792
|
-
error,
|
|
1793
|
-
text: chunk.contextualizedText
|
|
1794
|
-
});
|
|
1795
|
-
} else {
|
|
1796
|
-
results.push({
|
|
1797
|
-
embedding: embedding ?? undefined,
|
|
1798
|
-
success: true,
|
|
1799
|
-
index: chunk.index,
|
|
1800
|
-
text: chunk.contextualizedText
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
} catch (error) {
|
|
1804
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1805
|
-
import_core3.logger.error(`Error generating embedding for chunk ${chunk.index}: ${errorMessage}`);
|
|
1806
|
-
results.push({
|
|
1807
|
-
success: false,
|
|
1808
|
-
index: chunk.index,
|
|
1809
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
1810
|
-
text: chunk.contextualizedText
|
|
1811
|
-
});
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
return results;
|
|
1815
|
-
}
|
|
1816
|
-
async function getContextualizedChunks(runtime, fullDocumentText, chunks, contentType, batchOriginalIndices, documentTitle) {
|
|
1817
|
-
const ctxEnabled = getCtxKnowledgeEnabled(runtime);
|
|
1818
|
-
if (ctxEnabled && fullDocumentText) {
|
|
1819
|
-
return await generateContextsInBatch(runtime, fullDocumentText, chunks, contentType, batchOriginalIndices, documentTitle);
|
|
1820
|
-
}
|
|
1821
|
-
return chunks.map((chunkText, idx) => ({
|
|
1822
|
-
contextualizedText: chunkText,
|
|
1823
|
-
index: batchOriginalIndices[idx],
|
|
1824
|
-
success: true
|
|
1825
|
-
}));
|
|
1826
|
-
}
|
|
1827
|
-
async function generateContextsInBatch(runtime, fullDocumentText, chunks, contentType, batchIndices, _documentTitle) {
|
|
1828
|
-
if (!chunks || chunks.length === 0) {
|
|
1829
|
-
return [];
|
|
1830
|
-
}
|
|
1831
|
-
const providerLimits = await getProviderRateLimits(runtime);
|
|
1832
|
-
const rateLimiter = createRateLimiter(providerLimits.requestsPerMinute || 60, providerLimits.tokensPerMinute, providerLimits.rateLimitEnabled);
|
|
1833
|
-
const config = validateModelConfig(runtime);
|
|
1834
|
-
const isUsingOpenRouter = config.TEXT_PROVIDER === "openrouter";
|
|
1835
|
-
const isUsingCacheCapableModel = isUsingOpenRouter && (config.TEXT_MODEL?.toLowerCase().includes("claude") || config.TEXT_MODEL?.toLowerCase().includes("gemini"));
|
|
1836
|
-
import_core3.logger.debug(`Contextualizing ${chunks.length} chunks with ${config.TEXT_PROVIDER}/${config.TEXT_MODEL} (cache: ${isUsingCacheCapableModel})`);
|
|
1837
|
-
const promptConfigs = prepareContextPrompts(chunks, fullDocumentText, contentType, batchIndices, isUsingCacheCapableModel);
|
|
1838
|
-
const contextualizedChunks = await Promise.all(promptConfigs.map(async (item) => {
|
|
1839
|
-
if (!item.valid) {
|
|
1840
|
-
return {
|
|
1841
|
-
contextualizedText: item.chunkText,
|
|
1842
|
-
success: false,
|
|
1843
|
-
index: item.originalIndex
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
const llmTokens = estimateTokens(item.chunkText + (item.prompt || ""));
|
|
1847
|
-
await rateLimiter(llmTokens);
|
|
1848
|
-
try {
|
|
1849
|
-
const generateTextOperation = async () => {
|
|
1850
|
-
if (useCustomLLM) {
|
|
1851
|
-
if (item.usesCaching && item.promptText) {
|
|
1852
|
-
return await generateText(runtime, item.promptText, item.systemPrompt, {
|
|
1853
|
-
cacheDocument: item.fullDocumentTextForContext,
|
|
1854
|
-
cacheOptions: { type: "ephemeral" },
|
|
1855
|
-
autoCacheContextualRetrieval: true
|
|
1856
|
-
});
|
|
1857
|
-
} else if (item.prompt) {
|
|
1858
|
-
return await generateText(runtime, item.prompt);
|
|
1859
|
-
}
|
|
1860
|
-
throw new Error("Missing prompt for text generation");
|
|
1861
|
-
} else {
|
|
1862
|
-
if (item.usesCaching && item.promptText) {
|
|
1863
|
-
const combinedPrompt = item.systemPrompt ? `${item.systemPrompt}
|
|
1864
|
-
|
|
1865
|
-
${item.promptText}` : item.promptText;
|
|
1866
|
-
return await runtime.useModel(import_core3.ModelType.TEXT_LARGE, {
|
|
1867
|
-
prompt: combinedPrompt
|
|
1868
|
-
});
|
|
1869
|
-
} else if (item.prompt) {
|
|
1870
|
-
return await runtime.useModel(import_core3.ModelType.TEXT_LARGE, {
|
|
1871
|
-
prompt: item.prompt
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
|
-
throw new Error("Missing prompt for text generation");
|
|
1875
|
-
}
|
|
1876
|
-
};
|
|
1877
|
-
const llmResponse = await withRateLimitRetry(generateTextOperation, `context generation for chunk ${item.originalIndex}`);
|
|
1878
|
-
const generatedContext = typeof llmResponse === "string" ? llmResponse : llmResponse.text;
|
|
1879
|
-
const contextualizedText = getChunkWithContext(item.chunkText, generatedContext);
|
|
1880
|
-
return {
|
|
1881
|
-
contextualizedText,
|
|
1882
|
-
success: true,
|
|
1883
|
-
index: item.originalIndex
|
|
1884
|
-
};
|
|
1885
|
-
} catch (error) {
|
|
1886
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1887
|
-
import_core3.logger.error(`Error generating context for chunk ${item.originalIndex}: ${errorMessage}`);
|
|
1888
|
-
return {
|
|
1889
|
-
contextualizedText: item.chunkText,
|
|
1890
|
-
success: false,
|
|
1891
|
-
index: item.originalIndex
|
|
1892
|
-
};
|
|
1893
|
-
}
|
|
1894
|
-
}));
|
|
1895
|
-
return contextualizedChunks;
|
|
1896
|
-
}
|
|
1897
|
-
function prepareContextPrompts(chunks, fullDocumentText, contentType, batchIndices, isUsingCacheCapableModel = false) {
|
|
1898
|
-
return chunks.map((chunkText, idx) => {
|
|
1899
|
-
const originalIndex = batchIndices ? batchIndices[idx] : idx;
|
|
1900
|
-
try {
|
|
1901
|
-
if (isUsingCacheCapableModel) {
|
|
1902
|
-
const cachingPromptInfo = contentType ? getCachingPromptForMimeType(contentType, chunkText) : getCachingContextualizationPrompt(chunkText);
|
|
1903
|
-
if (cachingPromptInfo.prompt.startsWith("Error:")) {
|
|
1904
|
-
return {
|
|
1905
|
-
originalIndex,
|
|
1906
|
-
chunkText,
|
|
1907
|
-
valid: false,
|
|
1908
|
-
usesCaching: false
|
|
1909
|
-
};
|
|
1910
|
-
}
|
|
1911
|
-
return {
|
|
1912
|
-
valid: true,
|
|
1913
|
-
originalIndex,
|
|
1914
|
-
chunkText,
|
|
1915
|
-
usesCaching: true,
|
|
1916
|
-
systemPrompt: cachingPromptInfo.systemPrompt,
|
|
1917
|
-
promptText: cachingPromptInfo.prompt,
|
|
1918
|
-
fullDocumentTextForContext: fullDocumentText
|
|
1919
|
-
};
|
|
1920
|
-
} else {
|
|
1921
|
-
const prompt = contentType ? getPromptForMimeType(contentType, fullDocumentText, chunkText) : getContextualizationPrompt(fullDocumentText, chunkText);
|
|
1922
|
-
if (prompt.startsWith("Error:")) {
|
|
1923
|
-
return {
|
|
1924
|
-
prompt: null,
|
|
1925
|
-
originalIndex,
|
|
1926
|
-
chunkText,
|
|
1927
|
-
valid: false,
|
|
1928
|
-
usesCaching: false
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
return {
|
|
1932
|
-
prompt,
|
|
1933
|
-
originalIndex,
|
|
1934
|
-
chunkText,
|
|
1935
|
-
valid: true,
|
|
1936
|
-
usesCaching: false
|
|
1937
|
-
};
|
|
1938
|
-
}
|
|
1939
|
-
} catch (error) {
|
|
1940
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1941
|
-
import_core3.logger.error(`Error preparing prompt for chunk ${originalIndex}: ${errorMessage}`);
|
|
1942
|
-
return {
|
|
1943
|
-
prompt: null,
|
|
1944
|
-
originalIndex,
|
|
1945
|
-
chunkText,
|
|
1946
|
-
valid: false,
|
|
1947
|
-
usesCaching: false
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
});
|
|
1951
|
-
}
|
|
1952
|
-
async function generateEmbeddingWithValidation(runtime, text) {
|
|
1953
|
-
try {
|
|
1954
|
-
const embeddingResult = await runtime.useModel(import_core3.ModelType.TEXT_EMBEDDING, { text });
|
|
1955
|
-
const embedding = Array.isArray(embeddingResult) ? embeddingResult : embeddingResult?.embedding;
|
|
1956
|
-
if (!embedding || embedding.length === 0) {
|
|
1957
|
-
return { embedding: null, success: false, error: new Error("Zero vector detected") };
|
|
1958
|
-
}
|
|
1959
|
-
return { embedding, success: true };
|
|
1960
|
-
} catch (error) {
|
|
1961
|
-
return {
|
|
1962
|
-
embedding: null,
|
|
1963
|
-
success: false,
|
|
1964
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
async function withRateLimitRetry(operation, errorContext, retryDelay) {
|
|
1969
|
-
try {
|
|
1970
|
-
return await operation();
|
|
1971
|
-
} catch (error) {
|
|
1972
|
-
const errorWithStatus = error;
|
|
1973
|
-
if (errorWithStatus.status === 429) {
|
|
1974
|
-
const delay = retryDelay || errorWithStatus.headers?.["retry-after"] || 5;
|
|
1975
|
-
await new Promise((resolve2) => setTimeout(resolve2, delay * 1000));
|
|
1976
|
-
try {
|
|
1977
|
-
return await operation();
|
|
1978
|
-
} catch (retryError) {
|
|
1979
|
-
const retryErrorMessage = retryError instanceof Error ? retryError.message : String(retryError);
|
|
1980
|
-
import_core3.logger.error(`Failed after retry for ${errorContext}: ${retryErrorMessage}`);
|
|
1981
|
-
throw retryError;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
throw error;
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
function createRateLimiter(requestsPerMinute, tokensPerMinute, rateLimitEnabled = true) {
|
|
1988
|
-
const requestTimes = [];
|
|
1989
|
-
const tokenUsage = [];
|
|
1990
|
-
const intervalMs = 60 * 1000;
|
|
1991
|
-
return async function rateLimiter(estimatedTokens = 1000) {
|
|
1992
|
-
if (!rateLimitEnabled)
|
|
1993
|
-
return;
|
|
1994
|
-
const now = Date.now();
|
|
1995
|
-
while (requestTimes.length > 0 && now - requestTimes[0] > intervalMs) {
|
|
1996
|
-
requestTimes.shift();
|
|
1997
|
-
}
|
|
1998
|
-
while (tokenUsage.length > 0 && now - tokenUsage[0].timestamp > intervalMs) {
|
|
1999
|
-
tokenUsage.shift();
|
|
2000
|
-
}
|
|
2001
|
-
const currentTokens = tokenUsage.reduce((sum, usage) => sum + usage.tokens, 0);
|
|
2002
|
-
const requestLimitExceeded = requestTimes.length >= requestsPerMinute;
|
|
2003
|
-
const tokenLimitExceeded = tokensPerMinute && currentTokens + estimatedTokens > tokensPerMinute;
|
|
2004
|
-
if (requestLimitExceeded || tokenLimitExceeded) {
|
|
2005
|
-
let timeToWait = 0;
|
|
2006
|
-
if (requestLimitExceeded) {
|
|
2007
|
-
timeToWait = Math.max(timeToWait, requestTimes[0] + intervalMs - now);
|
|
2008
|
-
}
|
|
2009
|
-
if (tokenLimitExceeded && tokenUsage.length > 0) {
|
|
2010
|
-
timeToWait = Math.max(timeToWait, tokenUsage[0].timestamp + intervalMs - now);
|
|
2011
|
-
}
|
|
2012
|
-
if (timeToWait > 0) {
|
|
2013
|
-
await new Promise((resolve2) => setTimeout(resolve2, timeToWait));
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
requestTimes.push(now);
|
|
2017
|
-
if (tokensPerMinute) {
|
|
2018
|
-
tokenUsage.push({ timestamp: now, tokens: estimatedTokens });
|
|
2019
|
-
}
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
// service.ts
|
|
2024
|
-
class KnowledgeService extends import_core4.Service {
|
|
2025
|
-
static serviceType = "knowledge";
|
|
2026
|
-
config = {};
|
|
2027
|
-
capabilityDescription = "Provides Retrieval Augmented Generation capabilities, including knowledge upload and querying.";
|
|
2028
|
-
knowledgeProcessingSemaphore;
|
|
2029
|
-
constructor(runtime, _config) {
|
|
2030
|
-
super(runtime);
|
|
2031
|
-
this.knowledgeProcessingSemaphore = new import_core4.Semaphore(10);
|
|
2032
|
-
}
|
|
2033
|
-
async loadInitialDocuments() {
|
|
2034
|
-
import_core4.logger.info(`Loading documents on startup for agent ${this.runtime.agentId}`);
|
|
2035
|
-
try {
|
|
2036
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1000));
|
|
2037
|
-
const knowledgePathSetting = this.runtime.getSetting("KNOWLEDGE_PATH");
|
|
2038
|
-
const knowledgePath = typeof knowledgePathSetting === "string" ? knowledgePathSetting : undefined;
|
|
2039
|
-
const result = await loadDocsFromPath(this, this.runtime.agentId, undefined, knowledgePath);
|
|
2040
|
-
if (result.successful > 0) {
|
|
2041
|
-
import_core4.logger.info(`Loaded ${result.successful} documents on startup`);
|
|
2042
|
-
}
|
|
2043
|
-
} catch (error) {
|
|
2044
|
-
import_core4.logger.error({ error }, "Error loading documents on startup");
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
static async start(runtime) {
|
|
2048
|
-
import_core4.logger.info(`Starting Knowledge service for agent: ${runtime.agentId}`);
|
|
2049
|
-
const validatedConfig = validateModelConfig(runtime);
|
|
2050
|
-
const ctxEnabled = validatedConfig.CTX_KNOWLEDGE_ENABLED;
|
|
2051
|
-
if (ctxEnabled) {
|
|
2052
|
-
import_core4.logger.info(`Contextual Knowledge enabled: ${validatedConfig.EMBEDDING_PROVIDER || "auto"} embeddings, ${validatedConfig.TEXT_PROVIDER} text generation`);
|
|
2053
|
-
import_core4.logger.info(`Text model: ${validatedConfig.TEXT_MODEL}`);
|
|
2054
|
-
} else {
|
|
2055
|
-
const usingPluginOpenAI = !process.env.EMBEDDING_PROVIDER;
|
|
2056
|
-
import_core4.logger.warn("Basic Embedding mode - documents will not be enriched with context");
|
|
2057
|
-
import_core4.logger.info("To enable contextual enrichment: Set CTX_KNOWLEDGE_ENABLED=true and configure TEXT_PROVIDER/TEXT_MODEL");
|
|
2058
|
-
if (usingPluginOpenAI) {
|
|
2059
|
-
import_core4.logger.info("Using plugin-openai configuration for embeddings");
|
|
2060
|
-
} else {
|
|
2061
|
-
import_core4.logger.info(`Using ${validatedConfig.EMBEDDING_PROVIDER} for embeddings with ${validatedConfig.TEXT_EMBEDDING_MODEL}`);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
const service = new KnowledgeService(runtime);
|
|
2065
|
-
service.config = validatedConfig;
|
|
2066
|
-
if (service.config.LOAD_DOCS_ON_STARTUP) {
|
|
2067
|
-
service.loadInitialDocuments().catch((error) => {
|
|
2068
|
-
import_core4.logger.error({ error }, "Error loading initial documents");
|
|
2069
|
-
});
|
|
2070
|
-
}
|
|
2071
|
-
if (service.runtime.character?.knowledge && service.runtime.character.knowledge.length > 0) {
|
|
2072
|
-
const stringKnowledge = service.runtime.character.knowledge.map((item) => {
|
|
2073
|
-
const itemAny = item;
|
|
2074
|
-
if (itemAny?.item?.case === "path" && typeof itemAny.item.value === "string") {
|
|
2075
|
-
return itemAny.item.value;
|
|
2076
|
-
}
|
|
2077
|
-
if (typeof itemAny?.path === "string") {
|
|
2078
|
-
return itemAny.path;
|
|
2079
|
-
}
|
|
2080
|
-
if (typeof item === "string") {
|
|
2081
|
-
return item;
|
|
2082
|
-
}
|
|
2083
|
-
return null;
|
|
2084
|
-
}).filter((item) => item !== null);
|
|
2085
|
-
await service.processCharacterKnowledge(stringKnowledge).catch((err) => {
|
|
2086
|
-
import_core4.logger.error({ error: err }, "Error processing character knowledge");
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2089
|
-
return service;
|
|
2090
|
-
}
|
|
2091
|
-
static async stop(runtime) {
|
|
2092
|
-
import_core4.logger.info(`Stopping Knowledge service for agent: ${runtime.agentId}`);
|
|
2093
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
2094
|
-
if (!service) {
|
|
2095
|
-
import_core4.logger.warn(`KnowledgeService not found for agent ${runtime.agentId} during stop.`);
|
|
2096
|
-
}
|
|
2097
|
-
if (service instanceof KnowledgeService) {
|
|
2098
|
-
await service.stop();
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
async stop() {
|
|
2102
|
-
import_core4.logger.info(`Knowledge service stopping for agent: ${this.runtime.character?.name}`);
|
|
2103
|
-
}
|
|
2104
|
-
async addKnowledge(options) {
|
|
2105
|
-
const agentId = options.agentId || this.runtime.agentId;
|
|
2106
|
-
const contentBasedId = generateContentBasedId(options.content, agentId, {
|
|
2107
|
-
includeFilename: options.originalFilename,
|
|
2108
|
-
contentType: options.contentType,
|
|
2109
|
-
maxChars: 2000
|
|
2110
|
-
});
|
|
2111
|
-
import_core4.logger.info(`Processing "${options.originalFilename}" (${options.contentType})`);
|
|
2112
|
-
try {
|
|
2113
|
-
const existingDocument = await this.runtime.getMemoryById(contentBasedId);
|
|
2114
|
-
if (existingDocument && existingDocument.metadata?.type === import_core4.MemoryType.DOCUMENT) {
|
|
2115
|
-
import_core4.logger.info(`"${options.originalFilename}" already exists - skipping`);
|
|
2116
|
-
const fragments = await this.runtime.getMemories({
|
|
2117
|
-
tableName: "knowledge"
|
|
2118
|
-
});
|
|
2119
|
-
const relatedFragments = fragments.filter((f) => f.metadata?.type === import_core4.MemoryType.FRAGMENT && f.metadata.documentId === contentBasedId);
|
|
2120
|
-
return {
|
|
2121
|
-
clientDocumentId: contentBasedId,
|
|
2122
|
-
storedDocumentMemoryId: existingDocument.id,
|
|
2123
|
-
fragmentCount: relatedFragments.length
|
|
2124
|
-
};
|
|
2125
|
-
}
|
|
2126
|
-
} catch (error) {
|
|
2127
|
-
import_core4.logger.debug(`Document ${contentBasedId} not found or error checking existence, proceeding with processing: ${error instanceof Error ? error.message : String(error)}`);
|
|
2128
|
-
}
|
|
2129
|
-
return this.processDocument({
|
|
2130
|
-
...options,
|
|
2131
|
-
clientDocumentId: contentBasedId
|
|
2132
|
-
});
|
|
2133
|
-
}
|
|
2134
|
-
async processDocument({
|
|
2135
|
-
agentId: passedAgentId,
|
|
2136
|
-
clientDocumentId,
|
|
2137
|
-
contentType,
|
|
2138
|
-
originalFilename,
|
|
2139
|
-
worldId,
|
|
2140
|
-
content,
|
|
2141
|
-
roomId,
|
|
2142
|
-
entityId,
|
|
2143
|
-
metadata
|
|
2144
|
-
}) {
|
|
2145
|
-
const agentId = passedAgentId || this.runtime.agentId;
|
|
2146
|
-
try {
|
|
2147
|
-
import_core4.logger.debug(`Processing document ${originalFilename} (type: ${contentType}) for agent: ${agentId}`);
|
|
2148
|
-
let fileBuffer = null;
|
|
2149
|
-
let extractedText;
|
|
2150
|
-
let documentContentToStore;
|
|
2151
|
-
const isPdfFile = contentType === "application/pdf" || originalFilename.toLowerCase().endsWith(".pdf");
|
|
2152
|
-
if (isPdfFile) {
|
|
2153
|
-
try {
|
|
2154
|
-
fileBuffer = Buffer.from(content, "base64");
|
|
2155
|
-
} catch (e) {
|
|
2156
|
-
import_core4.logger.error({ error: e }, `Failed to convert base64 to buffer for ${originalFilename}`);
|
|
2157
|
-
throw new Error(`Invalid base64 content for PDF file ${originalFilename}`);
|
|
2158
|
-
}
|
|
2159
|
-
extractedText = await extractTextFromDocument(fileBuffer, contentType, originalFilename);
|
|
2160
|
-
documentContentToStore = content;
|
|
2161
|
-
} else if (isBinaryContentType(contentType, originalFilename)) {
|
|
2162
|
-
try {
|
|
2163
|
-
fileBuffer = Buffer.from(content, "base64");
|
|
2164
|
-
} catch (e) {
|
|
2165
|
-
import_core4.logger.error({ error: e }, `Failed to convert base64 to buffer for ${originalFilename}`);
|
|
2166
|
-
throw new Error(`Invalid base64 content for binary file ${originalFilename}`);
|
|
2167
|
-
}
|
|
2168
|
-
extractedText = await extractTextFromDocument(fileBuffer, contentType, originalFilename);
|
|
2169
|
-
documentContentToStore = extractedText;
|
|
2170
|
-
} else {
|
|
2171
|
-
if (looksLikeBase64(content)) {
|
|
2172
|
-
try {
|
|
2173
|
-
const decodedBuffer = Buffer.from(content, "base64");
|
|
2174
|
-
const decodedText = decodedBuffer.toString("utf8");
|
|
2175
|
-
const invalidCharCount = (decodedText.match(/\ufffd/g) || []).length;
|
|
2176
|
-
const textLength = decodedText.length;
|
|
2177
|
-
if (invalidCharCount > 0 && invalidCharCount / textLength > 0.1) {
|
|
2178
|
-
throw new Error("Decoded content contains too many invalid characters");
|
|
2179
|
-
}
|
|
2180
|
-
import_core4.logger.debug(`Successfully decoded base64 content for text file: ${originalFilename}`);
|
|
2181
|
-
extractedText = decodedText;
|
|
2182
|
-
documentContentToStore = decodedText;
|
|
2183
|
-
} catch (e) {
|
|
2184
|
-
import_core4.logger.error({ error: e instanceof Error ? e : new Error(String(e)) }, `Failed to decode base64 for ${originalFilename}`);
|
|
2185
|
-
throw new Error(`File ${originalFilename} appears to be corrupted or incorrectly encoded`);
|
|
2186
|
-
}
|
|
2187
|
-
} else {
|
|
2188
|
-
import_core4.logger.debug(`Treating content as plain text for file: ${originalFilename}`);
|
|
2189
|
-
extractedText = content;
|
|
2190
|
-
documentContentToStore = content;
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
if (!extractedText || extractedText.trim() === "") {
|
|
2194
|
-
throw new Error(`No text content extracted from ${originalFilename} (type: ${contentType})`);
|
|
2195
|
-
}
|
|
2196
|
-
const documentMemory = createDocumentMemory({
|
|
2197
|
-
text: documentContentToStore,
|
|
2198
|
-
agentId,
|
|
2199
|
-
clientDocumentId,
|
|
2200
|
-
originalFilename,
|
|
2201
|
-
contentType,
|
|
2202
|
-
worldId,
|
|
2203
|
-
fileSize: fileBuffer ? fileBuffer.length : extractedText.length,
|
|
2204
|
-
documentId: clientDocumentId,
|
|
2205
|
-
customMetadata: metadata
|
|
2206
|
-
});
|
|
2207
|
-
const memoryWithScope = {
|
|
2208
|
-
...documentMemory,
|
|
2209
|
-
id: clientDocumentId,
|
|
2210
|
-
agentId,
|
|
2211
|
-
roomId: roomId || agentId,
|
|
2212
|
-
entityId: entityId || agentId
|
|
2213
|
-
};
|
|
2214
|
-
await this.runtime.createMemory(memoryWithScope, "documents");
|
|
2215
|
-
const fragmentCount = await processFragmentsSynchronously({
|
|
2216
|
-
runtime: this.runtime,
|
|
2217
|
-
documentId: clientDocumentId,
|
|
2218
|
-
fullDocumentText: extractedText,
|
|
2219
|
-
agentId,
|
|
2220
|
-
contentType,
|
|
2221
|
-
roomId: roomId || agentId,
|
|
2222
|
-
entityId: entityId || agentId,
|
|
2223
|
-
worldId: worldId || agentId,
|
|
2224
|
-
documentTitle: originalFilename
|
|
2225
|
-
});
|
|
2226
|
-
import_core4.logger.debug(`"${originalFilename}" stored with ${fragmentCount} fragments`);
|
|
2227
|
-
return {
|
|
2228
|
-
clientDocumentId,
|
|
2229
|
-
storedDocumentMemoryId: memoryWithScope.id,
|
|
2230
|
-
fragmentCount
|
|
2231
|
-
};
|
|
2232
|
-
} catch (error) {
|
|
2233
|
-
import_core4.logger.error({ error }, `Error processing document ${originalFilename}`);
|
|
2234
|
-
throw error;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
async checkExistingKnowledge(knowledgeId) {
|
|
2238
|
-
const existingDocument = await this.runtime.getMemoryById(knowledgeId);
|
|
2239
|
-
return !!existingDocument;
|
|
2240
|
-
}
|
|
2241
|
-
async getKnowledge(message, scope) {
|
|
2242
|
-
if (!message?.content?.text || message?.content?.text.trim().length === 0) {
|
|
2243
|
-
import_core4.logger.warn("Invalid or empty message content for knowledge query");
|
|
2244
|
-
return [];
|
|
2245
|
-
}
|
|
2246
|
-
const embedding = await this.runtime.useModel(import_core4.ModelType.TEXT_EMBEDDING, {
|
|
2247
|
-
text: message.content.text
|
|
2248
|
-
});
|
|
2249
|
-
const filterScope = {};
|
|
2250
|
-
if (scope?.roomId)
|
|
2251
|
-
filterScope.roomId = scope.roomId;
|
|
2252
|
-
if (scope?.worldId)
|
|
2253
|
-
filterScope.worldId = scope.worldId;
|
|
2254
|
-
if (scope?.entityId)
|
|
2255
|
-
filterScope.entityId = scope.entityId;
|
|
2256
|
-
const fragments = await this.runtime.searchMemories({
|
|
2257
|
-
tableName: "knowledge",
|
|
2258
|
-
embedding,
|
|
2259
|
-
query: message.content.text,
|
|
2260
|
-
...filterScope,
|
|
2261
|
-
count: 20,
|
|
2262
|
-
match_threshold: 0.1
|
|
2263
|
-
});
|
|
2264
|
-
return fragments.filter((fragment) => fragment.id !== undefined).map((fragment) => ({
|
|
2265
|
-
id: fragment.id,
|
|
2266
|
-
content: fragment.content,
|
|
2267
|
-
similarity: fragment.similarity,
|
|
2268
|
-
metadata: fragment.metadata,
|
|
2269
|
-
worldId: fragment.worldId
|
|
2270
|
-
}));
|
|
2271
|
-
}
|
|
2272
|
-
async enrichConversationMemoryWithRAG(memoryId, ragMetadata) {
|
|
2273
|
-
try {
|
|
2274
|
-
const existingMemory = await this.runtime.getMemoryById(memoryId);
|
|
2275
|
-
if (!existingMemory) {
|
|
2276
|
-
import_core4.logger.warn(`Cannot enrich memory ${memoryId} - memory not found`);
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
const ragUsageData = {
|
|
2280
|
-
retrievedFragments: ragMetadata.retrievedFragments,
|
|
2281
|
-
queryText: ragMetadata.queryText,
|
|
2282
|
-
totalFragments: ragMetadata.totalFragments,
|
|
2283
|
-
retrievalTimestamp: ragMetadata.retrievalTimestamp,
|
|
2284
|
-
usedInResponse: true
|
|
2285
|
-
};
|
|
2286
|
-
const updatedMetadata = {
|
|
2287
|
-
...existingMemory.metadata,
|
|
2288
|
-
knowledgeUsed: true,
|
|
2289
|
-
ragUsage: JSON.stringify(ragUsageData),
|
|
2290
|
-
timestamp: existingMemory.metadata?.timestamp ?? Date.now(),
|
|
2291
|
-
type: import_core4.MemoryType.CUSTOM
|
|
2292
|
-
};
|
|
2293
|
-
await this.runtime.updateMemory({
|
|
2294
|
-
id: memoryId,
|
|
2295
|
-
metadata: updatedMetadata
|
|
2296
|
-
});
|
|
2297
|
-
} catch (error) {
|
|
2298
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2299
|
-
import_core4.logger.warn(`Failed to enrich conversation memory ${memoryId} with RAG data: ${errorMessage}`);
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
pendingRAGEnrichment = [];
|
|
2303
|
-
setPendingRAGMetadata(ragMetadata) {
|
|
2304
|
-
const now = Date.now();
|
|
2305
|
-
this.pendingRAGEnrichment = this.pendingRAGEnrichment.filter((entry) => now - entry.timestamp < 30000);
|
|
2306
|
-
this.pendingRAGEnrichment.push({
|
|
2307
|
-
ragMetadata,
|
|
2308
|
-
timestamp: now
|
|
2309
|
-
});
|
|
2310
|
-
}
|
|
2311
|
-
async enrichRecentMemoriesWithPendingRAG() {
|
|
2312
|
-
if (this.pendingRAGEnrichment.length === 0) {
|
|
2313
|
-
return;
|
|
2314
|
-
}
|
|
2315
|
-
try {
|
|
2316
|
-
const recentMemories = await this.runtime.getMemories({
|
|
2317
|
-
tableName: "messages",
|
|
2318
|
-
count: 10
|
|
2319
|
-
});
|
|
2320
|
-
const now = Date.now();
|
|
2321
|
-
const recentConversationMemories = recentMemories.filter((memory) => memory.metadata?.type === "message" && now - (memory.createdAt || 0) < 1e4 && !(memory.metadata && ("ragUsage" in memory.metadata) && memory.metadata.ragUsage)).sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
|
|
2322
|
-
for (const pendingEntry of this.pendingRAGEnrichment) {
|
|
2323
|
-
const matchingMemory = recentConversationMemories.find((memory) => (memory.createdAt || 0) > pendingEntry.timestamp);
|
|
2324
|
-
if (matchingMemory?.id) {
|
|
2325
|
-
await this.enrichConversationMemoryWithRAG(matchingMemory.id, pendingEntry.ragMetadata);
|
|
2326
|
-
const index = this.pendingRAGEnrichment.indexOf(pendingEntry);
|
|
2327
|
-
if (index > -1) {
|
|
2328
|
-
this.pendingRAGEnrichment.splice(index, 1);
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
} catch (error) {
|
|
2333
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2334
|
-
import_core4.logger.warn(`Error enriching recent memories with RAG data: ${errorMessage}`);
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
async processCharacterKnowledge(items) {
|
|
2338
|
-
await new Promise((resolve2) => setTimeout(resolve2, 1000));
|
|
2339
|
-
import_core4.logger.info(`Processing ${items.length} character knowledge items`);
|
|
2340
|
-
const processingPromises = items.map(async (item) => {
|
|
2341
|
-
await this.knowledgeProcessingSemaphore.acquire();
|
|
2342
|
-
try {
|
|
2343
|
-
const knowledgeId = generateContentBasedId(item, this.runtime.agentId, {
|
|
2344
|
-
maxChars: 2000,
|
|
2345
|
-
includeFilename: "character-knowledge"
|
|
2346
|
-
});
|
|
2347
|
-
if (await this.checkExistingKnowledge(knowledgeId)) {
|
|
2348
|
-
return;
|
|
2349
|
-
}
|
|
2350
|
-
let metadata = {
|
|
2351
|
-
type: import_core4.MemoryType.CUSTOM,
|
|
2352
|
-
timestamp: Date.now(),
|
|
2353
|
-
source: "character"
|
|
2354
|
-
};
|
|
2355
|
-
const pathMatch = item.match(/^Path: (.+?)(?:\n|\r\n)/);
|
|
2356
|
-
if (pathMatch) {
|
|
2357
|
-
const filePath = pathMatch[1].trim();
|
|
2358
|
-
const extension = filePath.split(".").pop() || "";
|
|
2359
|
-
const filename = filePath.split("/").pop() || "";
|
|
2360
|
-
const title = filename.replace(`.${extension}`, "");
|
|
2361
|
-
metadata = {
|
|
2362
|
-
...metadata,
|
|
2363
|
-
path: filePath,
|
|
2364
|
-
filename,
|
|
2365
|
-
fileExt: extension,
|
|
2366
|
-
title,
|
|
2367
|
-
fileType: `text/${extension || "plain"}`,
|
|
2368
|
-
fileSize: item.length
|
|
2369
|
-
};
|
|
2370
|
-
}
|
|
2371
|
-
await this._internalAddKnowledge({
|
|
2372
|
-
id: knowledgeId,
|
|
2373
|
-
content: {
|
|
2374
|
-
text: item
|
|
2375
|
-
},
|
|
2376
|
-
metadata
|
|
2377
|
-
}, undefined, {
|
|
2378
|
-
roomId: this.runtime.agentId,
|
|
2379
|
-
entityId: this.runtime.agentId,
|
|
2380
|
-
worldId: this.runtime.agentId
|
|
2381
|
-
});
|
|
2382
|
-
} catch (error) {
|
|
2383
|
-
import_core4.logger.error({ error }, "Error processing character knowledge");
|
|
2384
|
-
} finally {
|
|
2385
|
-
this.knowledgeProcessingSemaphore.release();
|
|
2386
|
-
}
|
|
2387
|
-
});
|
|
2388
|
-
await Promise.all(processingPromises);
|
|
2389
|
-
}
|
|
2390
|
-
async _internalAddKnowledge(item, options = {
|
|
2391
|
-
targetTokens: 1500,
|
|
2392
|
-
overlap: 200,
|
|
2393
|
-
modelContextSize: 4096
|
|
2394
|
-
}, scope = {
|
|
2395
|
-
roomId: this.runtime.agentId,
|
|
2396
|
-
entityId: this.runtime.agentId,
|
|
2397
|
-
worldId: this.runtime.agentId
|
|
2398
|
-
}) {
|
|
2399
|
-
const finalScope = {
|
|
2400
|
-
roomId: scope?.roomId ?? this.runtime.agentId,
|
|
2401
|
-
worldId: scope?.worldId ?? this.runtime.agentId,
|
|
2402
|
-
entityId: scope?.entityId ?? this.runtime.agentId
|
|
2403
|
-
};
|
|
2404
|
-
const documentMetadata = {
|
|
2405
|
-
...item.metadata ?? {},
|
|
2406
|
-
type: import_core4.MemoryType.CUSTOM,
|
|
2407
|
-
documentId: item.id
|
|
2408
|
-
};
|
|
2409
|
-
const documentMemory = {
|
|
2410
|
-
id: item.id,
|
|
2411
|
-
agentId: this.runtime.agentId,
|
|
2412
|
-
roomId: finalScope.roomId,
|
|
2413
|
-
worldId: finalScope.worldId,
|
|
2414
|
-
entityId: finalScope.entityId,
|
|
2415
|
-
content: item.content,
|
|
2416
|
-
metadata: documentMetadata,
|
|
2417
|
-
createdAt: Date.now()
|
|
2418
|
-
};
|
|
2419
|
-
const existingDocument = await this.runtime.getMemoryById(item.id);
|
|
2420
|
-
if (existingDocument) {
|
|
2421
|
-
await this.runtime.updateMemory({
|
|
2422
|
-
...documentMemory,
|
|
2423
|
-
id: item.id
|
|
2424
|
-
});
|
|
2425
|
-
} else {
|
|
2426
|
-
await this.runtime.createMemory(documentMemory, "documents");
|
|
2427
|
-
}
|
|
2428
|
-
const fragments = await this.splitAndCreateFragments(item, options.targetTokens, options.overlap, finalScope);
|
|
2429
|
-
for (const fragment of fragments) {
|
|
2430
|
-
try {
|
|
2431
|
-
await this.processDocumentFragment(fragment);
|
|
2432
|
-
} catch (error) {
|
|
2433
|
-
import_core4.logger.error({ error }, `KnowledgeService: Error processing fragment ${fragment.id} for document ${item.id}`);
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
async processDocumentFragment(fragment) {
|
|
2438
|
-
try {
|
|
2439
|
-
await this.runtime.addEmbeddingToMemory(fragment);
|
|
2440
|
-
await this.runtime.createMemory(fragment, "knowledge");
|
|
2441
|
-
} catch (error) {
|
|
2442
|
-
import_core4.logger.error({ error }, `Error processing fragment ${fragment.id}`);
|
|
2443
|
-
throw error;
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
async splitAndCreateFragments(document, targetTokens, overlap, scope) {
|
|
2447
|
-
if (!document.content.text) {
|
|
2448
|
-
return [];
|
|
2449
|
-
}
|
|
2450
|
-
const text = document.content.text;
|
|
2451
|
-
const chunks = await import_core4.splitChunks(text, targetTokens, overlap);
|
|
2452
|
-
return chunks.map((chunk, index) => {
|
|
2453
|
-
const fragmentIdContent = `${document.id}-fragment-${index}-${Date.now()}`;
|
|
2454
|
-
const fragmentId = import_core4.createUniqueUuid(this.runtime, fragmentIdContent);
|
|
2455
|
-
return {
|
|
2456
|
-
id: fragmentId,
|
|
2457
|
-
entityId: scope.entityId,
|
|
2458
|
-
agentId: this.runtime.agentId,
|
|
2459
|
-
roomId: scope.roomId,
|
|
2460
|
-
worldId: scope.worldId,
|
|
2461
|
-
content: {
|
|
2462
|
-
text: chunk
|
|
2463
|
-
},
|
|
2464
|
-
metadata: {
|
|
2465
|
-
...document.metadata || {},
|
|
2466
|
-
type: import_core4.MemoryType.FRAGMENT,
|
|
2467
|
-
documentId: document.id,
|
|
2468
|
-
position: index,
|
|
2469
|
-
timestamp: Date.now()
|
|
2470
|
-
},
|
|
2471
|
-
createdAt: Date.now()
|
|
2472
|
-
};
|
|
2473
|
-
});
|
|
2474
|
-
}
|
|
2475
|
-
async getMemories(params) {
|
|
2476
|
-
return this.runtime.getMemories({
|
|
2477
|
-
...params,
|
|
2478
|
-
agentId: this.runtime.agentId
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
async countMemories(params) {
|
|
2482
|
-
const roomId = params.roomId || this.runtime.agentId;
|
|
2483
|
-
const unique = params.unique ?? false;
|
|
2484
|
-
const tableName = params.tableName;
|
|
2485
|
-
return this.runtime.countMemories(roomId, unique, tableName);
|
|
2486
|
-
}
|
|
2487
|
-
async deleteMemory(memoryId) {
|
|
2488
|
-
await this.runtime.deleteMemory(memoryId);
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
// actions.ts
|
|
2493
|
-
var processKnowledgeAction = {
|
|
2494
|
-
name: "PROCESS_KNOWLEDGE",
|
|
2495
|
-
description: "Process and store knowledge from a file path or text content into the knowledge base",
|
|
2496
|
-
similes: [],
|
|
2497
|
-
examples: [
|
|
2498
|
-
[
|
|
2499
|
-
{
|
|
2500
|
-
name: "user",
|
|
2501
|
-
content: {
|
|
2502
|
-
text: "Process the document at /path/to/document.pdf"
|
|
2503
|
-
}
|
|
2504
|
-
},
|
|
2505
|
-
{
|
|
2506
|
-
name: "assistant",
|
|
2507
|
-
content: {
|
|
2508
|
-
text: "I'll process the document at /path/to/document.pdf and add it to my knowledge base.",
|
|
2509
|
-
actions: ["PROCESS_KNOWLEDGE"]
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
],
|
|
2513
|
-
[
|
|
2514
|
-
{
|
|
2515
|
-
name: "user",
|
|
2516
|
-
content: {
|
|
2517
|
-
text: "Add this to your knowledge: The capital of France is Paris."
|
|
2518
|
-
}
|
|
2519
|
-
},
|
|
2520
|
-
{
|
|
2521
|
-
name: "assistant",
|
|
2522
|
-
content: {
|
|
2523
|
-
text: "I'll add that information to my knowledge base.",
|
|
2524
|
-
actions: ["PROCESS_KNOWLEDGE"]
|
|
2525
|
-
}
|
|
2526
|
-
}
|
|
2527
|
-
]
|
|
2528
|
-
],
|
|
2529
|
-
validate: async (runtime, message, state, options) => {
|
|
2530
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
2531
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
2532
|
-
const __avKeywords = ["process", "knowledge"];
|
|
2533
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
2534
|
-
const __avRegexOk = /\b(?:process|knowledge)\b/i.test(__avText);
|
|
2535
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
2536
|
-
const __avExpectedSource = "";
|
|
2537
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
2538
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
2539
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
2540
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
2541
|
-
return false;
|
|
2542
|
-
}
|
|
2543
|
-
const __avLegacyValidate = async (runtime2, message2, _state) => {
|
|
2544
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
2545
|
-
const knowledgeKeywords = [
|
|
2546
|
-
"process",
|
|
2547
|
-
"add",
|
|
2548
|
-
"upload",
|
|
2549
|
-
"document",
|
|
2550
|
-
"knowledge",
|
|
2551
|
-
"learn",
|
|
2552
|
-
"remember",
|
|
2553
|
-
"store",
|
|
2554
|
-
"ingest",
|
|
2555
|
-
"file"
|
|
2556
|
-
];
|
|
2557
|
-
const hasKeyword = knowledgeKeywords.some((keyword) => text.includes(keyword));
|
|
2558
|
-
const pathPattern = /(?:\/[\w.-]+)+|(?:[a-zA-Z]:[\\/][\w\s.-]+(?:[\\/][\w\s.-]+)*)/;
|
|
2559
|
-
const hasPath = pathPattern.test(text);
|
|
2560
|
-
const service = runtime2.getService(KnowledgeService.serviceType);
|
|
2561
|
-
if (!service) {
|
|
2562
|
-
import_core5.logger.warn("Knowledge service not available for PROCESS_KNOWLEDGE action");
|
|
2563
|
-
return false;
|
|
2564
|
-
}
|
|
2565
|
-
return hasKeyword || hasPath;
|
|
2566
|
-
};
|
|
2567
|
-
try {
|
|
2568
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
2569
|
-
} catch {
|
|
2570
|
-
return false;
|
|
2571
|
-
}
|
|
2572
|
-
},
|
|
2573
|
-
handler: async (runtime, message, _state, _options, callback) => {
|
|
2574
|
-
try {
|
|
2575
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
2576
|
-
if (!service) {
|
|
2577
|
-
throw new Error("Knowledge service not available");
|
|
2578
|
-
}
|
|
2579
|
-
const text = message.content.text || "";
|
|
2580
|
-
const pathPattern = /(?:\/[\w.-]+)+|(?:[a-zA-Z]:[\\/][\w\s.-]+(?:[\\/][\w\s.-]+)*)/;
|
|
2581
|
-
const pathMatch = text.match(pathPattern);
|
|
2582
|
-
let response;
|
|
2583
|
-
if (pathMatch) {
|
|
2584
|
-
const filePath = pathMatch[0];
|
|
2585
|
-
if (!fs2.existsSync(filePath)) {
|
|
2586
|
-
response = {
|
|
2587
|
-
text: `I couldn't find the file at ${filePath}. Please check the path and try again.`
|
|
2588
|
-
};
|
|
2589
|
-
if (callback) {
|
|
2590
|
-
await callback(response);
|
|
2591
|
-
}
|
|
2592
|
-
return;
|
|
2593
|
-
}
|
|
2594
|
-
const fileBuffer = fs2.readFileSync(filePath);
|
|
2595
|
-
const fileName = path2.basename(filePath);
|
|
2596
|
-
const fileExt = path2.extname(filePath).toLowerCase();
|
|
2597
|
-
let contentType = "text/plain";
|
|
2598
|
-
if (fileExt === ".pdf")
|
|
2599
|
-
contentType = "application/pdf";
|
|
2600
|
-
else if (fileExt === ".docx")
|
|
2601
|
-
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
2602
|
-
else if (fileExt === ".doc")
|
|
2603
|
-
contentType = "application/msword";
|
|
2604
|
-
else if ([".txt", ".md", ".tson", ".xml", ".csv"].includes(fileExt))
|
|
2605
|
-
contentType = "text/plain";
|
|
2606
|
-
const knowledgeOptions = {
|
|
2607
|
-
clientDocumentId: import_core5.stringToUuid(runtime.agentId + fileName + Date.now()),
|
|
2608
|
-
contentType,
|
|
2609
|
-
originalFilename: fileName,
|
|
2610
|
-
worldId: runtime.agentId,
|
|
2611
|
-
content: fileBuffer.toString("base64"),
|
|
2612
|
-
roomId: message.roomId,
|
|
2613
|
-
entityId: message.entityId
|
|
2614
|
-
};
|
|
2615
|
-
const result = await service.addKnowledge(knowledgeOptions);
|
|
2616
|
-
response = {
|
|
2617
|
-
text: `I've successfully processed the document "${fileName}". It has been split into ${result?.fragmentCount || 0} searchable fragments and added to my knowledge base.`
|
|
2618
|
-
};
|
|
2619
|
-
} else {
|
|
2620
|
-
const knowledgeContent = text.replace(/^(add|store|remember|process|learn)\s+(this|that|the following)?:?\s*/i, "").trim();
|
|
2621
|
-
if (!knowledgeContent) {
|
|
2622
|
-
response = {
|
|
2623
|
-
text: "I need some content to add to my knowledge base. Please provide text or a file path."
|
|
2624
|
-
};
|
|
2625
|
-
if (callback) {
|
|
2626
|
-
await callback(response);
|
|
2627
|
-
}
|
|
2628
|
-
return;
|
|
2629
|
-
}
|
|
2630
|
-
const knowledgeOptions = {
|
|
2631
|
-
clientDocumentId: import_core5.stringToUuid(`${runtime.agentId}text${Date.now()}user-knowledge`),
|
|
2632
|
-
contentType: "text/plain",
|
|
2633
|
-
originalFilename: "user-knowledge.txt",
|
|
2634
|
-
worldId: runtime.agentId,
|
|
2635
|
-
content: knowledgeContent,
|
|
2636
|
-
roomId: message.roomId,
|
|
2637
|
-
entityId: message.entityId
|
|
2638
|
-
};
|
|
2639
|
-
await service.addKnowledge(knowledgeOptions);
|
|
2640
|
-
response = {
|
|
2641
|
-
text: `I've added that information to my knowledge base. It has been stored and indexed for future reference.`
|
|
2642
|
-
};
|
|
2643
|
-
}
|
|
2644
|
-
if (callback) {
|
|
2645
|
-
await callback(response);
|
|
2646
|
-
}
|
|
2647
|
-
return { success: true, text: response.text };
|
|
2648
|
-
} catch (error) {
|
|
2649
|
-
import_core5.logger.error({ error }, "Error in PROCESS_KNOWLEDGE action");
|
|
2650
|
-
const errorResponse = {
|
|
2651
|
-
text: `I encountered an error while processing the knowledge: ${error instanceof Error ? error.message : String(error)}`
|
|
2652
|
-
};
|
|
2653
|
-
if (callback) {
|
|
2654
|
-
await callback(errorResponse);
|
|
2655
|
-
}
|
|
2656
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
};
|
|
2660
|
-
var searchKnowledgeAction = {
|
|
2661
|
-
name: "SEARCH_KNOWLEDGE",
|
|
2662
|
-
description: "Search the knowledge base for specific information",
|
|
2663
|
-
similes: [
|
|
2664
|
-
"search knowledge",
|
|
2665
|
-
"find information",
|
|
2666
|
-
"look up",
|
|
2667
|
-
"query knowledge base",
|
|
2668
|
-
"search documents",
|
|
2669
|
-
"find in knowledge"
|
|
2670
|
-
],
|
|
2671
|
-
examples: [
|
|
2672
|
-
[
|
|
2673
|
-
{
|
|
2674
|
-
name: "user",
|
|
2675
|
-
content: {
|
|
2676
|
-
text: "Search your knowledge for information about quantum computing"
|
|
2677
|
-
}
|
|
2678
|
-
},
|
|
2679
|
-
{
|
|
2680
|
-
name: "assistant",
|
|
2681
|
-
content: {
|
|
2682
|
-
text: "I'll search my knowledge base for information about quantum computing.",
|
|
2683
|
-
actions: ["SEARCH_KNOWLEDGE"]
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
]
|
|
2687
|
-
],
|
|
2688
|
-
validate: async (runtime, message, state, options) => {
|
|
2689
|
-
const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
|
|
2690
|
-
const __avText = __avTextRaw.toLowerCase();
|
|
2691
|
-
const __avKeywords = ["search", "knowledge"];
|
|
2692
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
2693
|
-
const __avRegexOk = /\b(?:search|knowledge)\b/i.test(__avText);
|
|
2694
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
2695
|
-
const __avExpectedSource = "";
|
|
2696
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
2697
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
2698
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
2699
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
2700
|
-
return false;
|
|
2701
|
-
}
|
|
2702
|
-
const __avLegacyValidate = async (runtime2, message2, _state) => {
|
|
2703
|
-
const text = message2.content.text?.toLowerCase() || "";
|
|
2704
|
-
const searchKeywords = ["search", "find", "look up", "query", "what do you know about"];
|
|
2705
|
-
const knowledgeKeywords = ["knowledge", "information", "document", "database"];
|
|
2706
|
-
const hasSearchKeyword = searchKeywords.some((keyword) => text.includes(keyword));
|
|
2707
|
-
const hasKnowledgeKeyword = knowledgeKeywords.some((keyword) => text.includes(keyword));
|
|
2708
|
-
const service = runtime2.getService(KnowledgeService.serviceType);
|
|
2709
|
-
if (!service) {
|
|
2710
|
-
return false;
|
|
2711
|
-
}
|
|
2712
|
-
return hasSearchKeyword && hasKnowledgeKeyword;
|
|
2713
|
-
};
|
|
2714
|
-
try {
|
|
2715
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
2716
|
-
} catch {
|
|
2717
|
-
return false;
|
|
2718
|
-
}
|
|
2719
|
-
},
|
|
2720
|
-
handler: async (runtime, message, _state, _options, callback) => {
|
|
2721
|
-
try {
|
|
2722
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
2723
|
-
if (!service) {
|
|
2724
|
-
throw new Error("Knowledge service not available");
|
|
2725
|
-
}
|
|
2726
|
-
const text = message.content.text || "";
|
|
2727
|
-
const query = text.replace(/^(search|find|look up|query)\s+(your\s+)?knowledge\s+(base\s+)?(for\s+)?/i, "").trim();
|
|
2728
|
-
if (!query) {
|
|
2729
|
-
const response2 = {
|
|
2730
|
-
text: "What would you like me to search for in my knowledge base?"
|
|
2731
|
-
};
|
|
2732
|
-
if (callback) {
|
|
2733
|
-
await callback(response2);
|
|
2734
|
-
}
|
|
2735
|
-
return;
|
|
2736
|
-
}
|
|
2737
|
-
const searchMessage = {
|
|
2738
|
-
...message,
|
|
2739
|
-
content: {
|
|
2740
|
-
text: query
|
|
2741
|
-
}
|
|
2742
|
-
};
|
|
2743
|
-
const results = await service.getKnowledge(searchMessage);
|
|
2744
|
-
let response;
|
|
2745
|
-
if (results.length === 0) {
|
|
2746
|
-
response = {
|
|
2747
|
-
text: `I couldn't find any information about "${query}" in my knowledge base.`
|
|
2748
|
-
};
|
|
2749
|
-
} else {
|
|
2750
|
-
const formattedResults = results.slice(0, 3).map((item, index) => `${index + 1}. ${item.content.text}`).join(`
|
|
2751
|
-
|
|
2752
|
-
`);
|
|
2753
|
-
response = {
|
|
2754
|
-
text: `Here's what I found about "${query}":
|
|
2755
|
-
|
|
2756
|
-
${formattedResults}`
|
|
2757
|
-
};
|
|
2758
|
-
}
|
|
2759
|
-
if (callback) {
|
|
2760
|
-
await callback(response);
|
|
2761
|
-
}
|
|
2762
|
-
return { success: true, text: response.text };
|
|
2763
|
-
} catch (error) {
|
|
2764
|
-
import_core5.logger.error({ error }, "Error in SEARCH_KNOWLEDGE action");
|
|
2765
|
-
const errorResponse = {
|
|
2766
|
-
text: `I encountered an error while searching the knowledge base: ${error instanceof Error ? error.message : String(error)}`
|
|
2767
|
-
};
|
|
2768
|
-
if (callback) {
|
|
2769
|
-
await callback(errorResponse);
|
|
2770
|
-
}
|
|
2771
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
};
|
|
2775
|
-
var knowledgeActions = [processKnowledgeAction, searchKnowledgeAction];
|
|
2776
|
-
|
|
2777
|
-
// documents-provider.ts
|
|
2778
|
-
var import_core6 = require("@elizaos/core");
|
|
2779
|
-
var documentsProvider = {
|
|
2780
|
-
name: "AVAILABLE_DOCUMENTS",
|
|
2781
|
-
description: "List of documents available in the knowledge base. Shows which documents the agent can reference and retrieve information from.",
|
|
2782
|
-
dynamic: true,
|
|
2783
|
-
get: async (runtime, _message, _state) => {
|
|
2784
|
-
try {
|
|
2785
|
-
const knowledgeService = runtime.getService("knowledge");
|
|
2786
|
-
if (!knowledgeService) {
|
|
2787
|
-
import_core6.logger.warn("Knowledge service not available for documents provider");
|
|
2788
|
-
return {
|
|
2789
|
-
data: { documents: [] },
|
|
2790
|
-
values: {
|
|
2791
|
-
documentsCount: 0,
|
|
2792
|
-
documents: "",
|
|
2793
|
-
availableDocuments: ""
|
|
2794
|
-
},
|
|
2795
|
-
text: ""
|
|
2796
|
-
};
|
|
2797
|
-
}
|
|
2798
|
-
const allMemories = await knowledgeService.getMemories({
|
|
2799
|
-
tableName: "documents",
|
|
2800
|
-
roomId: runtime.agentId,
|
|
2801
|
-
count: 100
|
|
2802
|
-
});
|
|
2803
|
-
const documents = allMemories.filter((memory) => memory.metadata?.type === import_core6.MemoryType.DOCUMENT);
|
|
2804
|
-
if (!documents || documents.length === 0) {
|
|
2805
|
-
return {
|
|
2806
|
-
data: { documents: [] },
|
|
2807
|
-
values: {
|
|
2808
|
-
documentsCount: 0,
|
|
2809
|
-
documents: "",
|
|
2810
|
-
availableDocuments: ""
|
|
2811
|
-
},
|
|
2812
|
-
text: ""
|
|
2813
|
-
};
|
|
2814
|
-
}
|
|
2815
|
-
const documentsList = documents.map((doc, index) => {
|
|
2816
|
-
const metadata = doc.metadata;
|
|
2817
|
-
const filename = metadata?.filename || metadata?.title || `Document ${index + 1}`;
|
|
2818
|
-
const fileType = metadata?.fileExt || metadata?.fileType || "";
|
|
2819
|
-
const source = metadata?.source || "upload";
|
|
2820
|
-
const fileSize = metadata?.fileSize;
|
|
2821
|
-
const parts = [filename];
|
|
2822
|
-
if (fileType) {
|
|
2823
|
-
parts.push(fileType);
|
|
2824
|
-
}
|
|
2825
|
-
if (fileSize) {
|
|
2826
|
-
const sizeKB = Math.round(fileSize / 1024);
|
|
2827
|
-
if (sizeKB > 1024) {
|
|
2828
|
-
parts.push(`${Math.round(sizeKB / 1024)}MB`);
|
|
2829
|
-
} else {
|
|
2830
|
-
parts.push(`${sizeKB}KB`);
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
if (source && source !== "upload") {
|
|
2834
|
-
parts.push(`from ${source}`);
|
|
2835
|
-
}
|
|
2836
|
-
return parts.join(" - ");
|
|
2837
|
-
}).join(`
|
|
2838
|
-
`);
|
|
2839
|
-
const documentsText = import_core6.addHeader("# Available Documents", `${documents.length} document(s) in knowledge base:
|
|
2840
|
-
${documentsList}`);
|
|
2841
|
-
return {
|
|
2842
|
-
data: {
|
|
2843
|
-
documents: documents.map((doc) => ({
|
|
2844
|
-
id: doc.id,
|
|
2845
|
-
filename: doc.metadata?.filename || doc.metadata?.title,
|
|
2846
|
-
fileType: doc.metadata?.fileType || doc.metadata?.fileExt,
|
|
2847
|
-
source: doc.metadata?.source
|
|
2848
|
-
})),
|
|
2849
|
-
count: documents.length
|
|
2850
|
-
},
|
|
2851
|
-
values: {
|
|
2852
|
-
documentsCount: documents.length,
|
|
2853
|
-
documents: documentsList,
|
|
2854
|
-
availableDocuments: documentsText
|
|
2855
|
-
},
|
|
2856
|
-
text: documentsText
|
|
2857
|
-
};
|
|
2858
|
-
} catch (error) {
|
|
2859
|
-
import_core6.logger.error("Error in documents provider:", error instanceof Error ? error.message : String(error));
|
|
2860
|
-
return {
|
|
2861
|
-
data: { documents: [], error: error instanceof Error ? error.message : String(error) },
|
|
2862
|
-
values: {
|
|
2863
|
-
documentsCount: 0,
|
|
2864
|
-
documents: "",
|
|
2865
|
-
availableDocuments: ""
|
|
2866
|
-
},
|
|
2867
|
-
text: ""
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
};
|
|
2872
|
-
|
|
2873
|
-
// provider.ts
|
|
2874
|
-
var import_core7 = require("@elizaos/core");
|
|
2875
|
-
var knowledgeProvider = {
|
|
2876
|
-
name: "KNOWLEDGE",
|
|
2877
|
-
description: "Knowledge from the knowledge base that the agent knows, retrieved whenever the agent needs to answer a question about their expertise.",
|
|
2878
|
-
dynamic: true,
|
|
2879
|
-
get: async (runtime, message) => {
|
|
2880
|
-
const knowledgeService = runtime.getService("knowledge");
|
|
2881
|
-
const knowledgeData = await knowledgeService?.getKnowledge(message);
|
|
2882
|
-
if (!knowledgeData || knowledgeData.length === 0) {
|
|
2883
|
-
return {
|
|
2884
|
-
text: "",
|
|
2885
|
-
values: { knowledge: "", knowledgeUsed: false },
|
|
2886
|
-
data: { knowledge: "", ragMetadata: null, knowledgeUsed: false }
|
|
2887
|
-
};
|
|
2888
|
-
}
|
|
2889
|
-
const firstFiveKnowledgeItems = knowledgeData.slice(0, 5);
|
|
2890
|
-
let knowledge = import_core7.addHeader("# Knowledge", firstFiveKnowledgeItems.map((item) => `- ${item.content.text}`).join(`
|
|
2891
|
-
`));
|
|
2892
|
-
const tokenLength = 3.5;
|
|
2893
|
-
const maxChars = 4000 * tokenLength;
|
|
2894
|
-
if (knowledge.length > maxChars) {
|
|
2895
|
-
knowledge = knowledge.slice(0, maxChars);
|
|
86
|
+
var import_multer = __toESM(require("multer"));
|
|
87
|
+
|
|
88
|
+
// utils.ts
|
|
89
|
+
var import_node_buffer = require("node:buffer");
|
|
90
|
+
var mammoth = __toESM(require("mammoth"));
|
|
91
|
+
var import_unpdf = require("unpdf");
|
|
92
|
+
var MAX_FALLBACK_SIZE_BYTES = 5 * 1024 * 1024;
|
|
93
|
+
function normalizeS3Url(url) {
|
|
94
|
+
try {
|
|
95
|
+
const urlObj = new URL(url);
|
|
96
|
+
return `${urlObj.origin}${urlObj.pathname}`;
|
|
97
|
+
} catch {
|
|
98
|
+
return url;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function fetchUrlContent(url) {
|
|
102
|
+
try {
|
|
103
|
+
const controller = new AbortController;
|
|
104
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
105
|
+
const response = await fetch(url, {
|
|
106
|
+
signal: controller.signal,
|
|
107
|
+
headers: {
|
|
108
|
+
"User-Agent": "Eliza-Knowledge-Plugin/1.0"
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
clearTimeout(timeoutId);
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
|
|
2896
114
|
}
|
|
2897
|
-
const
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
fragmentId: fragment.id,
|
|
2902
|
-
documentTitle: fragmentMetadata?.filename || fragmentMetadata?.title || "",
|
|
2903
|
-
similarityScore: fragment.similarity,
|
|
2904
|
-
contentPreview: `${(fragment.content?.text || "").substring(0, 100)}...`
|
|
2905
|
-
};
|
|
2906
|
-
}),
|
|
2907
|
-
queryText: message.content?.text || "",
|
|
2908
|
-
totalFragments: knowledgeData.length,
|
|
2909
|
-
retrievalTimestamp: Date.now()
|
|
2910
|
-
};
|
|
2911
|
-
knowledgeService.setPendingRAGMetadata(ragMetadata);
|
|
2912
|
-
setTimeout(async () => {
|
|
2913
|
-
await knowledgeService.enrichRecentMemoriesWithPendingRAG();
|
|
2914
|
-
}, 2000);
|
|
115
|
+
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
116
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
117
|
+
const buffer = import_node_buffer.Buffer.from(arrayBuffer);
|
|
118
|
+
const base64Content = buffer.toString("base64");
|
|
2915
119
|
return {
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
ragMetadata,
|
|
2919
|
-
knowledgeUsed: true
|
|
2920
|
-
},
|
|
2921
|
-
values: {
|
|
2922
|
-
knowledge,
|
|
2923
|
-
knowledgeUsed: true
|
|
2924
|
-
},
|
|
2925
|
-
text: knowledge,
|
|
2926
|
-
ragMetadata,
|
|
2927
|
-
knowledgeUsed: true
|
|
120
|
+
content: base64Content,
|
|
121
|
+
contentType
|
|
2928
122
|
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
125
|
+
throw new Error(`Failed to fetch content from URL: ${errorMessage}`);
|
|
2929
126
|
}
|
|
2930
|
-
}
|
|
127
|
+
}
|
|
2931
128
|
|
|
2932
129
|
// routes.ts
|
|
2933
|
-
var import_node_fs = __toESM(require("node:fs"));
|
|
2934
|
-
var import_node_path = __toESM(require("node:path"));
|
|
2935
|
-
var import_core8 = require("@elizaos/core");
|
|
2936
|
-
var import_multer = __toESM(require("multer"));
|
|
2937
130
|
function asWritableStream(res) {
|
|
2938
131
|
return res;
|
|
2939
132
|
}
|
|
@@ -2980,7 +173,7 @@ var cleanupFile = (filePath) => {
|
|
|
2980
173
|
try {
|
|
2981
174
|
import_node_fs.default.unlinkSync(filePath);
|
|
2982
175
|
} catch (error) {
|
|
2983
|
-
|
|
176
|
+
import_core.logger.error({ error }, `Error cleaning up file ${filePath}`);
|
|
2984
177
|
}
|
|
2985
178
|
}
|
|
2986
179
|
};
|
|
@@ -2992,7 +185,7 @@ var cleanupFiles = (files) => {
|
|
|
2992
185
|
}
|
|
2993
186
|
};
|
|
2994
187
|
async function uploadKnowledgeHandler(req, res, runtime) {
|
|
2995
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
188
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
2996
189
|
if (!service) {
|
|
2997
190
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
2998
191
|
}
|
|
@@ -3009,15 +202,15 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3009
202
|
}
|
|
3010
203
|
const invalidFiles = files.filter((file) => {
|
|
3011
204
|
if (file.size === 0) {
|
|
3012
|
-
|
|
205
|
+
import_core.logger.warn(`File ${file.originalname} is empty`);
|
|
3013
206
|
return true;
|
|
3014
207
|
}
|
|
3015
208
|
if (!file.originalname || file.originalname.trim() === "") {
|
|
3016
|
-
|
|
209
|
+
import_core.logger.warn(`File has no name`);
|
|
3017
210
|
return true;
|
|
3018
211
|
}
|
|
3019
212
|
if (!file.path) {
|
|
3020
|
-
|
|
213
|
+
import_core.logger.warn(`File ${file.originalname} has no path`);
|
|
3021
214
|
return true;
|
|
3022
215
|
}
|
|
3023
216
|
return false;
|
|
@@ -3029,11 +222,11 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3029
222
|
}
|
|
3030
223
|
const agentId = req.body?.agentId || req.query?.agentId;
|
|
3031
224
|
if (!agentId) {
|
|
3032
|
-
|
|
225
|
+
import_core.logger.error("No agent ID provided in upload request");
|
|
3033
226
|
return sendError(res, 400, "MISSING_AGENT_ID", "Agent ID is required for uploading knowledge");
|
|
3034
227
|
}
|
|
3035
228
|
const worldId = req.body?.worldId || agentId;
|
|
3036
|
-
|
|
229
|
+
import_core.logger.info(`Processing file upload for agent: ${agentId}`);
|
|
3037
230
|
const processingPromises = files.map(async (file) => {
|
|
3038
231
|
const originalFilename = file.originalname;
|
|
3039
232
|
const filePath = file.path;
|
|
@@ -3061,7 +254,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3061
254
|
status: "success"
|
|
3062
255
|
};
|
|
3063
256
|
} catch (fileError) {
|
|
3064
|
-
|
|
257
|
+
import_core.logger.error(`Error processing file ${file.originalname}: ${fileError instanceof Error ? fileError.message : String(fileError)}`);
|
|
3065
258
|
cleanupFile(filePath);
|
|
3066
259
|
return {
|
|
3067
260
|
id: "",
|
|
@@ -3080,7 +273,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3080
273
|
}
|
|
3081
274
|
const agentId = req.body?.agentId || req.query?.agentId;
|
|
3082
275
|
if (!agentId) {
|
|
3083
|
-
|
|
276
|
+
import_core.logger.error("No agent ID provided in URL request");
|
|
3084
277
|
return sendError(res, 400, "MISSING_AGENT_ID", "Agent ID is required for uploading knowledge from URLs");
|
|
3085
278
|
}
|
|
3086
279
|
const processingPromises = fileUrls.map(async (fileUrl) => {
|
|
@@ -3090,7 +283,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3090
283
|
const pathSegments = urlObject.pathname.split("/");
|
|
3091
284
|
const encodedFilename = pathSegments[pathSegments.length - 1] || "document.pdf";
|
|
3092
285
|
const originalFilename = decodeURIComponent(encodedFilename);
|
|
3093
|
-
|
|
286
|
+
import_core.logger.debug(`Fetching content from URL: ${fileUrl}`);
|
|
3094
287
|
const { content, contentType: fetchedContentType } = await fetchUrlContent(fileUrl);
|
|
3095
288
|
let contentType = fetchedContentType;
|
|
3096
289
|
if (contentType === "application/octet-stream") {
|
|
@@ -3137,7 +330,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3137
330
|
status: "success"
|
|
3138
331
|
};
|
|
3139
332
|
} catch (urlError) {
|
|
3140
|
-
|
|
333
|
+
import_core.logger.error(`Error processing URL ${fileUrl}: ${urlError instanceof Error ? urlError.message : String(urlError)}`);
|
|
3141
334
|
return {
|
|
3142
335
|
fileUrl,
|
|
3143
336
|
status: "error_processing",
|
|
@@ -3149,7 +342,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3149
342
|
sendSuccess(res, results);
|
|
3150
343
|
}
|
|
3151
344
|
} catch (error) {
|
|
3152
|
-
|
|
345
|
+
import_core.logger.error({ error }, "Error processing knowledge");
|
|
3153
346
|
if (hasUploadedFiles) {
|
|
3154
347
|
cleanupFiles(req.files);
|
|
3155
348
|
}
|
|
@@ -3157,7 +350,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3157
350
|
}
|
|
3158
351
|
}
|
|
3159
352
|
async function getKnowledgeDocumentsHandler(req, res, runtime) {
|
|
3160
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
353
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3161
354
|
if (!service) {
|
|
3162
355
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found for getKnowledgeDocumentsHandler");
|
|
3163
356
|
}
|
|
@@ -3174,7 +367,7 @@ async function getKnowledgeDocumentsHandler(req, res, runtime) {
|
|
|
3174
367
|
let filteredMemories = memories;
|
|
3175
368
|
if (fileUrls && fileUrls.length > 0) {
|
|
3176
369
|
const normalizedRequestUrls = fileUrls.map((url) => normalizeS3Url(String(url)));
|
|
3177
|
-
const urlBasedIds = normalizedRequestUrls.map((url) =>
|
|
370
|
+
const urlBasedIds = normalizedRequestUrls.map((url) => import_core.createUniqueUuid(runtime, url));
|
|
3178
371
|
filteredMemories = memories.filter((memory) => urlBasedIds.includes(memory.id) || memory.metadata && ("url" in memory.metadata) && typeof memory.metadata.url === "string" && normalizedRequestUrls.includes(normalizeS3Url(memory.metadata.url)));
|
|
3179
372
|
}
|
|
3180
373
|
const cleanMemories = includeEmbedding ? filteredMemories : filteredMemories.map((memory) => ({
|
|
@@ -3188,42 +381,42 @@ async function getKnowledgeDocumentsHandler(req, res, runtime) {
|
|
|
3188
381
|
totalRequested: fileUrls ? fileUrls.length : 0
|
|
3189
382
|
});
|
|
3190
383
|
} catch (error) {
|
|
3191
|
-
|
|
384
|
+
import_core.logger.error({ error }, "Error retrieving documents");
|
|
3192
385
|
sendError(res, 500, "RETRIEVAL_ERROR", "Failed to retrieve documents", error instanceof Error ? error.message : String(error));
|
|
3193
386
|
}
|
|
3194
387
|
}
|
|
3195
388
|
async function deleteKnowledgeDocumentHandler(req, res, runtime) {
|
|
3196
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
389
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3197
390
|
if (!service) {
|
|
3198
391
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found for deleteKnowledgeDocumentHandler");
|
|
3199
392
|
}
|
|
3200
393
|
const knowledgeId = req.params?.knowledgeId;
|
|
3201
394
|
if (!knowledgeId || knowledgeId.length < 36) {
|
|
3202
|
-
|
|
395
|
+
import_core.logger.error(`Invalid knowledge ID format: ${knowledgeId}`);
|
|
3203
396
|
return sendError(res, 400, "INVALID_ID", "Invalid Knowledge ID format");
|
|
3204
397
|
}
|
|
3205
398
|
try {
|
|
3206
399
|
const typedKnowledgeId = knowledgeId;
|
|
3207
|
-
|
|
400
|
+
import_core.logger.debug(`Deleting document: ${typedKnowledgeId}`);
|
|
3208
401
|
await service.deleteMemory(typedKnowledgeId);
|
|
3209
402
|
sendSuccess(res, null, 204);
|
|
3210
403
|
} catch (error) {
|
|
3211
|
-
|
|
404
|
+
import_core.logger.error({ error }, `Error deleting document ${knowledgeId}`);
|
|
3212
405
|
sendError(res, 500, "DELETE_ERROR", "Failed to delete document", error instanceof Error ? error.message : String(error));
|
|
3213
406
|
}
|
|
3214
407
|
}
|
|
3215
408
|
async function getKnowledgeByIdHandler(req, res, runtime) {
|
|
3216
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
409
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3217
410
|
if (!service) {
|
|
3218
411
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found for getKnowledgeByIdHandler");
|
|
3219
412
|
}
|
|
3220
413
|
const knowledgeId = req.params?.knowledgeId;
|
|
3221
414
|
if (!knowledgeId || knowledgeId.length < 36) {
|
|
3222
|
-
|
|
415
|
+
import_core.logger.error(`Invalid knowledge ID format: ${knowledgeId}`);
|
|
3223
416
|
return sendError(res, 400, "INVALID_ID", "Invalid Knowledge ID format");
|
|
3224
417
|
}
|
|
3225
418
|
try {
|
|
3226
|
-
|
|
419
|
+
import_core.logger.debug(`Retrieving document: ${knowledgeId}`);
|
|
3227
420
|
const memories = await service.getMemories({
|
|
3228
421
|
tableName: "documents",
|
|
3229
422
|
count: 1e4
|
|
@@ -3239,7 +432,7 @@ async function getKnowledgeByIdHandler(req, res, runtime) {
|
|
|
3239
432
|
};
|
|
3240
433
|
sendSuccess(res, { document: cleanDocument });
|
|
3241
434
|
} catch (error) {
|
|
3242
|
-
|
|
435
|
+
import_core.logger.error({ error }, `Error retrieving document ${knowledgeId}`);
|
|
3243
436
|
sendError(res, 500, "RETRIEVAL_ERROR", "Failed to retrieve document", error instanceof Error ? error.message : String(error));
|
|
3244
437
|
}
|
|
3245
438
|
}
|
|
@@ -3248,7 +441,7 @@ async function knowledgePanelHandler(req, res, runtime) {
|
|
|
3248
441
|
const requestPath = req.originalUrl || req.url || req.path || "";
|
|
3249
442
|
const pluginBasePath = requestPath.replace(/\/display.*$/, "");
|
|
3250
443
|
try {
|
|
3251
|
-
const currentDir = import_node_path.default.dirname(new URL("file:///Users/shawwalters/eliza-workspace/plugins/plugin-knowledge/typescript/routes.ts").pathname);
|
|
444
|
+
const currentDir = import_node_path.default.dirname(new URL("file:///Users/shawwalters/eliza-workspace/milady/plugins/plugin-knowledge/typescript/routes.ts").pathname);
|
|
3252
445
|
const frontendPath = import_node_path.default.join(currentDir, "../dist/index.html");
|
|
3253
446
|
if (import_node_fs.default.existsSync(frontendPath)) {
|
|
3254
447
|
const html = await import_node_fs.default.promises.readFile(frontendPath, "utf8");
|
|
@@ -3317,14 +510,14 @@ async function knowledgePanelHandler(req, res, runtime) {
|
|
|
3317
510
|
res.end(html);
|
|
3318
511
|
}
|
|
3319
512
|
} catch (error) {
|
|
3320
|
-
|
|
513
|
+
import_core.logger.error({ error }, "Error serving frontend");
|
|
3321
514
|
sendError(res, 500, "FRONTEND_ERROR", "Failed to load knowledge panel", error instanceof Error ? error.message : String(error));
|
|
3322
515
|
}
|
|
3323
516
|
}
|
|
3324
517
|
async function frontendAssetHandler(req, res, _runtime) {
|
|
3325
518
|
try {
|
|
3326
519
|
const fullPath = req.originalUrl || req.url || req.path || "";
|
|
3327
|
-
const currentDir = import_node_path.default.dirname(new URL("file:///Users/shawwalters/eliza-workspace/plugins/plugin-knowledge/typescript/routes.ts").pathname);
|
|
520
|
+
const currentDir = import_node_path.default.dirname(new URL("file:///Users/shawwalters/eliza-workspace/milady/plugins/plugin-knowledge/typescript/routes.ts").pathname);
|
|
3328
521
|
const assetsMarker = "/assets/";
|
|
3329
522
|
const assetsStartIndex = fullPath.lastIndexOf(assetsMarker);
|
|
3330
523
|
let assetName = null;
|
|
@@ -3353,12 +546,12 @@ async function frontendAssetHandler(req, res, _runtime) {
|
|
|
3353
546
|
sendError(res, 404, "NOT_FOUND", `Asset not found: ${req.url}`);
|
|
3354
547
|
}
|
|
3355
548
|
} catch (error) {
|
|
3356
|
-
|
|
549
|
+
import_core.logger.error({ error }, `Error serving asset ${req.url}`);
|
|
3357
550
|
sendError(res, 500, "ASSET_ERROR", `Failed to load asset ${req.url}`, error instanceof Error ? error.message : String(error));
|
|
3358
551
|
}
|
|
3359
552
|
}
|
|
3360
553
|
async function getKnowledgeChunksHandler(req, res, runtime) {
|
|
3361
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
554
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3362
555
|
if (!service) {
|
|
3363
556
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3364
557
|
}
|
|
@@ -3412,12 +605,12 @@ async function getKnowledgeChunksHandler(req, res, runtime) {
|
|
|
3412
605
|
}
|
|
3413
606
|
});
|
|
3414
607
|
} catch (error) {
|
|
3415
|
-
|
|
608
|
+
import_core.logger.error({ error }, "Error retrieving chunks");
|
|
3416
609
|
sendError(res, 500, "RETRIEVAL_ERROR", "Failed to retrieve knowledge chunks", error instanceof Error ? error.message : String(error));
|
|
3417
610
|
}
|
|
3418
611
|
}
|
|
3419
612
|
async function searchKnowledgeHandler(req, res, runtime) {
|
|
3420
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
613
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3421
614
|
if (!service) {
|
|
3422
615
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3423
616
|
}
|
|
@@ -3433,7 +626,7 @@ async function searchKnowledgeHandler(req, res, runtime) {
|
|
|
3433
626
|
if (!searchText || searchText.trim().length === 0) {
|
|
3434
627
|
return sendError(res, 400, "INVALID_QUERY", "Search query cannot be empty");
|
|
3435
628
|
}
|
|
3436
|
-
const embedding = await runtime.useModel(
|
|
629
|
+
const embedding = await runtime.useModel(import_core.ModelType.TEXT_EMBEDDING, {
|
|
3437
630
|
text: searchText
|
|
3438
631
|
});
|
|
3439
632
|
const results = await runtime.searchMemories({
|
|
@@ -3476,12 +669,12 @@ async function searchKnowledgeHandler(req, res, runtime) {
|
|
|
3476
669
|
count: enhancedResults.length
|
|
3477
670
|
});
|
|
3478
671
|
} catch (error) {
|
|
3479
|
-
|
|
672
|
+
import_core.logger.error({ error }, "Error searching knowledge");
|
|
3480
673
|
sendError(res, 500, "SEARCH_ERROR", "Failed to search knowledge", error instanceof Error ? error.message : String(error));
|
|
3481
674
|
}
|
|
3482
675
|
}
|
|
3483
676
|
async function getGraphNodesHandler(req, res, runtime) {
|
|
3484
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
677
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3485
678
|
if (!service) {
|
|
3486
679
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3487
680
|
}
|
|
@@ -3510,7 +703,7 @@ async function getGraphNodesHandler(req, res, runtime) {
|
|
|
3510
703
|
const links = [];
|
|
3511
704
|
paginatedDocuments.forEach((doc) => {
|
|
3512
705
|
if (!doc.id) {
|
|
3513
|
-
|
|
706
|
+
import_core.logger.warn("Skipping document without ID");
|
|
3514
707
|
return;
|
|
3515
708
|
}
|
|
3516
709
|
nodes.push({ id: doc.id, type: "document" });
|
|
@@ -3528,7 +721,7 @@ async function getGraphNodesHandler(req, res, runtime) {
|
|
|
3528
721
|
const docFragments = allFragments.filter((fragment) => {
|
|
3529
722
|
const metadata = fragment.metadata;
|
|
3530
723
|
const typeString = typeof metadata?.type === "string" ? metadata.type : null;
|
|
3531
|
-
const isFragment = typeString && typeString.toLowerCase() === "fragment" || metadata?.type ===
|
|
724
|
+
const isFragment = typeString && typeString.toLowerCase() === "fragment" || metadata?.type === import_core.MemoryType.FRAGMENT || !metadata?.type && metadata?.documentId;
|
|
3532
725
|
return metadata?.documentId === doc.id && isFragment;
|
|
3533
726
|
});
|
|
3534
727
|
docFragments.forEach((frag) => {
|
|
@@ -3552,12 +745,12 @@ async function getGraphNodesHandler(req, res, runtime) {
|
|
|
3552
745
|
}
|
|
3553
746
|
});
|
|
3554
747
|
} catch (error) {
|
|
3555
|
-
|
|
748
|
+
import_core.logger.error({ error }, "Error fetching graph nodes");
|
|
3556
749
|
sendError(res, 500, "GRAPH_ERROR", "Failed to fetch graph nodes", error instanceof Error ? error.message : String(error));
|
|
3557
750
|
}
|
|
3558
751
|
}
|
|
3559
752
|
async function getGraphNodeDetailsHandler(req, res, runtime) {
|
|
3560
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
753
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3561
754
|
if (!service) {
|
|
3562
755
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3563
756
|
}
|
|
@@ -3611,15 +804,15 @@ async function getGraphNodeDetailsHandler(req, res, runtime) {
|
|
|
3611
804
|
});
|
|
3612
805
|
return;
|
|
3613
806
|
}
|
|
3614
|
-
|
|
807
|
+
import_core.logger.error(`Node ${nodeId} not found`);
|
|
3615
808
|
sendError(res, 404, "NOT_FOUND", `Node with ID ${nodeId} not found`);
|
|
3616
809
|
} catch (error) {
|
|
3617
|
-
|
|
810
|
+
import_core.logger.error({ error }, `Error fetching node details for ${nodeId}`);
|
|
3618
811
|
sendError(res, 500, "GRAPH_ERROR", "Failed to fetch node details", error instanceof Error ? error.message : String(error));
|
|
3619
812
|
}
|
|
3620
813
|
}
|
|
3621
814
|
async function expandDocumentGraphHandler(req, res, runtime) {
|
|
3622
|
-
const service = runtime.getService(KnowledgeService.serviceType);
|
|
815
|
+
const service = runtime.getService(import_core.KnowledgeService.serviceType);
|
|
3623
816
|
if (!service) {
|
|
3624
817
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3625
818
|
}
|
|
@@ -3637,7 +830,7 @@ async function expandDocumentGraphHandler(req, res, runtime) {
|
|
|
3637
830
|
const documentFragments = allFragments.filter((fragment) => {
|
|
3638
831
|
const metadata = fragment.metadata;
|
|
3639
832
|
const typeString = typeof metadata?.type === "string" ? metadata.type : null;
|
|
3640
|
-
const isFragment = typeString && typeString.toLowerCase() === "fragment" || metadata?.type ===
|
|
833
|
+
const isFragment = typeString && typeString.toLowerCase() === "fragment" || metadata?.type === import_core.MemoryType.FRAGMENT || !metadata?.type && metadata?.documentId;
|
|
3641
834
|
return metadata?.documentId === documentId && isFragment;
|
|
3642
835
|
});
|
|
3643
836
|
const nodes = documentFragments.filter((frag) => frag.id !== undefined).map((frag) => ({
|
|
@@ -3655,7 +848,7 @@ async function expandDocumentGraphHandler(req, res, runtime) {
|
|
|
3655
848
|
fragmentCount: nodes.length
|
|
3656
849
|
});
|
|
3657
850
|
} catch (error) {
|
|
3658
|
-
|
|
851
|
+
import_core.logger.error({ error }, `Error expanding document ${documentId}`);
|
|
3659
852
|
sendError(res, 500, "GRAPH_ERROR", "Failed to expand document", error instanceof Error ? error.message : String(error));
|
|
3660
853
|
}
|
|
3661
854
|
}
|
|
@@ -3664,7 +857,7 @@ async function uploadKnowledgeWithMulter(req, res, runtime) {
|
|
|
3664
857
|
const uploadArray = upload.array("files", parseInt(String(runtime.getSetting("KNOWLEDGE_MAX_FILES") || "10"), 10));
|
|
3665
858
|
uploadArray(req, res, (err) => {
|
|
3666
859
|
if (err) {
|
|
3667
|
-
|
|
860
|
+
import_core.logger.error({ error: err }, "File upload error");
|
|
3668
861
|
return sendError(res, 400, "UPLOAD_ERROR", err.message);
|
|
3669
862
|
}
|
|
3670
863
|
uploadKnowledgeHandler(req, res, runtime);
|
|
@@ -3733,22 +926,205 @@ var knowledgeRoutes = [
|
|
|
3733
926
|
}
|
|
3734
927
|
];
|
|
3735
928
|
|
|
929
|
+
// index.ts
|
|
930
|
+
var import_core5 = require("@elizaos/core");
|
|
931
|
+
|
|
932
|
+
// documents-provider.ts
|
|
933
|
+
var import_core2 = require("@elizaos/core");
|
|
934
|
+
var documentsProvider = {
|
|
935
|
+
name: "AVAILABLE_DOCUMENTS",
|
|
936
|
+
description: "List of documents available in the knowledge base. Shows which documents the agent can reference and retrieve information from.",
|
|
937
|
+
dynamic: true,
|
|
938
|
+
get: async (runtime, _message, _state) => {
|
|
939
|
+
try {
|
|
940
|
+
const knowledgeService = runtime.getService("knowledge");
|
|
941
|
+
if (!knowledgeService) {
|
|
942
|
+
import_core2.logger.warn("Knowledge service not available for documents provider");
|
|
943
|
+
return {
|
|
944
|
+
data: { documents: [] },
|
|
945
|
+
values: {
|
|
946
|
+
documentsCount: 0,
|
|
947
|
+
documents: "",
|
|
948
|
+
availableDocuments: ""
|
|
949
|
+
},
|
|
950
|
+
text: ""
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
const allMemories = await knowledgeService.getMemories({
|
|
954
|
+
tableName: "documents",
|
|
955
|
+
roomId: runtime.agentId,
|
|
956
|
+
count: 100
|
|
957
|
+
});
|
|
958
|
+
const documents = allMemories.filter((memory) => memory.metadata?.type === import_core2.MemoryType.DOCUMENT);
|
|
959
|
+
if (!documents || documents.length === 0) {
|
|
960
|
+
return {
|
|
961
|
+
data: { documents: [] },
|
|
962
|
+
values: {
|
|
963
|
+
documentsCount: 0,
|
|
964
|
+
documents: "",
|
|
965
|
+
availableDocuments: ""
|
|
966
|
+
},
|
|
967
|
+
text: ""
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
const documentsList = documents.map((doc, index) => {
|
|
971
|
+
const metadata = doc.metadata;
|
|
972
|
+
const filename = metadata?.filename || metadata?.title || `Document ${index + 1}`;
|
|
973
|
+
const fileType = metadata?.fileExt || metadata?.fileType || "";
|
|
974
|
+
const source = metadata?.source || "upload";
|
|
975
|
+
const fileSize = metadata?.fileSize;
|
|
976
|
+
const parts = [filename];
|
|
977
|
+
if (fileType) {
|
|
978
|
+
parts.push(fileType);
|
|
979
|
+
}
|
|
980
|
+
if (fileSize) {
|
|
981
|
+
const sizeKB = Math.round(fileSize / 1024);
|
|
982
|
+
if (sizeKB > 1024) {
|
|
983
|
+
parts.push(`${Math.round(sizeKB / 1024)}MB`);
|
|
984
|
+
} else {
|
|
985
|
+
parts.push(`${sizeKB}KB`);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (source && source !== "upload") {
|
|
989
|
+
parts.push(`from ${source}`);
|
|
990
|
+
}
|
|
991
|
+
return parts.join(" - ");
|
|
992
|
+
}).join(`
|
|
993
|
+
`);
|
|
994
|
+
const documentsText = import_core2.addHeader("# Available Documents", `${documents.length} document(s) in knowledge base:
|
|
995
|
+
${documentsList}`);
|
|
996
|
+
return {
|
|
997
|
+
data: {
|
|
998
|
+
documents: documents.map((doc) => ({
|
|
999
|
+
id: doc.id,
|
|
1000
|
+
filename: doc.metadata?.filename || doc.metadata?.title,
|
|
1001
|
+
fileType: doc.metadata?.fileType || doc.metadata?.fileExt,
|
|
1002
|
+
source: doc.metadata?.source
|
|
1003
|
+
})),
|
|
1004
|
+
count: documents.length
|
|
1005
|
+
},
|
|
1006
|
+
values: {
|
|
1007
|
+
documentsCount: documents.length,
|
|
1008
|
+
documents: documentsList,
|
|
1009
|
+
availableDocuments: documentsText
|
|
1010
|
+
},
|
|
1011
|
+
text: documentsText
|
|
1012
|
+
};
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
import_core2.logger.error("Error in documents provider:", error instanceof Error ? error.message : String(error));
|
|
1015
|
+
return {
|
|
1016
|
+
data: { documents: [], error: error instanceof Error ? error.message : String(error) },
|
|
1017
|
+
values: {
|
|
1018
|
+
documentsCount: 0,
|
|
1019
|
+
documents: "",
|
|
1020
|
+
availableDocuments: ""
|
|
1021
|
+
},
|
|
1022
|
+
text: ""
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
// provider.ts
|
|
1028
|
+
var import_core3 = require("@elizaos/core");
|
|
1029
|
+
var knowledgeProvider = {
|
|
1030
|
+
name: "KNOWLEDGE",
|
|
1031
|
+
description: "Knowledge from the knowledge base that the agent knows, retrieved whenever the agent needs to answer a question about their expertise.",
|
|
1032
|
+
dynamic: true,
|
|
1033
|
+
get: async (runtime, message) => {
|
|
1034
|
+
const knowledgeService = runtime.getService("knowledge");
|
|
1035
|
+
const knowledgeData = await knowledgeService?.getKnowledge(message);
|
|
1036
|
+
if (!knowledgeData || knowledgeData.length === 0) {
|
|
1037
|
+
return {
|
|
1038
|
+
text: "",
|
|
1039
|
+
values: { knowledge: "", knowledgeUsed: false },
|
|
1040
|
+
data: { knowledge: "", ragMetadata: null, knowledgeUsed: false }
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
const firstFiveKnowledgeItems = knowledgeData.slice(0, 5);
|
|
1044
|
+
let knowledge = import_core3.addHeader("# Knowledge", firstFiveKnowledgeItems.map((item) => `- ${item.content.text}`).join(`
|
|
1045
|
+
`));
|
|
1046
|
+
const tokenLength = 3.5;
|
|
1047
|
+
const maxChars = 4000 * tokenLength;
|
|
1048
|
+
if (knowledge.length > maxChars) {
|
|
1049
|
+
knowledge = knowledge.slice(0, maxChars);
|
|
1050
|
+
}
|
|
1051
|
+
const ragMetadata = {
|
|
1052
|
+
retrievedFragments: knowledgeData.map((fragment) => {
|
|
1053
|
+
const fragmentMetadata = fragment.metadata;
|
|
1054
|
+
return {
|
|
1055
|
+
fragmentId: fragment.id,
|
|
1056
|
+
documentTitle: fragmentMetadata?.filename || fragmentMetadata?.title || "",
|
|
1057
|
+
similarityScore: fragment.similarity,
|
|
1058
|
+
contentPreview: `${(fragment.content?.text || "").substring(0, 100)}...`
|
|
1059
|
+
};
|
|
1060
|
+
}),
|
|
1061
|
+
queryText: message.content?.text || "",
|
|
1062
|
+
totalFragments: knowledgeData.length,
|
|
1063
|
+
retrievalTimestamp: Date.now()
|
|
1064
|
+
};
|
|
1065
|
+
knowledgeService.setPendingRAGMetadata(ragMetadata);
|
|
1066
|
+
setTimeout(async () => {
|
|
1067
|
+
await knowledgeService.enrichRecentMemoriesWithPendingRAG();
|
|
1068
|
+
}, 2000);
|
|
1069
|
+
return {
|
|
1070
|
+
data: {
|
|
1071
|
+
knowledge,
|
|
1072
|
+
ragMetadata,
|
|
1073
|
+
knowledgeUsed: true
|
|
1074
|
+
},
|
|
1075
|
+
values: {
|
|
1076
|
+
knowledge,
|
|
1077
|
+
knowledgeUsed: true
|
|
1078
|
+
},
|
|
1079
|
+
text: knowledge,
|
|
1080
|
+
ragMetadata,
|
|
1081
|
+
knowledgeUsed: true
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
// types.ts
|
|
1086
|
+
var import_zod = __toESM(require("zod"));
|
|
1087
|
+
var ModelConfigSchema = import_zod.default.object({
|
|
1088
|
+
EMBEDDING_PROVIDER: import_zod.default.enum(["local", "openai", "google"]).optional(),
|
|
1089
|
+
TEXT_PROVIDER: import_zod.default.enum(["openai", "anthropic", "openrouter", "google"]).optional(),
|
|
1090
|
+
OPENAI_API_KEY: import_zod.default.string().optional(),
|
|
1091
|
+
ANTHROPIC_API_KEY: import_zod.default.string().optional(),
|
|
1092
|
+
OPENROUTER_API_KEY: import_zod.default.string().optional(),
|
|
1093
|
+
GOOGLE_API_KEY: import_zod.default.string().optional(),
|
|
1094
|
+
OPENAI_BASE_URL: import_zod.default.string().optional(),
|
|
1095
|
+
ANTHROPIC_BASE_URL: import_zod.default.string().optional(),
|
|
1096
|
+
OPENROUTER_BASE_URL: import_zod.default.string().optional(),
|
|
1097
|
+
GOOGLE_BASE_URL: import_zod.default.string().optional(),
|
|
1098
|
+
TEXT_EMBEDDING_MODEL: import_zod.default.string(),
|
|
1099
|
+
TEXT_MODEL: import_zod.default.string().optional(),
|
|
1100
|
+
MAX_INPUT_TOKENS: import_zod.default.string().or(import_zod.default.number()).transform((val) => typeof val === "string" ? parseInt(val, 10) : val),
|
|
1101
|
+
MAX_OUTPUT_TOKENS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 4096),
|
|
1102
|
+
EMBEDDING_DIMENSION: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 1536),
|
|
1103
|
+
LOAD_DOCS_ON_STARTUP: import_zod.default.boolean().default(false),
|
|
1104
|
+
CTX_KNOWLEDGE_ENABLED: import_zod.default.boolean().default(false),
|
|
1105
|
+
RATE_LIMIT_ENABLED: import_zod.default.boolean().default(true),
|
|
1106
|
+
MAX_CONCURRENT_REQUESTS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 150),
|
|
1107
|
+
REQUESTS_PER_MINUTE: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 300),
|
|
1108
|
+
TOKENS_PER_MINUTE: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 750000),
|
|
1109
|
+
BATCH_DELAY_MS: import_zod.default.string().or(import_zod.default.number()).optional().transform((val) => val ? typeof val === "string" ? parseInt(val, 10) : val : 100)
|
|
1110
|
+
});
|
|
1111
|
+
var KnowledgeServiceType = {
|
|
1112
|
+
KNOWLEDGE: "knowledge"
|
|
1113
|
+
};
|
|
1114
|
+
|
|
3736
1115
|
// index.ts
|
|
3737
1116
|
function createKnowledgePlugin(config = {}) {
|
|
3738
|
-
const { enableUI = true, enableRoutes = true, enableActions = true
|
|
3739
|
-
const plugin = {
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
1117
|
+
const { enableUI = true, enableRoutes = true, enableActions = true } = config;
|
|
1118
|
+
const plugin = import_core4.createKnowledgePlugin({
|
|
1119
|
+
enableActions,
|
|
1120
|
+
enableProviders: true
|
|
1121
|
+
});
|
|
1122
|
+
if (!enableActions) {
|
|
1123
|
+
plugin.actions = undefined;
|
|
1124
|
+
}
|
|
3745
1125
|
if (enableUI || enableRoutes) {
|
|
3746
1126
|
plugin.routes = knowledgeRoutes;
|
|
3747
1127
|
}
|
|
3748
|
-
if (enableActions) {
|
|
3749
|
-
plugin.actions = knowledgeActions;
|
|
3750
|
-
}
|
|
3751
|
-
if (enableTests) {}
|
|
3752
1128
|
return plugin;
|
|
3753
1129
|
}
|
|
3754
1130
|
var knowledgePluginCore = createKnowledgePlugin({
|
|
@@ -3767,8 +1143,8 @@ var knowledgePlugin = createKnowledgePlugin({
|
|
|
3767
1143
|
enableUI: true,
|
|
3768
1144
|
enableRoutes: true,
|
|
3769
1145
|
enableActions: true,
|
|
3770
|
-
enableTests:
|
|
1146
|
+
enableTests: false
|
|
3771
1147
|
});
|
|
3772
1148
|
|
|
3773
|
-
//# debugId=
|
|
1149
|
+
//# debugId=51A9920C1D88785B64756E2164756E21
|
|
3774
1150
|
//# sourceMappingURL=index.node.cjs.map
|