UNPKG

@vfarcic/dot-ai

Version:

Universal Kubernetes application deployment agent with CLI and MCP interfaces

199 lines (198 loc) 6.47 kB
"use strict"; /** * Embedding Service * * Optional semantic search enhancement for pattern matching. * Gracefully falls back to keyword-only search when OpenAI API key is not available. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmbeddingService = exports.OpenAIEmbeddingProvider = void 0; const openai_1 = __importDefault(require("openai")); /** * OpenAI Embedding Provider * Optional provider for semantic search enhancement */ class OpenAIEmbeddingProvider { client = null; model; dimensions; available; constructor(config = {}) { const apiKey = config.apiKey || process.env.OPENAI_API_KEY; this.model = config.model || 'text-embedding-3-small'; this.dimensions = config.dimensions || 1536; // text-embedding-3-small default this.available = false; if (apiKey) { try { this.client = new openai_1.default({ apiKey: apiKey }); this.available = true; } catch (error) { // Client creation failed, remain unavailable this.available = false; this.client = null; } } } async generateEmbedding(text) { if (!this.isAvailable()) { throw new Error('OpenAI embedding provider not available'); } if (!text || text.trim().length === 0) { throw new Error('Text cannot be empty for embedding generation'); } try { const response = await this.client.embeddings.create({ model: this.model, input: text.trim(), encoding_format: 'float' }); if (!response.data || response.data.length === 0) { throw new Error('No embedding data returned from OpenAI API'); } return response.data[0].embedding; } catch (error) { if (error instanceof Error) { throw new Error(`OpenAI embedding failed: ${error.message}`); } throw new Error(`OpenAI embedding failed: ${String(error)}`); } } async generateEmbeddings(texts) { if (!this.isAvailable()) { throw new Error('OpenAI embedding provider not available'); } if (!texts || texts.length === 0) { return []; } const validTexts = texts .map(t => t?.trim()) .filter(t => t && t.length > 0); if (validTexts.length === 0) { return []; } try { const response = await this.client.embeddings.create({ model: this.model, input: validTexts, encoding_format: 'float' }); return response.data.map(item => item.embedding); } catch (error) { if (error instanceof Error) { throw new Error(`OpenAI batch embedding failed: ${error.message}`); } throw new Error(`OpenAI batch embedding failed: ${String(error)}`); } } isAvailable() { return this.available && this.client !== null; } getDimensions() { return this.dimensions; } getModel() { return this.model; } } exports.OpenAIEmbeddingProvider = OpenAIEmbeddingProvider; /** * Main Embedding Service * Provides optional semantic search capabilities with graceful degradation */ class EmbeddingService { provider; constructor(config = {}) { // Try to initialize OpenAI provider, but don't fail if unavailable try { const provider = new OpenAIEmbeddingProvider(config); this.provider = provider.isAvailable() ? provider : null; } catch (error) { this.provider = null; } } /** * Generate embedding for text (optional enhancement) * Returns null if embeddings not available - caller should handle gracefully */ async generateEmbedding(text) { if (!this.isAvailable()) { return null; } try { return await this.provider.generateEmbedding(text); } catch (error) { // Log error but don't throw - allow graceful fallback console.warn('Embedding generation failed, falling back to keyword search:', error); return null; } } /** * Generate embeddings for multiple texts (optional enhancement) * Returns empty array if embeddings not available */ async generateEmbeddings(texts) { if (!this.isAvailable()) { return []; } try { return await this.provider.generateEmbeddings(texts); } catch (error) { // Log error but don't throw - allow graceful fallback console.warn('Batch embedding generation failed, falling back to keyword search:', error); return []; } } /** * Check if semantic search is available */ isAvailable() { return this.provider !== null && this.provider.isAvailable(); } /** * Get embedding dimensions (if available) */ getDimensions() { return this.provider?.getDimensions() || 1536; } /** * Get status information for debugging/logging */ getStatus() { if (this.isAvailable()) { const openaiProvider = this.provider; return { available: true, provider: 'openai', model: openaiProvider.getModel(), dimensions: openaiProvider.getDimensions() }; } return { available: false, provider: null, reason: 'OPENAI_API_KEY not set - using keyword-only pattern search' }; } /** * Create searchable text from pattern data */ createPatternSearchText(pattern) { return [ pattern.description, ...pattern.triggers, pattern.rationale, // Include resource types for better semantic matching ...pattern.suggestedResources.map(r => `kubernetes ${r.toLowerCase()}`) ].join(' ').trim(); } } exports.EmbeddingService = EmbeddingService;