Skip to main content
Glama
embedding-service.ts5.23 kB
import OpenAI from 'openai'; import dotenv from 'dotenv'; dotenv.config(); export interface EmbeddingResult { embedding: number[]; tokenCount: number; } export class EmbeddingService { private openai: OpenAI; private model: string; private maxTokens: number; constructor() { const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) { throw new Error('OPENAI_API_KEY не найден в переменных окружения'); } this.openai = new OpenAI({ apiKey, }); this.model = 'text-embedding-3-small'; // Новейшая модель OpenAI this.maxTokens = 8191; // Лимит для text-embedding-3-small } async generateEmbedding(text: string): Promise<EmbeddingResult> { try { console.log(`🧠 Генерация эмбеддинга для текста (${text.length} символов)`); // Обрезаем текст если он слишком длинный const truncatedText = this.truncateText(text, this.maxTokens); const response = await this.openai.embeddings.create({ model: this.model, input: truncatedText, encoding_format: 'float', }); const embedding = response.data[0].embedding; const tokenCount = response.usage.total_tokens; console.log(`✅ Эмбеддинг сгенерирован (${embedding.length} измерений, ${tokenCount} токенов)`); return { embedding, tokenCount }; } catch (error) { console.error('Ошибка генерации эмбеддинга:', error); throw new Error(`Ошибка генерации эмбеддинга: ${error}`); } } async generateEmbeddings(texts: string[]): Promise<EmbeddingResult[]> { try { console.log(`🧠 Генерация эмбеддингов для ${texts.length} текстов`); // Обрезаем тексты если нужно и фильтруем пустые const truncatedTexts = texts .map(text => this.truncateText(text, this.maxTokens)) .filter(text => text && text.trim().length > 0); if (truncatedTexts.length === 0) { console.log('⚠️ Нет валидных текстов для генерации эмбеддингов'); return []; } console.log(`📝 Генерируем эмбеддинги для ${truncatedTexts.length} валидных текстов`); const response = await this.openai.embeddings.create({ model: this.model, input: truncatedTexts, encoding_format: 'float', }); const results: EmbeddingResult[] = response.data.map(item => ({ embedding: item.embedding, tokenCount: 0 // OpenAI не возвращает токены для batch запросов })); console.log(`✅ Сгенерировано ${results.length} эмбеддингов`); return results; } catch (error) { console.error('Ошибка генерации эмбеддингов:', error); throw new Error(`Ошибка генерации эмбеддингов: ${error}`); } } calculateSimilarity(embedding1: number[], embedding2: number[]): number { if (embedding1.length !== embedding2.length) { throw new Error('Размеры эмбеддингов должны совпадать'); } // Косинусное сходство let dotProduct = 0; let norm1 = 0; let norm2 = 0; for (let i = 0; i < embedding1.length; i++) { dotProduct += embedding1[i] * embedding2[i]; norm1 += embedding1[i] * embedding1[i]; norm2 += embedding2[i] * embedding2[i]; } norm1 = Math.sqrt(norm1); norm2 = Math.sqrt(norm2); if (norm1 === 0 || norm2 === 0) { return 0; } return dotProduct / (norm1 * norm2); } findMostSimilar( queryEmbedding: number[], embeddings: Array<{ id: string | number; embedding: number[] }>, topK: number = 5 ): Array<{ id: string | number; similarity: number }> { const similarities = embeddings.map(item => ({ id: item.id, similarity: this.calculateSimilarity(queryEmbedding, item.embedding) })); // Сортируем по убыванию сходства similarities.sort((a, b) => b.similarity - a.similarity); return similarities.slice(0, topK); } private truncateText(text: string, maxTokens: number): string { // Проверяем, что text не undefined и не null if (!text || typeof text !== 'string') { return ''; } // Простая эвристика: ~4 символа на токен const estimatedTokens = Math.ceil(text.length / 4); if (estimatedTokens <= maxTokens) { return text; } // Обрезаем до безопасного размера const maxChars = maxTokens * 4; return text.substring(0, maxChars); } async testConnection(): Promise<boolean> { try { const result = await this.generateEmbedding('test'); return result.embedding.length > 0; } catch { return false; } } }

Latest Blog Posts

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/Galiusbro/MCP'

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