@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 +38 -0
- package/dist/index.js +103 -61
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
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) =>
|
|
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
|
-
|
|
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.
|
|
3361
|
-
const filePath = file.
|
|
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
|
-
|
|
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
|
|
3406
|
+
// Directly from express-fileupload file object
|
|
3376
3407
|
originalFilename,
|
|
3377
|
-
// Directly from
|
|
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
|
-
|
|
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.
|
|
3431
|
+
`[KNOWLEDGE UPLOAD HANDLER] Error processing file ${file.name}: ${fileError}`
|
|
3399
3432
|
);
|
|
3400
|
-
|
|
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
|
-
|
|
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
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
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:
|
|
3932
|
+
handler: handleKnowledgeUpload
|
|
3891
3933
|
},
|
|
3892
3934
|
{
|
|
3893
3935
|
type: "GET",
|