@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/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-BB6B27BS.js";
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.config = {
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.config
1183
+ this.knowledgeConfig
1181
1184
  );
1182
- if (this.config.LOAD_DOCS_ON_STARTUP) {
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 (agent: ${agentId}) processing document for public addKnowledge: ${options.originalFilename}, type: ${options.contentType}`
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
- const base64Regex = /^[A-Za-z0-9+/]+=*$/;
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, createUniqueUuid as createUniqueUuid2 } from "@elizaos/core";
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: createUniqueUuid2(runtime.agentId + fileName + Date.now(), fileName),
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: createUniqueUuid2(runtime.agentId + "text" + Date.now(), "user-knowledge"),
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 { MemoryType as MemoryType4, createUniqueUuid as createUniqueUuid3, logger as logger5 } from "@elizaos/core";
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 isMultipartRequest = req.files && Object.keys(req.files).length > 0;
2931
- const isJsonRequest = !isMultipartRequest && req.body && (req.body.fileUrl || req.body.fileUrls);
2932
- if (!isMultipartRequest && !isJsonRequest) {
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 (isMultipartRequest) {
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 || createUniqueUuid3(runtime, `knowledge-${originalFilename}-${Date.now()}`);
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: runtime.agentId,
2981
- // Or a more specific room ID if available
2982
- entityId: runtime.agentId
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 = createUniqueUuid3(runtime, normalizedUrl);
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: runtime.agentId,
3052
- roomId: runtime.agentId,
3053
- entityId: runtime.agentId,
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(`[KNOWLEDGE URL HANDLER] Processing knowledge from URL: ${fileUrl} (type: ${contentType})`);
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 (isMultipartRequest) {
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((url) => createUniqueUuid3(runtime, url));
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(`[KNOWLEDGE GET HANDLER] Filtered documents by URLs: ${fileUrls.length} URLs, found ${filteredMemories.length} matching documents`);
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(`[KNOWLEDGE DELETE HANDLER] Attempting to delete document with ID: ${typedKnowledgeId}`);
3215
+ logger5.debug(
3216
+ `[KNOWLEDGE DELETE HANDLER] Attempting to delete document with ID: ${typedKnowledgeId}`
3217
+ );
3158
3218
  await service.deleteMemory(typedKnowledgeId);
3159
- logger5.info(`[KNOWLEDGE DELETE HANDLER] Successfully deleted document with ID: ${typedKnowledgeId}`);
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/agents/${agentId}/plugins/knowledge'
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/agents/${agentId}/plugins/knowledge'
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: uploadKnowledgeHandler,
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-NAE6ASGY.js");
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`);