UNPKG

mongodb-memory-bank-mcp

Version:

FIXED: MongoDB Memory Bank MCP with bulletproof error handling, smart operations, and session state management. Eliminates [object Object] errors and user confusion.

217 lines (216 loc) โ€ข 8.82 kB
import axios from 'axios'; import { mongoConfig } from '../../main/config/mongodb-config.js'; export class VoyageEmbeddingService { apiKey; apiUrl = 'https://api.voyageai.com/v1/embeddings'; model = 'voyage-3.5'; // โœ… UPDATED: Use current recommended model constructor(apiKey = mongoConfig.voyageApiKey || '') { this.apiKey = apiKey; // โœ… ENHANCED: Comprehensive debugging console.log('๐Ÿ” VoyageEmbeddingService Debug:', { apiKeyPresent: !!this.apiKey, apiKeyLength: this.apiKey?.length || 0, apiKeyPrefix: this.apiKey ? this.apiKey.substring(0, 8) + '...' : 'MISSING', enableVectorSearch: mongoConfig.enableVectorSearch, isAtlas: mongoConfig.isAtlas, isAvailable: this.isAvailable(), mongoConfigKeys: Object.keys(mongoConfig) }); if (!this.apiKey && mongoConfig.enableVectorSearch) { console.warn('โš ๏ธ Voyage AI API key not provided. Vector search will be disabled.'); } } async generateEmbedding(text) { if (!this.apiKey || !mongoConfig.enableVectorSearch) { console.log('โŒ Embedding generation skipped:', { hasApiKey: !!this.apiKey, apiKeyLength: this.apiKey?.length || 0, enableVectorSearch: mongoConfig.enableVectorSearch, isAtlas: mongoConfig.isAtlas }); return null; } try { console.log('๐Ÿš€ Generating embedding with Voyage AI...', { textLength: text.length, model: this.model, apiUrl: this.apiUrl, apiKeyPrefix: this.apiKey.substring(0, 12) + '...', headers: { 'Authorization': `Bearer ${this.apiKey.substring(0, 12)}...`, 'Content-Type': 'application/json' } }); const requestPayload = { input: [text], model: this.model, // voyage-3.5 input_type: 'document' // โœ… CORRECT: For storing documents }; console.log('๐Ÿ“ค Request payload:', { input: [`${text.substring(0, 100)}...`], model: requestPayload.model, input_type: requestPayload.input_type }); const response = await axios.post(this.apiUrl, requestPayload, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, timeout: 15000 // Increased to 15 second timeout }); console.log('๐Ÿ“ฅ Raw response status:', response.status); console.log('๐Ÿ“ฅ Raw response headers:', response.headers); const data = response.data; console.log('๐Ÿ“ฅ Response data structure:', { hasData: !!data, dataKeys: data ? Object.keys(data) : [], hasDataArray: !!(data && data.data), dataArrayLength: data?.data?.length || 0, hasUsage: !!(data && data.usage), fullResponse: JSON.stringify(data, null, 2) }); if (data && data.data && data.data.length > 0) { const embedding = data.data[0].embedding; const tokens = data.usage?.total_tokens || 0; console.log('โœ… Embedding generated successfully:', { dimensions: embedding?.length || 0, tokens: tokens, embeddingType: typeof embedding, isArray: Array.isArray(embedding), firstFewValues: embedding?.slice(0, 5) || [] }); return { embedding: embedding, tokens: tokens }; } console.log('โŒ No embedding data in response:', { responseStructure: data, hasData: !!data, hasDataArray: !!(data && data.data), dataArrayLength: data?.data?.length || 0 }); return null; } catch (error) { console.error('โŒ Failed to generate embedding - DETAILED ERROR:', { message: error.message, status: error.response?.status, statusText: error.response?.statusText, responseData: error.response?.data, responseHeaders: error.response?.headers, requestConfig: { url: error.config?.url, method: error.config?.method, headers: error.config?.headers }, isAxiosError: error.isAxiosError, code: error.code, stack: error.stack }); return null; } } async generateQueryEmbedding(query) { if (!this.apiKey || !mongoConfig.enableVectorSearch) { return null; } try { const response = await axios.post(this.apiUrl, { input: [query], model: this.model, // voyage-3.5 input_type: 'query' // โœ… CORRECT: For search queries }, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, timeout: 5000 // 5 second timeout for queries }); const data = response.data; if (data.data && data.data.length > 0) { return data.data[0].embedding; } return null; } catch (error) { console.error('Failed to generate query embedding:', error); return null; } } isAvailable() { const available = !!(this.apiKey && mongoConfig.enableVectorSearch && mongoConfig.isAtlas); console.log('๐Ÿ” VoyageEmbeddingService.isAvailable() - DETAILED CHECK:', { apiKey: !!this.apiKey, apiKeyLength: this.apiKey?.length || 0, apiKeyPrefix: this.apiKey ? this.apiKey.substring(0, 12) + '...' : 'MISSING', apiKeyStartsWith: this.apiKey ? this.apiKey.substring(0, 3) : 'N/A', enableVectorSearch: mongoConfig.enableVectorSearch, isAtlas: mongoConfig.isAtlas, result: available, environmentVariables: { VOYAGE_API_KEY: !!process.env.VOYAGE_API_KEY, ENABLE_VECTOR_SEARCH: process.env.ENABLE_VECTOR_SEARCH, MONGODB_ATLAS: process.env.MONGODB_ATLAS }, mongoConfigValues: { voyageApiKey: !!mongoConfig.voyageApiKey, enableVectorSearch: mongoConfig.enableVectorSearch, isAtlas: mongoConfig.isAtlas } }); return available; } /** * Test API key validity with a simple API call */ async testApiConnection() { if (!this.apiKey) { return { success: false, error: 'No API key provided' }; } try { console.log('๐Ÿงช Testing Voyage AI API connection...'); const response = await axios.post(this.apiUrl, { input: ['test'], model: this.model, input_type: 'document' }, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, timeout: 10000 }); console.log('โœ… API connection test successful:', { status: response.status, hasData: !!response.data?.data, dataLength: response.data?.data?.length || 0 }); return { success: true, details: { status: response.status, model: response.data?.model, usage: response.data?.usage } }; } catch (error) { console.error('โŒ API connection test failed:', { message: error.message, status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data }); return { success: false, error: error.message, details: { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data } }; } } }