Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
memory-vector-search.ts7.9 kB
import { CodeIndexOllamaEmbedder } from '../code-index/embedders/ollama' import { OpenAICompatibleEmbedder } from '../code-index/embedders/openai-compatible' import { IEmbedder } from '../code-index/interfaces/embedder' import { EmbedderConfig } from '../code-index/interfaces/config' export interface VectorDocument { id: string content: string vector: number[] metadata?: Record<string, any> } export class MemoryVectorSearch { private documents: VectorDocument[] = [] private embedder: IEmbedder constructor(config?: EmbedderConfig, model?: string) { console.log('[memory-vector-search]', config) // 向后兼容:如果第一个参数是字符串,则使用旧的 Ollama 配置 if (typeof config === 'string' || config === undefined) { const ollamaBaseUrl = config || 'http://localhost:11434' const ollamaModelId = model || 'nomic-embed-text' this.embedder = new CodeIndexOllamaEmbedder({ ollamaBaseUrl, ollamaModelId: ollamaModelId }) } else { // 新的配置对象方式 if (config.provider === 'openai-compatible') { this.embedder = new OpenAICompatibleEmbedder( (config as any).baseUrl, (config as any).apiKey, config.model ) } else { // 默认使用 Ollama this.embedder = new CodeIndexOllamaEmbedder({ ollamaBaseUrl: (config as any).baseUrl || 'http://localhost:11434', ollamaModelId: config.model || 'nomic-embed-text' }) } } } /** * 计算两个向量的余弦相似度 */ private cosineSimilarity(vecA: number[], vecB: number[]): number { if (vecA.length !== vecB.length) { throw new Error('Vectors must have the same length') } const dotProduct = vecA.reduce((sum, a, i) => sum + a * vecB[i], 0) const magnitudeA = Math.sqrt(vecA.reduce((sum, a) => sum + a * a, 0)) const magnitudeB = Math.sqrt(vecB.reduce((sum, b) => sum + b * b, 0)) if (magnitudeA === 0 || magnitudeB === 0) { return 0 } return dotProduct / (magnitudeA * magnitudeB) } /** * 添加文档到内存存储 */ async addDocument(id: string, content: string, metadata?: Record<string, any>): Promise<void> { const response = await this.embedder.createEmbeddings([content]) const vector = response.embeddings[0] this.documents.push({ id, content, vector, metadata }) } /** * 批量添加文档 */ async addDocuments(docs: Array<{ id: string; content: string; metadata?: Record<string, any> }>): Promise<void> { try { console.log('📝 开始批量添加文档,数量:', docs.length) // 分批处理以避免超时和服务器负载过大 const BATCH_SIZE = 10 const batches = [] for (let i = 0; i < docs.length; i += BATCH_SIZE) { batches.push(docs.slice(i, i + BATCH_SIZE)) } console.log(`📝 将分成 ${batches.length} 个批次处理,每批最多 ${BATCH_SIZE} 个文档`) for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) { const batch = batches[batchIndex] console.log(`📝 处理批次 ${batchIndex + 1}/${batches.length}: ${batch.length} 个文档`) const contents = batch.map(doc => doc.content) console.log('📝 内容示例:', contents.slice(0, 3)) console.log('📝 调用embedder.createEmbeddings...') console.log('📝 准备发送网络请求,等待响应...') // 添加超时控制,给分批处理更长的超时时间 const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(`批次 ${batchIndex + 1} 请求超时:等待嵌入服务响应超过60秒`)), 60000) }) const embeddingPromise = this.embedder.createEmbeddings(contents) const response: any = await Promise.race([embeddingPromise, timeoutPromise]) console.log('📝 嵌入向量创建成功,维度:', response.embeddings[0].length) console.log('📝 返回的嵌入向量数量:', response.embeddings.length) for (let i = 0; i < batch.length; i++) { this.documents.push({ id: batch[i].id, content: batch[i].content, vector: response.embeddings[i], metadata: batch[i].metadata }) } console.log(`📝 批次 ${batchIndex + 1} 添加成功`) } console.log('📝 所有文档添加成功') } catch (error: any) { console.error('❌ addDocuments 发生错误:') console.error('错误类型:', error?.constructor?.name || 'Unknown') console.error('错误消息:', error?.message || error) console.error('错误堆栈:', error?.stack || 'No stack trace') // 网络相关错误诊断 if (error?.message?.includes('请求超时')) { console.error('🔍 网络诊断: 请求超时,可能的原因:') console.error(' 1. 嵌入服务未启动或无响应') console.error(' 2. 网络连接问题') console.error(' 3. 代理配置问题') console.error(' 4. 服务器负载过高') console.error(' 💡 建议: 当前使用分批处理,如仍超时可进一步减小批次大小') } else if (error?.code === 'ECONNREFUSED') { console.error('🔍 网络诊断: 连接被拒绝') console.error(' - 请检查嵌入服务是否在 http://192.168.31.10:5000 运行') console.error(' - 请检查防火墙设置') } else if (error?.code === 'ENOTFOUND') { console.error('🔍 网络诊断: 主机未找到') console.error(' - 请检查IP地址���否正确') console.error(' - 请检查网络连接') } else if (error?.message?.includes('fetch')) { console.error('🔍 网络诊断: HTTP请求失败') console.error(' - 请检查服务URL和API密钥') console.error(' - 请检查服务是否支持该模型') } if (error?.cause) { console.error('根本原因:', error.cause) } throw error } } /** * 搜索相似文档 */ async search(query: string, topK = 5): Promise<Array<{ document: VectorDocument; score: number }>> { try { if (this.documents.length === 0) { console.log('📝 没有文档可搜索') return [] } console.log('📝 开始搜索,查询:', query) // 获取查询向量 const queryResponse = await this.embedder.createEmbeddings(["search_code: " + query]) const queryVector = queryResponse.embeddings[0] console.log('📝 查询向量维度:', queryVector.length) // 计算所有文档的相似度 const scores = this.documents.map(doc => ({ document: doc, score: this.cosineSimilarity(queryVector, doc.vector) })) // 按相似度排序并返回前K个 const results = scores .sort((a, b) => b.score - a.score) .slice(0, topK) console.log('📝 搜索完成,返回结果数量:', results.length) return results } catch (error: any) { console.error('❌ search 发生错误:') console.error('错误类型:', error?.constructor?.name || 'Unknown') console.error('错误消息:', error?.message || error) console.error('错误堆栈:', error?.stack || 'No stack trace') throw error } } /** * 获取存储的文档数量 */ getDocumentCount(): number { return this.documents.length } /** * 清空所有文档 */ clear(): void { this.documents = [] } /** * 根据ID获取文档 */ getDocument(id: string): VectorDocument | undefined { return this.documents.find(doc => doc.id === id) } /** * 获取所有文档 */ getAllDocuments(): VectorDocument[] { return [...this.documents] } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anrgct/autodev-codebase'

If you have feedback or need assistance with the MCP directory API, please join our Discord server