diff --git a/package.json b/package.json index 0e016a1..b771b51 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@elizaos/plugin-knowledge", "description": "Plugin for Knowledge", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "main": "dist/index.js", "module": "dist/index.js", diff --git a/src/actions.ts b/src/actions.ts index b34183c..156e228 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -197,7 +197,7 @@ export const processKnowledgeAction: Action = { await callback(response); } } catch (error) { - logger.error('Error in PROCESS_KNOWLEDGE action:', error); + logger.error({ error }, 'Error in PROCESS_KNOWLEDGE action'); const errorResponse: Content = { text: `I encountered an error while processing the knowledge: ${error instanceof Error ? error.message : 'Unknown error'}`, @@ -327,7 +327,7 @@ export const searchKnowledgeAction: Action = { await callback(response); } } catch (error) { - logger.error('Error in SEARCH_KNOWLEDGE action:', error); + logger.error({ error }, 'Error in SEARCH_KNOWLEDGE action'); const errorResponse: Content = { text: `I encountered an error while searching the knowledge base: ${error instanceof Error ? error.message : 'Unknown error'}`, diff --git a/src/docs-loader.ts b/src/docs-loader.ts index 3d12f96..450421f 100644 --- a/src/docs-loader.ts +++ b/src/docs-loader.ts @@ -10,7 +10,8 @@ import { isBinaryContentType } from './utils.ts'; */ export function getKnowledgePath(runtimePath?: string): string { // Priority: runtime setting > environment variable > default - const knowledgePath = runtimePath || process.env.KNOWLEDGE_PATH || path.join(process.cwd(), 'docs'); + const knowledgePath = + runtimePath || process.env.KNOWLEDGE_PATH || path.join(process.cwd(), 'docs'); const resolvedPath = path.resolve(knowledgePath); if (!fs.existsSync(resolvedPath)) { @@ -107,7 +108,7 @@ export async function loadDocsFromPath( logger.info(`✅ "${fileName}": ${result.fragmentCount} fragments created`); successful++; } catch (error) { - logger.error(`Failed to process file ${filePath}:`, error); + logger.error({ error }, `Failed to process file ${filePath}`); failed++; } } @@ -143,7 +144,7 @@ function getAllFiles(dirPath: string, files: string[] = []): string[] { } } } catch (error) { - logger.error(`Error reading directory ${dirPath}:`, error); + logger.error({ error }, `Error reading directory ${dirPath}`); } return files; diff --git a/src/llm.ts b/src/llm.ts index dd5626b..d6edd8a 100644 --- a/src/llm.ts +++ b/src/llm.ts @@ -33,7 +33,7 @@ export async function generateTextEmbedding( throw new Error(`Unsupported embedding provider: ${config.EMBEDDING_PROVIDER}`); } catch (error) { - logger.error(`[Document Processor] ${config.EMBEDDING_PROVIDER} embedding error:`, error); + logger.error({ error }, `[Document Processor] ${config.EMBEDDING_PROVIDER} embedding error`); throw error; } } @@ -81,7 +81,7 @@ export async function generateTextEmbeddingsBatch( index: globalIndex, }; } catch (error) { - logger.error(`[Document Processor] Embedding error for item ${globalIndex}:`, error); + logger.error({ error }, `[Document Processor] Embedding error for item ${globalIndex}`); return { embedding: null, success: false, @@ -245,7 +245,7 @@ export async function generateText( throw new Error(`Unsupported text provider: ${provider}`); } } catch (error) { - logger.error(`[Document Processor] ${provider} ${modelName} error:`, error); + logger.error({ error }, `[Document Processor] ${provider} ${modelName} error`); throw error; } } diff --git a/src/routes.ts b/src/routes.ts index 4daa26c..b4d48e1 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -74,7 +74,7 @@ const cleanupFile = (filePath: string) => { try { fs.unlinkSync(filePath); } catch (error) { - logger.error(`Error cleaning up file ${filePath}:`, error); + logger.error({ error }, `Error cleaning up file ${filePath}`); } } }; @@ -334,7 +334,7 @@ async function uploadKnowledgeHandler(req: any, res: any, runtime: IAgentRuntime sendSuccess(res, results); } } catch (error: any) { - logger.error('[Document Processor] ❌ Error processing knowledge:', error); + logger.error({ error }, '[Document Processor] ❌ Error processing knowledge'); if (hasUploadedFiles) { cleanupFiles(req.files as MulterFile[]); } @@ -411,7 +411,7 @@ async function getKnowledgeDocumentsHandler(req: any, res: any, runtime: IAgentR totalRequested: fileUrls ? fileUrls.length : 0, }); } catch (error: any) { - logger.error('[Document Processor] ❌ Error retrieving documents:', error); + logger.error({ error }, '[Document Processor] ❌ Error retrieving documents'); sendError(res, 500, 'RETRIEVAL_ERROR', 'Failed to retrieve documents', error.message); } } @@ -446,7 +446,7 @@ async function deleteKnowledgeDocumentHandler(req: any, res: any, runtime: IAgen logger.info(`[Document Processor] ✅ Successfully deleted document: ${typedKnowledgeId}`); sendSuccess(res, null, 204); } catch (error: any) { - logger.error(`[Document Processor] ❌ Error deleting document ${knowledgeId}:`, error); + logger.error({ error }, `[Document Processor] ❌ Error deleting document ${knowledgeId}`); sendError(res, 500, 'DELETE_ERROR', 'Failed to delete document', error.message); } } @@ -502,7 +502,7 @@ async function getKnowledgeByIdHandler(req: any, res: any, runtime: IAgentRuntim sendSuccess(res, { document: cleanDocument }); } catch (error: any) { - logger.error(`[Document Processor] ❌ Error retrieving document ${knowledgeId}:`, error); + logger.error({ error }, `[Document Processor] ❌ Error retrieving document ${knowledgeId}`); sendError(res, 500, 'RETRIEVAL_ERROR', 'Failed to retrieve document', error.message); } } @@ -560,7 +560,7 @@ async function knowledgePanelHandler(req: any, res: any, runtime: IAgentRuntime) } } } catch (manifestError) { - logger.error('[Document Processor] ❌ Error reading manifest:', manifestError); + logger.error({ error: manifestError }, '[Document Processor] ❌ Error reading manifest'); // Continue with default filenames if manifest can't be read } } @@ -600,7 +600,7 @@ async function knowledgePanelHandler(req: any, res: any, runtime: IAgentRuntime) res.end(html); } } catch (error: any) { - logger.error('[Document Processor] ❌ Error serving frontend:', error); + logger.error({ error }, '[Document Processor] ❌ Error serving frontend'); sendError(res, 500, 'FRONTEND_ERROR', 'Failed to load knowledge panel', error.message); } } @@ -647,7 +647,7 @@ async function frontendAssetHandler(req: any, res: any, runtime: IAgentRuntime) sendError(res, 404, 'NOT_FOUND', `Asset not found: ${req.url}`); } } catch (error: any) { - logger.error(`[Document Processor] ❌ Error serving asset ${req.url}:`, error); + logger.error({ error }, `[Document Processor] ❌ Error serving asset ${req.url}`); sendError(res, 500, 'ASSET_ERROR', `Failed to load asset ${req.url}`, error.message); } } @@ -722,7 +722,7 @@ async function getKnowledgeChunksHandler(req: any, res: any, runtime: IAgentRunt }, }); } catch (error: any) { - logger.error('[Document Processor] ❌ Error retrieving chunks:', error); + logger.error({ error }, '[Document Processor] ❌ Error retrieving chunks'); sendError(res, 500, 'RETRIEVAL_ERROR', 'Failed to retrieve knowledge chunks', error.message); } } @@ -838,7 +838,7 @@ async function searchKnowledgeHandler(req: any, res: any, runtime: IAgentRuntime count: enhancedResults.length, }); } catch (error: any) { - logger.error('[Document Processor] ❌ Error searching knowledge:', error); + logger.error({ error }, '[Document Processor] ❌ Error searching knowledge'); sendError(res, 500, 'SEARCH_ERROR', 'Failed to search knowledge', error.message); } } @@ -854,7 +854,7 @@ async function uploadKnowledgeWithMulter(req: any, res: any, runtime: IAgentRunt // Apply multer middleware manually uploadArray(req, res, (err: any) => { if (err) { - logger.error('[Document Processor] ❌ File upload error:', err); + logger.error({ error: err }, '[Document Processor] ❌ File upload error'); return sendError(res, 400, 'UPLOAD_ERROR', err.message); } // If multer succeeded, call the actual handler diff --git a/src/service.ts b/src/service.ts index 97678c5..1c5565a 100644 --- a/src/service.ts +++ b/src/service.ts @@ -60,17 +60,17 @@ export class KnowledgeService extends Service { try { // Use a small delay to ensure runtime is fully ready if needed, though constructor implies it should be. await new Promise((resolve) => setTimeout(resolve, 1000)); - + // Get the agent-specific knowledge path from runtime settings const knowledgePath = this.runtime.getSetting('KNOWLEDGE_PATH'); - + const result: LoadResult = await loadDocsFromPath( - this as any, + this as any, this.runtime.agentId, undefined, // worldId knowledgePath ); - + if (result.successful > 0) { logger.info( `KnowledgeService: Loaded ${result.successful} documents from docs folder on startup for agent ${this.runtime.agentId}` @@ -82,8 +82,8 @@ export class KnowledgeService extends Service { } } catch (error) { logger.error( - `KnowledgeService: Error loading documents on startup for agent ${this.runtime.agentId}:`, - error + { error }, + `KnowledgeService: Error loading documents on startup for agent ${this.runtime.agentId}` ); } } @@ -161,14 +161,14 @@ export class KnowledgeService extends Service { } } - logger.success('Model configuration validated successfully.'); - logger.success(`Knowledge Plugin initialized for agent: ${runtime.character.name}`); + logger.info('Model configuration validated successfully.'); + logger.info(`Knowledge Plugin initialized for agent: ${runtime.character.name}`); logger.info( 'Knowledge Plugin initialized. Frontend panel should be discoverable via its public route.' ); } catch (error) { - logger.error('Failed to initialize Knowledge plugin:', error); + logger.error({ error }, 'Failed to initialize Knowledge plugin'); throw error; } @@ -178,7 +178,7 @@ export class KnowledgeService extends Service { if (service.config.LOAD_DOCS_ON_STARTUP) { logger.info('LOAD_DOCS_ON_STARTUP is enabled. Loading documents from docs folder...'); service.loadInitialDocuments().catch((error) => { - logger.error('Error during initial document loading in KnowledgeService:', error); + logger.error({ error }, 'Error during initial document loading in KnowledgeService'); }); } else { logger.info('LOAD_DOCS_ON_STARTUP is disabled. Skipping automatic document loading.'); @@ -195,8 +195,8 @@ export class KnowledgeService extends Service { // Run in background, don't await here to prevent blocking startup await service.processCharacterKnowledge(stringKnowledge).catch((err) => { logger.error( - `KnowledgeService: Error processing character knowledge during startup: ${err.message}`, - err + { error: err }, + 'KnowledgeService: Error processing character knowledge during startup' ); }); } else { @@ -330,7 +330,8 @@ export class KnowledgeService extends Service { fileBuffer = Buffer.from(content, 'base64'); } catch (e: any) { logger.error( - `KnowledgeService: Failed to convert base64 to buffer for ${originalFilename}: ${e.message}` + { error: e }, + `KnowledgeService: Failed to convert base64 to buffer for ${originalFilename}` ); throw new Error(`Invalid base64 content for PDF file ${originalFilename}`); } @@ -342,7 +343,8 @@ export class KnowledgeService extends Service { fileBuffer = Buffer.from(content, 'base64'); } catch (e: any) { logger.error( - `KnowledgeService: Failed to convert base64 to buffer for ${originalFilename}: ${e.message}` + { error: e }, + `KnowledgeService: Failed to convert base64 to buffer for ${originalFilename}` ); throw new Error(`Invalid base64 content for binary file ${originalFilename}`); } @@ -373,9 +375,7 @@ export class KnowledgeService extends Service { extractedText = decodedText; documentContentToStore = decodedText; } catch (e) { - logger.error( - `Failed to decode base64 for ${originalFilename}: ${e instanceof Error ? e.message : String(e)}` - ); + logger.error({ error: e as any }, `Failed to decode base64 for ${originalFilename}`); // If it looked like base64 but failed to decode properly, this is an error throw new Error( `File ${originalFilename} appears to be corrupted or incorrectly encoded` @@ -452,8 +452,8 @@ export class KnowledgeService extends Service { }; } catch (error: any) { logger.error( - `KnowledgeService: Error processing document ${originalFilename}: ${error.message}`, - error.stack + { error, stack: error.stack }, + `KnowledgeService: Error processing document ${originalFilename}` ); throw error; } @@ -462,7 +462,7 @@ export class KnowledgeService extends Service { // --- Knowledge methods moved from AgentRuntime --- private async handleProcessingError(error: any, context: string) { - logger.error(`KnowledgeService: Error ${context}:`, error?.message || error || 'Unknown error'); + logger.error({ error }, `KnowledgeService: Error ${context}`); throw error; } @@ -797,8 +797,8 @@ export class KnowledgeService extends Service { fragmentsProcessed++; } catch (error) { logger.error( - `KnowledgeService: Error processing fragment ${fragment.id} for document ${item.id}:`, - error + { error }, + `KnowledgeService: Error processing fragment ${fragment.id} for document ${item.id}` ); } } @@ -816,10 +816,7 @@ export class KnowledgeService extends Service { // Store the fragment in the knowledge table await this.runtime.createMemory(fragment, 'knowledge'); } catch (error) { - logger.error( - `KnowledgeService: Error processing fragment ${fragment.id}:`, - error instanceof Error ? error.message : String(error) - ); + logger.error({ error }, `KnowledgeService: Error processing fragment ${fragment.id}`); throw error; } } diff --git a/src/tests.ts b/src/tests.ts index 162ef99..731f47f 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -555,7 +555,7 @@ export class KnowledgeTestSuite implements TestSuite { } // Verify service is registered - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); const retrievedService = runtime.getService(KnowledgeService.serviceType); if (retrievedService !== service) { @@ -635,7 +635,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should add knowledge successfully', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); const testDocument = { clientDocumentId: uuidv4() as UUID, @@ -675,7 +675,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should handle duplicate document uploads', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); const testDocument = { clientDocumentId: uuidv4() as UUID, @@ -711,7 +711,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should retrieve knowledge based on query', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); // Add some test knowledge const testDocument = { @@ -762,7 +762,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should format knowledge in provider output', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set('knowledge' as any, service); + runtime.services.set('knowledge' as any, [service]); // Add test knowledge const testDocument = { @@ -885,7 +885,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should handle and log errors appropriately', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); // Clear previous mock calls mockLogger.clearCalls(); @@ -952,8 +952,8 @@ export class KnowledgeTestSuite implements TestSuite { // Start service const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); - runtime.services.set('knowledge' as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); + runtime.services.set('knowledge' as any, [service]); // Register provider runtime.registerProvider(knowledgeProvider); @@ -1029,7 +1029,7 @@ export class KnowledgeTestSuite implements TestSuite { name: 'Should handle large documents with chunking', fn: async (runtime: IAgentRuntime) => { const service = await KnowledgeService.start(runtime); - runtime.services.set(KnowledgeService.serviceType as any, service); + runtime.services.set(KnowledgeService.serviceType as any, [service]); // Create a large document const largeContent = Array(100)