@elizaos/plugin-knowledge 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -334
- package/dist/.vite/manifest.json +2 -2
- package/dist/assets/index-DiVvUMt0.css +1 -0
- package/dist/assets/index-DlJcnv-R.js +160 -0
- package/dist/{chunk-BB6B27BS.js → chunk-536BD2UA.js} +9 -2
- package/dist/{chunk-BB6B27BS.js.map → chunk-536BD2UA.js.map} +1 -1
- package/dist/{docs-loader-NAE6ASGY.js → docs-loader-IBTEOAYT.js} +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.html +2 -2
- package/dist/index.js +230 -66
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/dist/assets/index-Bjc7mMWy.css +0 -1
- package/dist/assets/index-BwFQYb4z.js +0 -150
- /package/dist/{docs-loader-NAE6ASGY.js.map → docs-loader-IBTEOAYT.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
fetchUrlContent,
|
|
5
5
|
isBinaryContentType,
|
|
6
6
|
loadDocsFromPath,
|
|
7
|
+
looksLikeBase64,
|
|
7
8
|
normalizeS3Url
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-536BD2UA.js";
|
|
9
10
|
|
|
10
11
|
// src/index.ts
|
|
11
12
|
import { logger as logger6 } from "@elizaos/core";
|
|
@@ -1152,6 +1153,7 @@ function createRateLimiter(requestsPerMinute) {
|
|
|
1152
1153
|
var KnowledgeService = class _KnowledgeService extends Service {
|
|
1153
1154
|
static serviceType = "knowledge";
|
|
1154
1155
|
config;
|
|
1156
|
+
knowledgeConfig;
|
|
1155
1157
|
capabilityDescription = "Provides Retrieval Augmented Generation capabilities, including knowledge upload and querying.";
|
|
1156
1158
|
knowledgeProcessingSemaphore;
|
|
1157
1159
|
/**
|
|
@@ -1166,7 +1168,7 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1166
1168
|
if (typeof value === "string") return value.toLowerCase() === "true";
|
|
1167
1169
|
return false;
|
|
1168
1170
|
};
|
|
1169
|
-
this.
|
|
1171
|
+
this.knowledgeConfig = {
|
|
1170
1172
|
CTX_KNOWLEDGE_ENABLED: parseBooleanEnv(config?.CTX_KNOWLEDGE_ENABLED),
|
|
1171
1173
|
LOAD_DOCS_ON_STARTUP: parseBooleanEnv(config?.LOAD_DOCS_ON_STARTUP),
|
|
1172
1174
|
MAX_INPUT_TOKENS: config?.MAX_INPUT_TOKENS,
|
|
@@ -1175,11 +1177,12 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1175
1177
|
TEXT_PROVIDER: config?.TEXT_PROVIDER,
|
|
1176
1178
|
TEXT_EMBEDDING_MODEL: config?.TEXT_EMBEDDING_MODEL
|
|
1177
1179
|
};
|
|
1180
|
+
this.config = { ...this.knowledgeConfig };
|
|
1178
1181
|
logger3.info(
|
|
1179
1182
|
`KnowledgeService initialized for agent ${this.runtime.agentId} with config:`,
|
|
1180
|
-
this.
|
|
1183
|
+
this.knowledgeConfig
|
|
1181
1184
|
);
|
|
1182
|
-
if (this.
|
|
1185
|
+
if (this.knowledgeConfig.LOAD_DOCS_ON_STARTUP) {
|
|
1183
1186
|
this.loadInitialDocuments().catch((error) => {
|
|
1184
1187
|
logger3.error("Error during initial document loading in KnowledgeService:", error);
|
|
1185
1188
|
});
|
|
@@ -1246,6 +1249,9 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1246
1249
|
if (!service) {
|
|
1247
1250
|
logger3.warn(`KnowledgeService not found for agent ${runtime.agentId} during stop.`);
|
|
1248
1251
|
}
|
|
1252
|
+
if (service instanceof _KnowledgeService) {
|
|
1253
|
+
await service.stop();
|
|
1254
|
+
}
|
|
1249
1255
|
}
|
|
1250
1256
|
/**
|
|
1251
1257
|
* Stop the service
|
|
@@ -1259,9 +1265,9 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1259
1265
|
* @returns Promise with document processing result
|
|
1260
1266
|
*/
|
|
1261
1267
|
async addKnowledge(options) {
|
|
1262
|
-
const agentId = this.runtime.agentId;
|
|
1268
|
+
const agentId = options.agentId || this.runtime.agentId;
|
|
1263
1269
|
logger3.info(
|
|
1264
|
-
`KnowledgeService
|
|
1270
|
+
`KnowledgeService processing document for agent: ${agentId}, file: ${options.originalFilename}, type: ${options.contentType}`
|
|
1265
1271
|
);
|
|
1266
1272
|
try {
|
|
1267
1273
|
const existingDocument = await this.runtime.getMemoryById(options.clientDocumentId);
|
|
@@ -1297,6 +1303,7 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1297
1303
|
* @returns Promise with document processing result
|
|
1298
1304
|
*/
|
|
1299
1305
|
async processDocument({
|
|
1306
|
+
agentId: passedAgentId,
|
|
1300
1307
|
clientDocumentId,
|
|
1301
1308
|
contentType,
|
|
1302
1309
|
originalFilename,
|
|
@@ -1306,10 +1313,10 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1306
1313
|
entityId,
|
|
1307
1314
|
metadata
|
|
1308
1315
|
}) {
|
|
1309
|
-
const agentId = this.runtime.agentId;
|
|
1316
|
+
const agentId = passedAgentId || this.runtime.agentId;
|
|
1310
1317
|
try {
|
|
1311
1318
|
logger3.debug(
|
|
1312
|
-
`KnowledgeService: Processing document ${originalFilename} (type: ${contentType}) via processDocument`
|
|
1319
|
+
`KnowledgeService: Processing document ${originalFilename} (type: ${contentType}) via processDocument for agent: ${agentId}`
|
|
1313
1320
|
);
|
|
1314
1321
|
let fileBuffer = null;
|
|
1315
1322
|
let extractedText;
|
|
@@ -1338,9 +1345,7 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1338
1345
|
extractedText = await extractTextFromDocument(fileBuffer, contentType, originalFilename);
|
|
1339
1346
|
documentContentToStore = extractedText;
|
|
1340
1347
|
} else {
|
|
1341
|
-
|
|
1342
|
-
const looksLikeBase64 = content && content.length > 0 && base64Regex.test(content.replace(/\s/g, ""));
|
|
1343
|
-
if (looksLikeBase64) {
|
|
1348
|
+
if (looksLikeBase64(content)) {
|
|
1344
1349
|
try {
|
|
1345
1350
|
const decodedBuffer = Buffer.from(content, "base64");
|
|
1346
1351
|
const decodedText = decodedBuffer.toString("utf8");
|
|
@@ -1392,9 +1397,16 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1392
1397
|
...documentMemory,
|
|
1393
1398
|
id: clientDocumentId,
|
|
1394
1399
|
// Ensure the ID of the memory is the clientDocumentId
|
|
1400
|
+
agentId,
|
|
1395
1401
|
roomId: roomId || agentId,
|
|
1396
1402
|
entityId: entityId || agentId
|
|
1397
1403
|
};
|
|
1404
|
+
logger3.debug(
|
|
1405
|
+
`KnowledgeService: Creating memory with agentId=${agentId}, entityId=${entityId}, roomId=${roomId}, this.runtime.agentId=${this.runtime.agentId}`
|
|
1406
|
+
);
|
|
1407
|
+
logger3.debug(
|
|
1408
|
+
`KnowledgeService: memoryWithScope agentId=${memoryWithScope.agentId}, entityId=${memoryWithScope.entityId}`
|
|
1409
|
+
);
|
|
1398
1410
|
await this.runtime.createMemory(memoryWithScope, "documents");
|
|
1399
1411
|
logger3.debug(
|
|
1400
1412
|
`KnowledgeService: Stored document ${originalFilename} (Memory ID: ${memoryWithScope.id})`
|
|
@@ -1665,16 +1677,10 @@ var KnowledgeService = class _KnowledgeService extends Service {
|
|
|
1665
1677
|
* Corresponds to GET /plugins/knowledge/documents
|
|
1666
1678
|
*/
|
|
1667
1679
|
async getMemories(params) {
|
|
1668
|
-
if (params.tableName !== "documents") {
|
|
1669
|
-
logger3.warn(
|
|
1670
|
-
`KnowledgeService.getMemories called with tableName ${params.tableName}, but this service primarily manages 'documents'. Proceeding, but review usage.`
|
|
1671
|
-
);
|
|
1672
|
-
}
|
|
1673
1680
|
return this.runtime.getMemories({
|
|
1674
1681
|
...params,
|
|
1675
1682
|
// includes tableName, roomId, count, end
|
|
1676
1683
|
agentId: this.runtime.agentId
|
|
1677
|
-
// Ensure agentId is correctly scoped
|
|
1678
1684
|
});
|
|
1679
1685
|
}
|
|
1680
1686
|
/**
|
|
@@ -2628,7 +2634,7 @@ var KnowledgeTestSuite = class {
|
|
|
2628
2634
|
var tests_default = new KnowledgeTestSuite();
|
|
2629
2635
|
|
|
2630
2636
|
// src/actions.ts
|
|
2631
|
-
import { logger as logger4,
|
|
2637
|
+
import { logger as logger4, stringToUuid } from "@elizaos/core";
|
|
2632
2638
|
import * as fs2 from "fs";
|
|
2633
2639
|
import * as path2 from "path";
|
|
2634
2640
|
var processKnowledgeAction = {
|
|
@@ -2729,7 +2735,7 @@ var processKnowledgeAction = {
|
|
|
2729
2735
|
else if ([".txt", ".md", ".tson", ".xml", ".csv"].includes(fileExt))
|
|
2730
2736
|
contentType = "text/plain";
|
|
2731
2737
|
const knowledgeOptions = {
|
|
2732
|
-
clientDocumentId:
|
|
2738
|
+
clientDocumentId: stringToUuid(runtime.agentId + fileName + Date.now()),
|
|
2733
2739
|
contentType,
|
|
2734
2740
|
originalFilename: fileName,
|
|
2735
2741
|
worldId: runtime.agentId,
|
|
@@ -2756,7 +2762,7 @@ var processKnowledgeAction = {
|
|
|
2756
2762
|
return;
|
|
2757
2763
|
}
|
|
2758
2764
|
const knowledgeOptions = {
|
|
2759
|
-
clientDocumentId:
|
|
2765
|
+
clientDocumentId: stringToUuid(runtime.agentId + "text" + Date.now() + "user-knowledge"),
|
|
2760
2766
|
contentType: "text/plain",
|
|
2761
2767
|
originalFilename: "user-knowledge.txt",
|
|
2762
2768
|
worldId: runtime.agentId,
|
|
@@ -2897,9 +2903,44 @@ ${formattedResults}`
|
|
|
2897
2903
|
var knowledgeActions = [processKnowledgeAction, searchKnowledgeAction];
|
|
2898
2904
|
|
|
2899
2905
|
// src/routes.ts
|
|
2900
|
-
import {
|
|
2906
|
+
import { createUniqueUuid as createUniqueUuid2, logger as logger5, ModelType as ModelType4 } from "@elizaos/core";
|
|
2901
2907
|
import fs3 from "fs";
|
|
2902
2908
|
import path3 from "path";
|
|
2909
|
+
import multer from "multer";
|
|
2910
|
+
var createUploadMiddleware = (runtime) => {
|
|
2911
|
+
const uploadDir = runtime.getSetting("KNOWLEDGE_UPLOAD_DIR") || "/tmp/uploads/";
|
|
2912
|
+
const maxFileSize = parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILE_SIZE") || "52428800");
|
|
2913
|
+
const maxFiles = parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILES") || "10");
|
|
2914
|
+
const allowedMimeTypes = runtime.getSetting("KNOWLEDGE_ALLOWED_MIME_TYPES")?.split(",") || [
|
|
2915
|
+
"text/plain",
|
|
2916
|
+
"text/markdown",
|
|
2917
|
+
"application/pdf",
|
|
2918
|
+
"application/msword",
|
|
2919
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
2920
|
+
"text/html",
|
|
2921
|
+
"application/json",
|
|
2922
|
+
"application/xml",
|
|
2923
|
+
"text/csv"
|
|
2924
|
+
];
|
|
2925
|
+
return multer({
|
|
2926
|
+
dest: uploadDir,
|
|
2927
|
+
limits: {
|
|
2928
|
+
fileSize: maxFileSize,
|
|
2929
|
+
files: maxFiles
|
|
2930
|
+
},
|
|
2931
|
+
fileFilter: (req, file, cb) => {
|
|
2932
|
+
if (allowedMimeTypes.includes(file.mimetype)) {
|
|
2933
|
+
cb(null, true);
|
|
2934
|
+
} else {
|
|
2935
|
+
cb(
|
|
2936
|
+
new Error(
|
|
2937
|
+
`File type ${file.mimetype} not allowed. Allowed types: ${allowedMimeTypes.join(", ")}`
|
|
2938
|
+
)
|
|
2939
|
+
);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2943
|
+
};
|
|
2903
2944
|
function sendSuccess(res, data, status = 200) {
|
|
2904
2945
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
2905
2946
|
res.end(JSON.stringify({ success: true, data }));
|
|
@@ -2927,47 +2968,43 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
2927
2968
|
if (!service) {
|
|
2928
2969
|
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
2929
2970
|
}
|
|
2930
|
-
const
|
|
2931
|
-
const isJsonRequest = !
|
|
2932
|
-
if (!
|
|
2971
|
+
const hasUploadedFiles = req.files && req.files.length > 0;
|
|
2972
|
+
const isJsonRequest = !hasUploadedFiles && req.body && (req.body.fileUrl || req.body.fileUrls);
|
|
2973
|
+
if (!hasUploadedFiles && !isJsonRequest) {
|
|
2933
2974
|
return sendError(res, 400, "INVALID_REQUEST", "Request must contain either files or URLs");
|
|
2934
2975
|
}
|
|
2935
2976
|
try {
|
|
2936
|
-
if (
|
|
2977
|
+
if (hasUploadedFiles) {
|
|
2937
2978
|
const files = req.files;
|
|
2938
2979
|
if (!files || files.length === 0) {
|
|
2939
2980
|
return sendError(res, 400, "NO_FILES", "No files uploaded");
|
|
2940
2981
|
}
|
|
2982
|
+
const agentId = req.body.agentId || req.query.agentId;
|
|
2983
|
+
if (!agentId) {
|
|
2984
|
+
logger5.error("[KNOWLEDGE UPLOAD HANDLER] No agent ID provided in request");
|
|
2985
|
+
return sendError(
|
|
2986
|
+
res,
|
|
2987
|
+
400,
|
|
2988
|
+
"MISSING_AGENT_ID",
|
|
2989
|
+
"Agent ID is required for uploading knowledge"
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
const worldId = req.body.worldId || agentId;
|
|
2993
|
+
logger5.info(`[KNOWLEDGE UPLOAD HANDLER] Processing upload for agent: ${agentId}`);
|
|
2941
2994
|
const processingPromises = files.map(async (file, index) => {
|
|
2942
2995
|
let knowledgeId;
|
|
2943
2996
|
const originalFilename = file.originalname;
|
|
2944
|
-
const worldId = req.body.worldId || runtime.agentId;
|
|
2945
2997
|
const filePath = file.path;
|
|
2946
|
-
knowledgeId = req.body?.documentIds && req.body.documentIds[index] || req.body?.documentId ||
|
|
2998
|
+
knowledgeId = req.body?.documentIds && req.body.documentIds[index] || req.body?.documentId || createUniqueUuid2(runtime, `knowledge-${originalFilename}-${Date.now()}`);
|
|
2999
|
+
logger5.debug(
|
|
3000
|
+
`[KNOWLEDGE UPLOAD HANDLER] File: ${originalFilename}, Agent ID: ${agentId}, World ID: ${worldId}, Knowledge ID: ${knowledgeId}`
|
|
3001
|
+
);
|
|
2947
3002
|
try {
|
|
2948
3003
|
const fileBuffer = await fs3.promises.readFile(filePath);
|
|
2949
|
-
const fileExt = file.originalname.split(".").pop()?.toLowerCase() || "";
|
|
2950
|
-
const filename = file.originalname;
|
|
2951
|
-
const title = filename.replace(`.${fileExt}`, "");
|
|
2952
3004
|
const base64Content = fileBuffer.toString("base64");
|
|
2953
|
-
const knowledgeItem = {
|
|
2954
|
-
id: knowledgeId,
|
|
2955
|
-
content: {
|
|
2956
|
-
text: base64Content
|
|
2957
|
-
},
|
|
2958
|
-
metadata: {
|
|
2959
|
-
type: MemoryType4.DOCUMENT,
|
|
2960
|
-
timestamp: Date.now(),
|
|
2961
|
-
source: "upload",
|
|
2962
|
-
filename,
|
|
2963
|
-
fileExt,
|
|
2964
|
-
title,
|
|
2965
|
-
path: originalFilename,
|
|
2966
|
-
fileType: file.mimetype,
|
|
2967
|
-
fileSize: file.size
|
|
2968
|
-
}
|
|
2969
|
-
};
|
|
2970
3005
|
const addKnowledgeOpts = {
|
|
3006
|
+
agentId,
|
|
3007
|
+
// Pass the agent ID from frontend
|
|
2971
3008
|
clientDocumentId: knowledgeId,
|
|
2972
3009
|
// This is knowledgeItem.id
|
|
2973
3010
|
contentType: file.mimetype,
|
|
@@ -2977,9 +3014,10 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
2977
3014
|
content: base64Content,
|
|
2978
3015
|
// The base64 string of the file
|
|
2979
3016
|
worldId,
|
|
2980
|
-
roomId:
|
|
2981
|
-
//
|
|
2982
|
-
entityId:
|
|
3017
|
+
roomId: agentId,
|
|
3018
|
+
// Use the correct agent ID
|
|
3019
|
+
entityId: agentId
|
|
3020
|
+
// Use the correct agent ID
|
|
2983
3021
|
};
|
|
2984
3022
|
await service.addKnowledge(addKnowledgeOpts);
|
|
2985
3023
|
cleanupFile(filePath);
|
|
@@ -3011,10 +3049,21 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3011
3049
|
if (fileUrls.length === 0) {
|
|
3012
3050
|
return sendError(res, 400, "MISSING_URL", "File URL is required");
|
|
3013
3051
|
}
|
|
3052
|
+
const agentId = req.body.agentId || req.query.agentId;
|
|
3053
|
+
if (!agentId) {
|
|
3054
|
+
logger5.error("[KNOWLEDGE URL HANDLER] No agent ID provided in request");
|
|
3055
|
+
return sendError(
|
|
3056
|
+
res,
|
|
3057
|
+
400,
|
|
3058
|
+
"MISSING_AGENT_ID",
|
|
3059
|
+
"Agent ID is required for uploading knowledge from URLs"
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
logger5.info(`[KNOWLEDGE URL HANDLER] Processing URL upload for agent: ${agentId}`);
|
|
3014
3063
|
const processingPromises = fileUrls.map(async (fileUrl) => {
|
|
3015
3064
|
try {
|
|
3016
3065
|
const normalizedUrl = normalizeS3Url(fileUrl);
|
|
3017
|
-
const knowledgeId =
|
|
3066
|
+
const knowledgeId = createUniqueUuid2(runtime, normalizedUrl);
|
|
3018
3067
|
const urlObject = new URL(fileUrl);
|
|
3019
3068
|
const pathSegments = urlObject.pathname.split("/");
|
|
3020
3069
|
const encodedFilename = pathSegments[pathSegments.length - 1] || "document.pdf";
|
|
@@ -3043,20 +3092,24 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3043
3092
|
}
|
|
3044
3093
|
}
|
|
3045
3094
|
const addKnowledgeOpts = {
|
|
3095
|
+
agentId,
|
|
3096
|
+
// Pass the agent ID from frontend
|
|
3046
3097
|
clientDocumentId: knowledgeId,
|
|
3047
3098
|
contentType,
|
|
3048
3099
|
originalFilename,
|
|
3049
3100
|
content,
|
|
3050
3101
|
// Use the base64 encoded content from the URL
|
|
3051
|
-
worldId:
|
|
3052
|
-
roomId:
|
|
3053
|
-
entityId:
|
|
3102
|
+
worldId: agentId,
|
|
3103
|
+
roomId: agentId,
|
|
3104
|
+
entityId: agentId,
|
|
3054
3105
|
// Store the normalized URL in metadata
|
|
3055
3106
|
metadata: {
|
|
3056
3107
|
url: normalizedUrl
|
|
3057
3108
|
}
|
|
3058
3109
|
};
|
|
3059
|
-
logger5.debug(
|
|
3110
|
+
logger5.debug(
|
|
3111
|
+
`[KNOWLEDGE URL HANDLER] Processing knowledge from URL: ${fileUrl} (type: ${contentType})`
|
|
3112
|
+
);
|
|
3060
3113
|
const result = await service.addKnowledge(addKnowledgeOpts);
|
|
3061
3114
|
return {
|
|
3062
3115
|
id: result.clientDocumentId,
|
|
@@ -3081,7 +3134,7 @@ async function uploadKnowledgeHandler(req, res, runtime) {
|
|
|
3081
3134
|
}
|
|
3082
3135
|
} catch (error) {
|
|
3083
3136
|
logger5.error("[KNOWLEDGE HANDLER] Error processing knowledge:", error);
|
|
3084
|
-
if (
|
|
3137
|
+
if (hasUploadedFiles) {
|
|
3085
3138
|
cleanupFiles(req.files);
|
|
3086
3139
|
}
|
|
3087
3140
|
sendError(res, 500, "PROCESSING_ERROR", "Failed to process knowledge", error.message);
|
|
@@ -3101,6 +3154,7 @@ async function getKnowledgeDocumentsHandler(req, res, runtime) {
|
|
|
3101
3154
|
const limit = req.query.limit ? Number.parseInt(req.query.limit, 10) : 20;
|
|
3102
3155
|
const before = req.query.before ? Number.parseInt(req.query.before, 10) : Date.now();
|
|
3103
3156
|
const includeEmbedding = req.query.includeEmbedding === "true";
|
|
3157
|
+
const agentId = req.query.agentId;
|
|
3104
3158
|
const fileUrls = req.query.fileUrls ? typeof req.query.fileUrls === "string" && req.query.fileUrls.includes(",") ? req.query.fileUrls.split(",") : [req.query.fileUrls] : null;
|
|
3105
3159
|
const memories = await service.getMemories({
|
|
3106
3160
|
tableName: "documents",
|
|
@@ -3110,13 +3164,17 @@ async function getKnowledgeDocumentsHandler(req, res, runtime) {
|
|
|
3110
3164
|
let filteredMemories = memories;
|
|
3111
3165
|
if (fileUrls && fileUrls.length > 0) {
|
|
3112
3166
|
const normalizedRequestUrls = fileUrls.map((url) => normalizeS3Url(url));
|
|
3113
|
-
const urlBasedIds = normalizedRequestUrls.map(
|
|
3167
|
+
const urlBasedIds = normalizedRequestUrls.map(
|
|
3168
|
+
(url) => createUniqueUuid2(runtime, url)
|
|
3169
|
+
);
|
|
3114
3170
|
filteredMemories = memories.filter(
|
|
3115
3171
|
(memory) => urlBasedIds.includes(memory.id) || // If the ID corresponds directly
|
|
3116
3172
|
// Or if the URL is stored in the metadata (check if it exists)
|
|
3117
3173
|
memory.metadata && "url" in memory.metadata && typeof memory.metadata.url === "string" && normalizedRequestUrls.includes(normalizeS3Url(memory.metadata.url))
|
|
3118
3174
|
);
|
|
3119
|
-
logger5.debug(
|
|
3175
|
+
logger5.debug(
|
|
3176
|
+
`[KNOWLEDGE GET HANDLER] Filtered documents by URLs: ${fileUrls.length} URLs, found ${filteredMemories.length} matching documents`
|
|
3177
|
+
);
|
|
3120
3178
|
}
|
|
3121
3179
|
const cleanMemories = includeEmbedding ? filteredMemories : filteredMemories.map((memory) => ({
|
|
3122
3180
|
...memory,
|
|
@@ -3154,9 +3212,13 @@ async function deleteKnowledgeDocumentHandler(req, res, runtime) {
|
|
|
3154
3212
|
}
|
|
3155
3213
|
try {
|
|
3156
3214
|
const typedKnowledgeId = knowledgeId;
|
|
3157
|
-
logger5.debug(
|
|
3215
|
+
logger5.debug(
|
|
3216
|
+
`[KNOWLEDGE DELETE HANDLER] Attempting to delete document with ID: ${typedKnowledgeId}`
|
|
3217
|
+
);
|
|
3158
3218
|
await service.deleteMemory(typedKnowledgeId);
|
|
3159
|
-
logger5.info(
|
|
3219
|
+
logger5.info(
|
|
3220
|
+
`[KNOWLEDGE DELETE HANDLER] Successfully deleted document with ID: ${typedKnowledgeId}`
|
|
3221
|
+
);
|
|
3160
3222
|
sendSuccess(res, null, 204);
|
|
3161
3223
|
} catch (error) {
|
|
3162
3224
|
logger5.error(`[KNOWLEDGE DELETE HANDLER] Error deleting document ${knowledgeId}:`, error);
|
|
@@ -3184,6 +3246,7 @@ async function getKnowledgeByIdHandler(req, res, runtime) {
|
|
|
3184
3246
|
}
|
|
3185
3247
|
try {
|
|
3186
3248
|
logger5.debug(`[KNOWLEDGE GET BY ID HANDLER] Retrieving document with ID: ${knowledgeId}`);
|
|
3249
|
+
const agentId = req.query.agentId;
|
|
3187
3250
|
const memories = await service.getMemories({
|
|
3188
3251
|
tableName: "documents",
|
|
3189
3252
|
count: 1e3
|
|
@@ -3205,9 +3268,11 @@ async function getKnowledgeByIdHandler(req, res, runtime) {
|
|
|
3205
3268
|
}
|
|
3206
3269
|
async function knowledgePanelHandler(req, res, runtime) {
|
|
3207
3270
|
const agentId = runtime.agentId;
|
|
3271
|
+
logger5.debug(`[KNOWLEDGE PANEL] Serving panel for agent ${agentId}, request path: ${req.path}`);
|
|
3208
3272
|
try {
|
|
3209
3273
|
const currentDir = path3.dirname(new URL(import.meta.url).pathname);
|
|
3210
3274
|
const frontendPath = path3.join(currentDir, "../dist/index.html");
|
|
3275
|
+
logger5.debug(`[KNOWLEDGE PANEL] Looking for frontend at: ${frontendPath}`);
|
|
3211
3276
|
if (fs3.existsSync(frontendPath)) {
|
|
3212
3277
|
const html = await fs3.promises.readFile(frontendPath, "utf8");
|
|
3213
3278
|
const injectedHtml = html.replace(
|
|
@@ -3216,7 +3281,7 @@ async function knowledgePanelHandler(req, res, runtime) {
|
|
|
3216
3281
|
<script>
|
|
3217
3282
|
window.ELIZA_CONFIG = {
|
|
3218
3283
|
agentId: '${agentId}',
|
|
3219
|
-
apiBase: '/api
|
|
3284
|
+
apiBase: '/api'
|
|
3220
3285
|
};
|
|
3221
3286
|
</script>`
|
|
3222
3287
|
);
|
|
@@ -3255,7 +3320,7 @@ async function knowledgePanelHandler(req, res, runtime) {
|
|
|
3255
3320
|
<script>
|
|
3256
3321
|
window.ELIZA_CONFIG = {
|
|
3257
3322
|
agentId: '${agentId}',
|
|
3258
|
-
apiBase: '/api
|
|
3323
|
+
apiBase: '/api'
|
|
3259
3324
|
};
|
|
3260
3325
|
</script>
|
|
3261
3326
|
<link rel="stylesheet" href="./assets/${cssFile}">
|
|
@@ -3332,9 +3397,9 @@ async function getKnowledgeChunksHandler(req, res, runtime) {
|
|
|
3332
3397
|
const limit = req.query.limit ? Number.parseInt(req.query.limit, 10) : 100;
|
|
3333
3398
|
const before = req.query.before ? Number.parseInt(req.query.before, 10) : Date.now();
|
|
3334
3399
|
const documentId = req.query.documentId;
|
|
3400
|
+
const agentId = req.query.agentId;
|
|
3335
3401
|
const chunks = await service.getMemories({
|
|
3336
3402
|
tableName: "knowledge",
|
|
3337
|
-
// or whatever table stores the chunks
|
|
3338
3403
|
count: limit,
|
|
3339
3404
|
end: before
|
|
3340
3405
|
});
|
|
@@ -3347,6 +3412,101 @@ async function getKnowledgeChunksHandler(req, res, runtime) {
|
|
|
3347
3412
|
sendError(res, 500, "RETRIEVAL_ERROR", "Failed to retrieve knowledge chunks", error.message);
|
|
3348
3413
|
}
|
|
3349
3414
|
}
|
|
3415
|
+
async function searchKnowledgeHandler(req, res, runtime) {
|
|
3416
|
+
const service = runtime.getService(KnowledgeService.serviceType);
|
|
3417
|
+
if (!service) {
|
|
3418
|
+
return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
|
|
3419
|
+
}
|
|
3420
|
+
try {
|
|
3421
|
+
const searchText = req.query.q;
|
|
3422
|
+
const parsedThreshold = req.query.threshold ? Number.parseFloat(req.query.threshold) : NaN;
|
|
3423
|
+
let matchThreshold = Number.isNaN(parsedThreshold) ? 0.5 : parsedThreshold;
|
|
3424
|
+
matchThreshold = Math.max(0, Math.min(1, matchThreshold));
|
|
3425
|
+
const parsedLimit = req.query.limit ? Number.parseInt(req.query.limit, 10) : NaN;
|
|
3426
|
+
let limit = Number.isNaN(parsedLimit) ? 20 : parsedLimit;
|
|
3427
|
+
limit = Math.max(1, Math.min(100, limit));
|
|
3428
|
+
const agentId = req.query.agentId || runtime.agentId;
|
|
3429
|
+
if (!searchText || searchText.trim().length === 0) {
|
|
3430
|
+
return sendError(res, 400, "INVALID_QUERY", "Search query cannot be empty");
|
|
3431
|
+
}
|
|
3432
|
+
if (req.query.threshold && (parsedThreshold < 0 || parsedThreshold > 1)) {
|
|
3433
|
+
logger5.debug(
|
|
3434
|
+
`[KNOWLEDGE SEARCH] Threshold value ${parsedThreshold} was clamped to ${matchThreshold}`
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
if (req.query.limit && (parsedLimit < 1 || parsedLimit > 100)) {
|
|
3438
|
+
logger5.debug(`[KNOWLEDGE SEARCH] Limit value ${parsedLimit} was clamped to ${limit}`);
|
|
3439
|
+
}
|
|
3440
|
+
logger5.debug(
|
|
3441
|
+
`[KNOWLEDGE SEARCH] Searching for: "${searchText}" with threshold: ${matchThreshold}, limit: ${limit}`
|
|
3442
|
+
);
|
|
3443
|
+
const embedding = await runtime.useModel(ModelType4.TEXT_EMBEDDING, {
|
|
3444
|
+
text: searchText
|
|
3445
|
+
});
|
|
3446
|
+
const results = await runtime.searchMemories({
|
|
3447
|
+
tableName: "knowledge",
|
|
3448
|
+
embedding,
|
|
3449
|
+
query: searchText,
|
|
3450
|
+
count: limit,
|
|
3451
|
+
match_threshold: matchThreshold,
|
|
3452
|
+
roomId: agentId
|
|
3453
|
+
});
|
|
3454
|
+
const enhancedResults = await Promise.all(
|
|
3455
|
+
results.map(async (fragment) => {
|
|
3456
|
+
let documentTitle = "Unknown Document";
|
|
3457
|
+
let documentFilename = "unknown";
|
|
3458
|
+
if (fragment.metadata && typeof fragment.metadata === "object" && "documentId" in fragment.metadata) {
|
|
3459
|
+
const documentId = fragment.metadata.documentId;
|
|
3460
|
+
try {
|
|
3461
|
+
const document = await runtime.getMemoryById(documentId);
|
|
3462
|
+
if (document && document.metadata) {
|
|
3463
|
+
documentTitle = document.metadata.title || document.metadata.filename || documentTitle;
|
|
3464
|
+
documentFilename = document.metadata.filename || documentFilename;
|
|
3465
|
+
}
|
|
3466
|
+
} catch (e) {
|
|
3467
|
+
logger5.debug(`Could not fetch document ${documentId} for fragment`);
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
return {
|
|
3471
|
+
id: fragment.id,
|
|
3472
|
+
content: fragment.content,
|
|
3473
|
+
similarity: fragment.similarity || 0,
|
|
3474
|
+
metadata: {
|
|
3475
|
+
...fragment.metadata || {},
|
|
3476
|
+
documentTitle,
|
|
3477
|
+
documentFilename
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
})
|
|
3481
|
+
);
|
|
3482
|
+
logger5.info(
|
|
3483
|
+
`[KNOWLEDGE SEARCH] Found ${enhancedResults.length} results for query: "${searchText}"`
|
|
3484
|
+
);
|
|
3485
|
+
sendSuccess(res, {
|
|
3486
|
+
query: searchText,
|
|
3487
|
+
threshold: matchThreshold,
|
|
3488
|
+
results: enhancedResults,
|
|
3489
|
+
count: enhancedResults.length
|
|
3490
|
+
});
|
|
3491
|
+
} catch (error) {
|
|
3492
|
+
logger5.error("[KNOWLEDGE SEARCH] Error searching knowledge:", error);
|
|
3493
|
+
sendError(res, 500, "SEARCH_ERROR", "Failed to search knowledge", error.message);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
async function uploadKnowledgeWithMulter(req, res, runtime) {
|
|
3497
|
+
const upload = createUploadMiddleware(runtime);
|
|
3498
|
+
const uploadArray = upload.array(
|
|
3499
|
+
"files",
|
|
3500
|
+
parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILES") || "10")
|
|
3501
|
+
);
|
|
3502
|
+
uploadArray(req, res, (err) => {
|
|
3503
|
+
if (err) {
|
|
3504
|
+
logger5.error("[KNOWLEDGE UPLOAD] Multer error:", err);
|
|
3505
|
+
return sendError(res, 400, "UPLOAD_ERROR", err.message);
|
|
3506
|
+
}
|
|
3507
|
+
uploadKnowledgeHandler(req, res, runtime);
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3350
3510
|
var knowledgeRoutes = [
|
|
3351
3511
|
{
|
|
3352
3512
|
type: "GET",
|
|
@@ -3363,8 +3523,7 @@ var knowledgeRoutes = [
|
|
|
3363
3523
|
{
|
|
3364
3524
|
type: "POST",
|
|
3365
3525
|
path: "/documents",
|
|
3366
|
-
handler:
|
|
3367
|
-
isMultipart: true
|
|
3526
|
+
handler: uploadKnowledgeWithMulter
|
|
3368
3527
|
},
|
|
3369
3528
|
{
|
|
3370
3529
|
type: "GET",
|
|
@@ -3385,6 +3544,11 @@ var knowledgeRoutes = [
|
|
|
3385
3544
|
type: "GET",
|
|
3386
3545
|
path: "/knowledges",
|
|
3387
3546
|
handler: getKnowledgeChunksHandler
|
|
3547
|
+
},
|
|
3548
|
+
{
|
|
3549
|
+
type: "GET",
|
|
3550
|
+
path: "/search",
|
|
3551
|
+
handler: searchKnowledgeHandler
|
|
3388
3552
|
}
|
|
3389
3553
|
];
|
|
3390
3554
|
|
|
@@ -3433,7 +3597,7 @@ var knowledgePlugin = {
|
|
|
3433
3597
|
try {
|
|
3434
3598
|
const service = runtime.getService(KnowledgeService.serviceType);
|
|
3435
3599
|
if (service instanceof KnowledgeService) {
|
|
3436
|
-
const { loadDocsFromPath: loadDocsFromPath2 } = await import("./docs-loader-
|
|
3600
|
+
const { loadDocsFromPath: loadDocsFromPath2 } = await import("./docs-loader-IBTEOAYT.js");
|
|
3437
3601
|
const result = await loadDocsFromPath2(service, runtime.agentId);
|
|
3438
3602
|
if (result.successful > 0) {
|
|
3439
3603
|
logger6.info(`Loaded ${result.successful} documents from docs folder on startup`);
|