UNPKG

@xiaohui-wang/mcpadvisor

Version:

MCP Advisor & Installation - Find the right MCP server for your needs

177 lines (176 loc) 6.25 kB
import { oceanBaseClient } from './controller.js'; import { OCEANBASE_URL } from '../../../config/constants.js'; import logger from '../../../utils/logger.js'; import { normalizeVector } from '../../../utils/vectorUtils.js'; /** * 将 MCPServerResponse 转换为元数据 */ const serverResponseToMetadata = (data) => { // 创建基本元数据 const metadata = { title: data.title, description: data.description, github_url: data.github_url, }; // 处理分类信息 if (data.categories) { metadata.categories = data.categories; } // 处理标签信息 if (data.tags) { metadata.tags = data.tags; } return metadata; }; /** * 将搜索结果转换为 MCPServerResponse */ const searchResultToServerResponse = (result) => { // 创建基本响应 const response = { id: result.id, title: result.metadata.title, description: result.metadata.description, github_url: result.metadata.github_url, similarity: result.similarity, }; // 添加分类信息(如果有) if (result.metadata.categories) { response.categories = result.metadata.categories; } // 添加标签信息(如果有) if (result.metadata.tags) { response.tags = result.metadata.tags; } return response; }; /** * OceanBase 向量搜索引擎实现 * 实现 IWritableVectorSearchEngine 接口,支持读写操作 */ export class OceanBaseVectorEngine { isInitialized = false; /** * 构造函数 */ constructor() { if (OCEANBASE_URL) { this.initDatabase().catch(error => { this.handleError(error, 'initializing OceanBase'); }); } else { logger.warn('OCEANBASE_URL is not set, OceanBase vector engine will not be initialized'); } } /** * 初始化数据库 */ async initDatabase() { try { await oceanBaseClient.connect(); await oceanBaseClient.initDatabase(); this.isInitialized = true; logger.info('OceanBase vector engine initialized'); } catch (error) { this.handleError(error, 'OceanBase initialization'); } } /** * 添加向量条目 * 在存储前对向量进行归一化处理 */ async addEntry(id, vector, data) { if (!OCEANBASE_URL || !this.isInitialized) { logger.warn('OceanBase vector engine is not initialized, cannot add entry'); return; } try { // 对向量进行归一化 const normalizedVector = normalizeVector(vector); logger.debug(`Vector normalized from magnitude ${this.calculateMagnitude(vector).toFixed(4)} to ${this.calculateMagnitude(normalizedVector).toFixed(4)}`); const metadata = serverResponseToMetadata(data); await oceanBaseClient.addVector(id, normalizedVector, metadata); logger.debug(`Added normalized vector entry for server: ${data.title}`); } catch (error) { this.handleError(error, 'adding vector entry'); } } /** * 向量相似度搜索 * 在搜索前对查询向量进行归一化处理 * @param queryVector 查询向量 * @param limit 结果数量限制 * @param options 选项,包括分类过滤、相似度阈值和文本查询 * @returns 搜索结果 */ async search(queryVector, limit = 10, options = {}) { if (!OCEANBASE_URL || !this.isInitialized) { logger.warn('OceanBase vector engine is not initialized, returning empty search results'); return []; } try { // 对查询向量进行归一化 const normalizedQueryVector = normalizeVector(queryVector); logger.debug(`Query vector normalized from magnitude ${this.calculateMagnitude(queryVector).toFixed(4)} to ${this.calculateMagnitude(normalizedQueryVector).toFixed(4)}`); // 记录搜索参数 if (options.categories && options.categories.length > 0) { logger.debug(`Applying category filter: ${options.categories.join(', ')}`); } if (options.minSimilarity) { logger.debug(`Using minimum similarity threshold: ${options.minSimilarity}`); } if (options.textQuery) { logger.debug(`Using text query: "${options.textQuery}"`); } // 执行搜索,包含向量搜索和文本搜索 const startTime = Date.now(); const results = await oceanBaseClient.searchVectors(normalizedQueryVector, limit, options); const duration = Date.now() - startTime; const serverResponses = results.map(searchResultToServerResponse); // 记录搜索结果 const searchType = options.textQuery ? 'hybrid (vector + text)' : 'vector-only'; logger.debug(`Found ${serverResponses.length} results from ${searchType} search in ${duration}ms`); return serverResponses; } catch (error) { this.handleError(error, 'vector search'); } } /** * 清除所有向量数据 */ async clear() { if (!OCEANBASE_URL || !this.isInitialized) { logger.warn('OceanBase vector engine is not initialized, cannot clear entries'); return; } try { await oceanBaseClient.deleteAll(); logger.info('Cleared all vector entries'); } catch (error) { this.handleError(error, 'clearing vector entries'); } } /** * 计算向量的大小(模) * @param vector 输入向量 * @returns 向量的模 */ calculateMagnitude(vector) { return Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0)); } /** * 统一错误处理 */ handleError(error, operation) { const message = error instanceof Error ? error.message : String(error); logger.error(`Error in ${operation}: ${message}`); throw error; } }