UNPKG

@alvinveroy/codecompass

Version:

AI-powered MCP server for codebase navigation and LLM prompt optimization

121 lines (120 loc) 7.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.initializeQdrant = initializeQdrant; exports.getQdrantClient = getQdrantClient; exports.batchUpsertVectors = batchUpsertVectors; const js_client_rest_1 = require("@qdrant/js-client-rest"); const config_service_1 = require("./config-service"); const retry_utils_1 = require("../utils/retry-utils"); let qdrantClientInstance = null; // Initialize Qdrant async function initializeQdrant() { if (qdrantClientInstance) { try { // Perform a lightweight operation to check if the client is still healthy await qdrantClientInstance.getCollections(); // Example: list collections config_service_1.logger.info("Qdrant client already initialized and healthy."); return qdrantClientInstance; } catch (e) { config_service_1.logger.warn("Qdrant client was initialized but seems unhealthy. Re-initializing.", { error: e instanceof Error ? e.message : String(e) }); // Fall through to re-initialize } } const qdrantHost = config_service_1.configService.QDRANT_HOST; const collectionName = config_service_1.configService.COLLECTION_NAME; config_service_1.logger.info(`Initializing Qdrant client for ${qdrantHost}`); const client = new js_client_rest_1.QdrantClient({ url: qdrantHost, timeout: config_service_1.configService.REQUEST_TIMEOUT }); // Added timeout await (0, retry_utils_1.withRetry)(async () => { // Expected vector configuration const expectedVectorSize = config_service_1.configService.EMBEDDING_DIMENSION; const expectedVectorDistance = "Cosine"; const collections = await client.getCollections(); const existingCollection = collections.collections.find(c => c.name === collectionName); if (!existingCollection) { config_service_1.logger.info(`Creating collection: ${collectionName} with vector size ${expectedVectorSize} and distance ${expectedVectorDistance}`); await client.createCollection(collectionName, { vectors: { size: expectedVectorSize, distance: expectedVectorDistance } }); config_service_1.logger.info(`Created collection: ${collectionName}`); } else { config_service_1.logger.info(`Collection '${collectionName}' already exists. Verifying configuration...`); const collectionInfo = await client.getCollection(collectionName); let actualSize; let actualDistance; // Check if vectors config is a single, unnamed vector config if (typeof collectionInfo.config.params.vectors === 'object' && collectionInfo.config.params.vectors !== null && 'size' in collectionInfo.config.params.vectors && 'distance' in collectionInfo.config.params.vectors && typeof collectionInfo.config.params.vectors.size === 'number' && typeof collectionInfo.config.params.vectors.distance === 'string') { actualSize = collectionInfo.config.params.vectors.size; actualDistance = collectionInfo.config.params.vectors.distance; } else { // This handles cases where vectors might be an object of named vectors, or an unexpected format config_service_1.logger.error(`Collection '${collectionName}' exists but its vector configuration is unexpected. It might be using named vectors, which is not supported by the current simple upsert logic. Config: ${JSON.stringify(collectionInfo.config.params.vectors)}`); throw new Error(`Collection '${collectionName}' has an incompatible vector configuration (e.g., named vectors or unexpected structure).`); } if (actualSize !== expectedVectorSize || actualDistance !== expectedVectorDistance) { config_service_1.logger.error(`Collection '${collectionName}' exists but has a mismatched configuration. Expected: size=${expectedVectorSize}, distance=${expectedVectorDistance}. Actual: size=${actualSize}, distance=${actualDistance}. Please delete the collection in Qdrant and restart, or ensure your EMBEDDING_MODEL matches the existing collection's vector size.`); throw new Error(`Collection '${collectionName}' has a mismatched vector configuration.`); } else { config_service_1.logger.info(`Collection '${collectionName}' configuration is compatible (size: ${actualSize}, distance: ${actualDistance}).`); } } }); qdrantClientInstance = client; config_service_1.logger.info("Qdrant client initialized successfully."); return qdrantClientInstance; } function getQdrantClient() { if (!qdrantClientInstance) { config_service_1.logger.error("Qdrant client has not been initialized. Call initializeQdrant() first."); throw new Error("Qdrant client is not initialized."); } return qdrantClientInstance; } // Add batch processing for vector operations async function batchUpsertVectors(client, collectionName, points, batchSize = 100) { config_service_1.logger.info(`Batch upserting ${points.length} points to collection '${collectionName}' with batch size ${batchSize}`); for (let i = 0; i < points.length; i += batchSize) { const batch = points.slice(i, i + batchSize); try { // TEMPORARY DEBUG LOG: config_service_1.logger.debug(`[DEBUG QDRANT IDs] Batch ${Math.floor(i / batchSize) + 1} IDs: ${JSON.stringify(batch.map(p => p.id))}`); await (0, retry_utils_1.withRetry)(async () => { // The js-client-rest library expects points to be an object { points: PointStruct[] } // or just PointStruct[] if using client.upsertPoints // The method client.upsert(collectionName, { wait: true, points: batch }) is correct await client.upsert(collectionName, { points: batch }); }); config_service_1.logger.debug(`Batch upserted ${batch.length} points (total processed: ${Math.min(i + batchSize, points.length)})`); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); const detailedErrorMessage = `Original error message: ${err.message}`; const errorDetailsPayload = { collectionName, batchStartIndex: i, batchSize }; // Attempt to extract more details if the error object has common fields from HTTP client errors if (typeof error === 'object' && error !== null) { if ('status' in error) errorDetailsPayload.qdrantErrorStatus = error.status; if ('data' in error) errorDetailsPayload.qdrantErrorData = error.data; // Common for some clients if ('response' in error && typeof error.response === 'object' && error.response !== null) { errorDetailsPayload.qdrantErrorResponse = error.response; // If response is an object } } config_service_1.logger.error(`Failed to batch upsert points. ${detailedErrorMessage}`, errorDetailsPayload); throw new Error(`Failed to batch upsert points: ${detailedErrorMessage}`); } } config_service_1.logger.info(`Successfully batch upserted ${points.length} points to collection '${collectionName}'`); }