@elizaos/plugin-knowledge 1.0.7 → 1.0.8

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 CHANGED
@@ -232,6 +232,44 @@ const searchResults = await knowledgeService.searchKnowledge({
232
232
  - **Contextual mode = better understanding** (but slower processing)
233
233
  - **Batch document uploads** rather than one-by-one for better performance
234
234
 
235
+ ### File Upload Configuration
236
+
237
+ #### Automatic MIME Type Correction
238
+ The frontend automatically corrects MIME types for code and document files to ensure proper processing:
239
+
240
+ ```typescript
241
+ const getCorrectMimeType = (file: File): string => {
242
+ const filename = file.name.toLowerCase();
243
+ const ext = filename.split('.').pop() || '';
244
+
245
+ // Map common text file extensions to text/plain
246
+ const textExtensions = [
247
+ 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs',
248
+ 'py', 'pyw', 'pyi', 'java', 'c', 'cpp', 'cc', 'cxx', 'h', 'hpp',
249
+ 'cs', 'php', 'rb', 'go', 'rs', 'swift', 'kt', 'kts', 'scala',
250
+ // ... and many more
251
+ ];
252
+
253
+ if (textExtensions.includes(ext)) {
254
+ return 'text/plain';
255
+ } else if (['md', 'markdown'].includes(ext)) {
256
+ return 'text/markdown';
257
+ } else if (ext === 'json') {
258
+ return 'application/json';
259
+ }
260
+ // ... additional mappings
261
+
262
+ return file.type || 'application/octet-stream';
263
+ };
264
+ ```
265
+
266
+ #### Supported File Types
267
+
268
+ - **Documents**: PDF, DOC, DOCX
269
+ - **Code**: JS, TS, PY, JAVA, C, CPP, etc.
270
+ - **Text**: TXT, MD, CSV, JSON, XML, HTML
271
+ - **Configuration**: ENV, CFG, INI, YAML, YML
272
+
235
273
  </details>
236
274
 
237
275
  ## 📝 License
package/dist/index.js CHANGED
@@ -3270,41 +3270,6 @@ var knowledgeActions = [processKnowledgeAction, searchKnowledgeAction];
3270
3270
  import { createUniqueUuid as createUniqueUuid2, logger as logger6, ModelType as ModelType4 } from "@elizaos/core";
3271
3271
  import fs3 from "fs";
3272
3272
  import path3 from "path";
3273
- import multer from "multer";
3274
- var createUploadMiddleware = (runtime) => {
3275
- const uploadDir = runtime.getSetting("KNOWLEDGE_UPLOAD_DIR") || "/tmp/uploads/";
3276
- const maxFileSize = parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILE_SIZE") || "52428800");
3277
- const maxFiles = parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILES") || "10");
3278
- const allowedMimeTypes = runtime.getSetting("KNOWLEDGE_ALLOWED_MIME_TYPES")?.split(",") || [
3279
- "text/plain",
3280
- "text/markdown",
3281
- "application/pdf",
3282
- "application/msword",
3283
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
3284
- "text/html",
3285
- "application/json",
3286
- "application/xml",
3287
- "text/csv"
3288
- ];
3289
- return multer({
3290
- dest: uploadDir,
3291
- limits: {
3292
- fileSize: maxFileSize,
3293
- files: maxFiles
3294
- },
3295
- fileFilter: (req, file, cb) => {
3296
- if (allowedMimeTypes.includes(file.mimetype)) {
3297
- cb(null, true);
3298
- } else {
3299
- cb(
3300
- new Error(
3301
- `File type ${file.mimetype} not allowed. Allowed types: ${allowedMimeTypes.join(", ")}`
3302
- )
3303
- );
3304
- }
3305
- }
3306
- });
3307
- };
3308
3273
  function sendSuccess(res, data, status = 200) {
3309
3274
  res.writeHead(status, { "Content-Type": "application/json" });
3310
3275
  res.end(JSON.stringify({ success: true, data }));
@@ -3324,7 +3289,11 @@ var cleanupFile = (filePath) => {
3324
3289
  };
3325
3290
  var cleanupFiles = (files) => {
3326
3291
  if (files) {
3327
- files.forEach((file) => cleanupFile(file.path));
3292
+ files.forEach((file) => {
3293
+ if (file.tempFilePath) {
3294
+ cleanupFile(file.tempFilePath);
3295
+ }
3296
+ });
3328
3297
  }
3329
3298
  };
3330
3299
  async function uploadKnowledgeHandler(req, res, runtime) {
@@ -3332,20 +3301,56 @@ async function uploadKnowledgeHandler(req, res, runtime) {
3332
3301
  if (!service) {
3333
3302
  return sendError(res, 500, "SERVICE_NOT_FOUND", "KnowledgeService not found");
3334
3303
  }
3335
- const hasUploadedFiles = req.files && req.files.length > 0;
3304
+ const hasUploadedFiles = req.files && Object.keys(req.files).length > 0;
3336
3305
  const isJsonRequest = !hasUploadedFiles && req.body && (req.body.fileUrl || req.body.fileUrls);
3337
3306
  if (!hasUploadedFiles && !isJsonRequest) {
3338
3307
  return sendError(res, 400, "INVALID_REQUEST", "Request must contain either files or URLs");
3339
3308
  }
3340
3309
  try {
3341
3310
  if (hasUploadedFiles) {
3342
- const files = req.files;
3311
+ let files = [];
3312
+ if (req.files.files) {
3313
+ if (Array.isArray(req.files.files)) {
3314
+ files = req.files.files;
3315
+ } else {
3316
+ files = [req.files.files];
3317
+ }
3318
+ } else if (req.files.file) {
3319
+ files = [req.files.file];
3320
+ } else {
3321
+ files = Object.values(req.files).flat();
3322
+ }
3343
3323
  if (!files || files.length === 0) {
3344
3324
  return sendError(res, 400, "NO_FILES", "No files uploaded");
3345
3325
  }
3326
+ const invalidFiles = files.filter((file) => {
3327
+ if (file.truncated) {
3328
+ logger6.warn(`File ${file.name} was truncated during upload`);
3329
+ return true;
3330
+ }
3331
+ if (file.size === 0) {
3332
+ logger6.warn(`File ${file.name} is empty`);
3333
+ return true;
3334
+ }
3335
+ if (!file.name || file.name.trim() === "") {
3336
+ logger6.warn(`File has no name`);
3337
+ return true;
3338
+ }
3339
+ if (!file.data && !file.tempFilePath) {
3340
+ logger6.warn(`File ${file.name} has no data or temp file path`);
3341
+ return true;
3342
+ }
3343
+ return false;
3344
+ });
3345
+ if (invalidFiles.length > 0) {
3346
+ cleanupFiles(files);
3347
+ const invalidFileNames = invalidFiles.map((f) => f.name || "unnamed").join(", ");
3348
+ return sendError(res, 400, "INVALID_FILES", `Invalid or corrupted files: ${invalidFileNames}`);
3349
+ }
3346
3350
  const agentId = req.body.agentId || req.query.agentId;
3347
3351
  if (!agentId) {
3348
3352
  logger6.error("[KNOWLEDGE UPLOAD HANDLER] No agent ID provided in request");
3353
+ cleanupFiles(files);
3349
3354
  return sendError(
3350
3355
  res,
3351
3356
  400,
@@ -3357,24 +3362,50 @@ async function uploadKnowledgeHandler(req, res, runtime) {
3357
3362
  logger6.info(`[KNOWLEDGE UPLOAD HANDLER] Processing upload for agent: ${agentId}`);
3358
3363
  const processingPromises = files.map(async (file, index) => {
3359
3364
  let knowledgeId;
3360
- const originalFilename = file.originalname;
3361
- const filePath = file.path;
3365
+ const originalFilename = file.name;
3366
+ const filePath = file.tempFilePath;
3362
3367
  knowledgeId = req.body?.documentIds && req.body.documentIds[index] || req.body?.documentId || createUniqueUuid2(runtime, `knowledge-${originalFilename}-${Date.now()}`);
3363
3368
  logger6.debug(
3364
3369
  `[KNOWLEDGE UPLOAD HANDLER] File: ${originalFilename}, Agent ID: ${agentId}, World ID: ${worldId}, Knowledge ID: ${knowledgeId}`
3365
3370
  );
3366
3371
  try {
3367
- const fileBuffer = await fs3.promises.readFile(filePath);
3372
+ let fileBuffer;
3373
+ if (filePath && fs3.existsSync(filePath)) {
3374
+ try {
3375
+ const stats = await fs3.promises.stat(filePath);
3376
+ if (stats.size === 0) {
3377
+ throw new Error("Temporary file is empty");
3378
+ }
3379
+ fileBuffer = await fs3.promises.readFile(filePath);
3380
+ logger6.debug(`[KNOWLEDGE UPLOAD] Read ${fileBuffer.length} bytes from temp file: ${filePath}`);
3381
+ } catch (fsError) {
3382
+ throw new Error(`Failed to read temporary file: ${fsError.message}`);
3383
+ }
3384
+ } else if (file.data && Buffer.isBuffer(file.data)) {
3385
+ fileBuffer = file.data;
3386
+ logger6.debug(`[KNOWLEDGE UPLOAD] Using in-memory buffer of ${fileBuffer.length} bytes`);
3387
+ } else {
3388
+ throw new Error("No file data available - neither temp file nor buffer found");
3389
+ }
3390
+ if (!Buffer.isBuffer(fileBuffer) || fileBuffer.length === 0) {
3391
+ throw new Error("Invalid or empty file buffer");
3392
+ }
3393
+ if (fileBuffer.length !== file.size) {
3394
+ logger6.warn(`File size mismatch for ${originalFilename}: expected ${file.size}, got ${fileBuffer.length}`);
3395
+ }
3368
3396
  const base64Content = fileBuffer.toString("base64");
3397
+ if (!base64Content || base64Content.length === 0) {
3398
+ throw new Error("Failed to convert file to base64");
3399
+ }
3369
3400
  const addKnowledgeOpts = {
3370
3401
  agentId,
3371
3402
  // Pass the agent ID from frontend
3372
3403
  clientDocumentId: knowledgeId,
3373
3404
  // This is knowledgeItem.id
3374
3405
  contentType: file.mimetype,
3375
- // Directly from multer file object
3406
+ // Directly from express-fileupload file object
3376
3407
  originalFilename,
3377
- // Directly from multer file object
3408
+ // Directly from express-fileupload file object
3378
3409
  content: base64Content,
3379
3410
  // The base64 string of the file
3380
3411
  worldId,
@@ -3384,7 +3415,9 @@ async function uploadKnowledgeHandler(req, res, runtime) {
3384
3415
  // Use the correct agent ID
3385
3416
  };
3386
3417
  await service.addKnowledge(addKnowledgeOpts);
3387
- cleanupFile(filePath);
3418
+ if (filePath) {
3419
+ cleanupFile(filePath);
3420
+ }
3388
3421
  return {
3389
3422
  id: knowledgeId,
3390
3423
  filename: originalFilename,
@@ -3395,9 +3428,11 @@ async function uploadKnowledgeHandler(req, res, runtime) {
3395
3428
  };
3396
3429
  } catch (fileError) {
3397
3430
  logger6.error(
3398
- `[KNOWLEDGE UPLOAD HANDLER] Error processing file ${file.originalname}: ${fileError}`
3431
+ `[KNOWLEDGE UPLOAD HANDLER] Error processing file ${file.name}: ${fileError}`
3399
3432
  );
3400
- cleanupFile(filePath);
3433
+ if (filePath) {
3434
+ cleanupFile(filePath);
3435
+ }
3401
3436
  return {
3402
3437
  id: knowledgeId,
3403
3438
  filename: originalFilename,
@@ -3498,8 +3533,9 @@ async function uploadKnowledgeHandler(req, res, runtime) {
3498
3533
  }
3499
3534
  } catch (error) {
3500
3535
  logger6.error("[KNOWLEDGE HANDLER] Error processing knowledge:", error);
3501
- if (hasUploadedFiles) {
3502
- cleanupFiles(req.files);
3536
+ if (hasUploadedFiles && req.files) {
3537
+ const allFiles = Object.values(req.files).flat();
3538
+ cleanupFiles(allFiles);
3503
3539
  }
3504
3540
  sendError(res, 500, "PROCESSING_ERROR", "Failed to process knowledge", error.message);
3505
3541
  }
@@ -3857,19 +3893,25 @@ async function searchKnowledgeHandler(req, res, runtime) {
3857
3893
  sendError(res, 500, "SEARCH_ERROR", "Failed to search knowledge", error.message);
3858
3894
  }
3859
3895
  }
3860
- async function uploadKnowledgeWithMulter(req, res, runtime) {
3861
- const upload = createUploadMiddleware(runtime);
3862
- const uploadArray = upload.array(
3863
- "files",
3864
- parseInt(runtime.getSetting("KNOWLEDGE_MAX_FILES") || "10")
3865
- );
3866
- uploadArray(req, res, (err) => {
3867
- if (err) {
3868
- logger6.error("[KNOWLEDGE UPLOAD] Multer error:", err);
3869
- return sendError(res, 400, "UPLOAD_ERROR", err.message);
3870
- }
3871
- uploadKnowledgeHandler(req, res, runtime);
3896
+ async function handleKnowledgeUpload(req, res, runtime) {
3897
+ logger6.debug("[KNOWLEDGE UPLOAD] Starting upload handler");
3898
+ logger6.debug("[KNOWLEDGE UPLOAD] Request details:", {
3899
+ method: req.method,
3900
+ url: req.url,
3901
+ contentType: req.headers["content-type"],
3902
+ contentLength: req.headers["content-length"],
3903
+ hasFiles: req.files ? Object.keys(req.files).length : 0,
3904
+ hasBody: req.body ? Object.keys(req.body).length : 0
3872
3905
  });
3906
+ try {
3907
+ logger6.debug("[KNOWLEDGE UPLOAD] Using files parsed by global middleware");
3908
+ await uploadKnowledgeHandler(req, res, runtime);
3909
+ } catch (handlerError) {
3910
+ logger6.error("[KNOWLEDGE UPLOAD] Handler error:", handlerError);
3911
+ if (!res.headersSent) {
3912
+ sendError(res, 500, "HANDLER_ERROR", "Failed to process upload");
3913
+ }
3914
+ }
3873
3915
  }
3874
3916
  var knowledgeRoutes = [
3875
3917
  {
@@ -3887,7 +3929,7 @@ var knowledgeRoutes = [
3887
3929
  {
3888
3930
  type: "POST",
3889
3931
  path: "/documents",
3890
- handler: uploadKnowledgeWithMulter
3932
+ handler: handleKnowledgeUpload
3891
3933
  },
3892
3934
  {
3893
3935
  type: "GET",