@alvinveroy/codecompass
Version:
AI-powered MCP server for codebase navigation and LLM prompt optimization
121 lines (120 loc) • 7.6 kB
JavaScript
;
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}'`);
}