UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

457 lines 18.2 kB
/** * OpenRouter Mastery Service * * Complete implementation of ALL OpenRouter API features and capabilities: * - Routing variants (:nitro, :floor, :online, :free) * - Streaming responses with real-time progress * - JSON mode with schema validation * - Function calling integration * - Cost tracking and budget enforcement * - Intelligent fallback systems * - Provider performance monitoring * - Advanced error handling and recovery */ import { getDatabase, getOpenRouterApiKey } from '../storage/unified-database.js'; // ===== ROUTING VARIANTS DEFINITIONS ===== export const ROUTING_VARIANTS = { ':nitro': { variant: ':nitro', description: 'Fastest available providers with premium performance', costMultiplier: 1.2, speedMultiplier: 2.0, features: ['fastest_routing', 'premium_providers', 'low_latency'] }, ':floor': { variant: ':floor', description: 'Most cost-effective options with good quality', costMultiplier: 0.7, speedMultiplier: 0.8, features: ['cost_optimization', 'budget_friendly', 'efficient'] }, ':online': { variant: ':online', description: 'Web-search augmented responses with current information', costMultiplier: 1.1, speedMultiplier: 0.9, features: ['web_search', 'current_information', 'fact_checking'] }, ':free': { variant: ':free', description: 'Rate-limited free access for development and testing', costMultiplier: 0.0, speedMultiplier: 0.6, features: ['free_tier', 'rate_limited', 'development'] }, ':default': { variant: ':default', description: 'Standard routing with balanced performance and cost', costMultiplier: 1.0, speedMultiplier: 1.0, features: ['balanced', 'standard', 'reliable'] } }; // ===== OPENROUTER MASTERY CLASS ===== export class OpenRouterMastery { apiKey; baseUrl = 'https://openrouter.ai/api/v1'; performanceCache = new Map(); circuitBreaker = new Map(); constructor(apiKey) { this.apiKey = apiKey; } /** * Master function that calls OpenRouter with ALL advanced features */ async callWithMastery(model, messages, options = {}) { const startTime = Date.now(); let currentCost = 0; let fallbacksUsed = 0; let providerSwitches = 0; const featuresUsed = []; try { // 1. Apply routing variant const routedModel = this.applyRoutingVariant(model, options.routingVariant); featuresUsed.push(`routing_${options.routingVariant || 'default'}`); // 2. Check budget controls if (options.budgetControls) { await this.enforcebudgetControls(options.budgetControls, currentCost); featuresUsed.push('budget_controls'); } // 3. Check circuit breaker if (this.isCircuitBreakerOpen(routedModel)) { throw new Error(`Circuit breaker open for model: ${routedModel}`); } // 4. Prepare request with advanced features const requestBody = await this.prepareAdvancedRequest(routedModel, messages, options, featuresUsed); // 5. Execute request with streaming or standard call let response; if (options.streaming) { response = await this.executeStreamingCall(requestBody, options, startTime); featuresUsed.push('streaming'); } else { response = await this.executeStandardCall(requestBody, startTime); } // 6. Update performance metrics await this.updatePerformanceMetrics(routedModel, response.performance); // 7. Track feature usage await this.trackFeatureUsage(routedModel, featuresUsed, response); // 8. Prepare final response response.metadata = { routingVariant: options.routingVariant || ':default', fallbacksUsed, providerSwitches, features: featuresUsed }; return response; } catch (error) { // Handle errors with intelligent fallback return await this.handleErrorWithFallback(model, messages, options, error, startTime, { currentCost, fallbacksUsed, providerSwitches, featuresUsed }); } } /** * Apply OpenRouter routing variants to model IDs */ applyRoutingVariant(model, variant) { if (!variant || variant === ':default') { return model; } // Remove any existing variant first const baseModel = model.replace(/(:(nitro|floor|online|free))$/, ''); return `${baseModel}${variant}`; } /** * Enforce budget controls and cost optimization */ async enforcebudgetControls(budgetControls, currentCost) { if (budgetControls.maxTotalCost && currentCost >= budgetControls.maxTotalCost) { throw new Error(`Budget exceeded: ${currentCost} >= ${budgetControls.maxTotalCost}`); } if (budgetControls.costThreshold && currentCost >= budgetControls.costThreshold) { // Auto-switch to cost-effective variants console.log(`💰 Cost threshold reached, switching to cost-optimized routing`); } } /** * Prepare request with advanced OpenRouter features */ async prepareAdvancedRequest(model, messages, options, featuresUsed) { const requestBody = { model, messages, stream: options.streaming || false }; // JSON mode with schema validation if (options.jsonMode && options.jsonSchema) { requestBody.response_format = { type: 'json_schema', json_schema: options.jsonSchema }; featuresUsed.push('json_mode', 'schema_validation'); } // Function calling / tools if (options.functionCalling && options.tools) { requestBody.tools = options.tools; requestBody.tool_choice = 'auto'; featuresUsed.push('function_calling', 'tools'); } // Add OpenRouter-specific headers requestBody.extra_headers = { 'HTTP-Referer': 'https://hive.ai', 'X-Title': 'Hive.AI Dynamic Consensus' }; return requestBody; } /** * Execute streaming call with real-time progress */ async executeStreamingCall(requestBody, options, startTime) { let content = ''; let inputTokens = 0; let outputTokens = 0; let cost = 0; const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', ...requestBody.extra_headers }, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(`OpenRouter API error: ${response.status} ${response.statusText}`); } const reader = response.body?.getReader(); if (!reader) { throw new Error('No stream reader available'); } const decoder = new TextDecoder(); try { while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n').filter(line => line.trim()); for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.slice(6); if (jsonStr === '[DONE]') continue; try { const data = JSON.parse(jsonStr); // Extract content if (data.choices?.[0]?.delta?.content) { const deltaContent = data.choices[0].delta.content; content += deltaContent; // Call progress callback if (options.streamingOptions?.onProgress) { options.streamingOptions.onProgress(deltaContent); } } // Extract usage and cost information if (data.usage) { inputTokens = data.usage.prompt_tokens || inputTokens; outputTokens = data.usage.completion_tokens || outputTokens; if (options.streamingOptions?.onTokenCount) { options.streamingOptions.onTokenCount(inputTokens, outputTokens); } } } catch (parseError) { // Skip invalid JSON lines continue; } } } } } finally { reader.releaseLock(); } const latencyMs = Date.now() - startTime; return { content, model: requestBody.model, provider: this.extractProvider(requestBody.model), cost, tokens: { input: inputTokens, output: outputTokens }, performance: { latencyMs, successRate: 1.0 }, metadata: { routingVariant: '', fallbacksUsed: 0, providerSwitches: 0, features: [] } }; } /** * Execute standard (non-streaming) call */ async executeStandardCall(requestBody, startTime) { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', ...requestBody.extra_headers }, body: JSON.stringify(requestBody) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} ${response.statusText} - ${errorText}`); } const data = await response.json(); const latencyMs = Date.now() - startTime; return { content: data.choices[0]?.message?.content || '', model: data.model || requestBody.model, provider: this.extractProvider(data.model || requestBody.model), cost: this.calculateCost(data.usage), tokens: { input: data.usage?.prompt_tokens || 0, output: data.usage?.completion_tokens || 0 }, performance: { latencyMs, successRate: 1.0 }, metadata: { routingVariant: '', fallbacksUsed: 0, providerSwitches: 0, features: [] } }; } /** * Handle errors with intelligent fallback strategies */ async handleErrorWithFallback(originalModel, messages, options, error, startTime, context) { console.warn(`⚠️ OpenRouter call failed: ${error.message}`); // Update circuit breaker this.updateCircuitBreaker(originalModel, false); // Try fallback strategies if (context.fallbacksUsed < 3) { // Max 3 fallbacks // Strategy 1: Try different routing variant if (options.routingVariant === ':nitro') { console.log('🔄 Falling back from :nitro to :default variant'); const fallbackOptions = { ...options, routingVariant: ':default' }; context.fallbacksUsed++; return await this.callWithMastery(originalModel, messages, fallbackOptions); } // Strategy 2: Try :floor variant for cost issues if (error.message.includes('budget') || error.message.includes('cost')) { console.log('🔄 Falling back to :floor variant for cost optimization'); const fallbackOptions = { ...options, routingVariant: ':floor' }; context.fallbacksUsed++; return await this.callWithMastery(originalModel, messages, fallbackOptions); } // Strategy 3: Try :free variant as emergency fallback if (options.budgetControls?.emergencyFallback) { console.log('🔄 Emergency fallback to :free variant'); const fallbackOptions = { ...options, routingVariant: ':free' }; context.fallbacksUsed++; return await this.callWithMastery(originalModel, messages, fallbackOptions); } } // If all fallbacks failed, throw the original error throw error; } /** * Circuit breaker pattern for provider health management */ isCircuitBreakerOpen(model) { const breaker = this.circuitBreaker.get(model); if (!breaker) return false; const now = Date.now(); const timeSinceLastFailure = now - breaker.lastFailure; // Reset circuit breaker after 5 minutes if (timeSinceLastFailure > 5 * 60 * 1000) { breaker.isOpen = false; breaker.failures = 0; } return breaker.isOpen; } updateCircuitBreaker(model, success) { let breaker = this.circuitBreaker.get(model); if (!breaker) { breaker = { failures: 0, lastFailure: 0, isOpen: false }; this.circuitBreaker.set(model, breaker); } if (success) { breaker.failures = 0; breaker.isOpen = false; } else { breaker.failures++; breaker.lastFailure = Date.now(); // Open circuit breaker after 3 consecutive failures if (breaker.failures >= 3) { breaker.isOpen = true; console.warn(`🚨 Circuit breaker opened for model: ${model}`); } } } /** * Update provider performance metrics */ async updatePerformanceMetrics(model, performance) { const db = await getDatabase(); const modelInternalId = await this.getModelInternalId(model); if (modelInternalId) { try { await db.run(` INSERT OR REPLACE INTO provider_performance (model_internal_id, provider_name, routing_variant, avg_latency_ms, success_rate, measured_at) VALUES (?, ?, ?, ?, ?, ?) `, [ modelInternalId, this.extractProvider(model), this.extractVariant(model), performance.latencyMs, performance.successRate, new Date().toISOString() ]); } catch (error) { console.warn('Failed to update performance metrics:', error); } } } /** * Track feature usage for analytics */ async trackFeatureUsage(model, features, response) { // This will be called with conversation_id when integrated with consensus engine console.log(`📊 Features used: ${features.join(', ')} for model: ${model}`); } // ===== UTILITY FUNCTIONS ===== extractProvider(model) { return model.split('/')[0] || 'unknown'; } extractVariant(model) { const match = model.match(/(:(nitro|floor|online|free))$/); return match ? match[1] : ':default'; } calculateCost(usage) { // Simplified cost calculation - in real implementation would use model-specific pricing if (!usage) return 0; const inputCost = (usage.prompt_tokens || 0) * 0.000001; // $1 per 1M tokens const outputCost = (usage.completion_tokens || 0) * 0.000002; // $2 per 1M tokens return inputCost + outputCost; } async getModelInternalId(openrouterId) { try { const db = await getDatabase(); const result = await db.get('SELECT internal_id FROM openrouter_models WHERE openrouter_id = ?', [openrouterId.replace(/(:(nitro|floor|online|free))$/, '')] // Remove variant for lookup ); return result?.internal_id || null; } catch (error) { console.warn('Failed to get model internal ID:', error); return null; } } } // ===== CONVENIENCE FUNCTIONS ===== /** * Create OpenRouter mastery instance */ export async function createOpenRouterMastery() { const apiKey = await getOpenRouterApiKey(); if (!apiKey) { throw new Error('OpenRouter API key not configured'); } return new OpenRouterMastery(apiKey); } /** * Quick call with routing variant */ export async function callWithVariant(model, messages, variant) { const mastery = await createOpenRouterMastery(); return await mastery.callWithMastery(model, messages, { routingVariant: variant }); } /** * Quick streaming call */ export async function callWithStreaming(model, messages, onProgress) { const mastery = await createOpenRouterMastery(); return await mastery.callWithMastery(model, messages, { streaming: true, streamingOptions: { onProgress } }); } /** * Budget-controlled call */ export async function callWithBudget(model, messages, maxCost) { const mastery = await createOpenRouterMastery(); return await mastery.callWithMastery(model, messages, { budgetControls: { maxCostPerCall: maxCost } }); } /** * JSON mode call with schema */ export async function callWithJsonSchema(model, messages, schema) { const mastery = await createOpenRouterMastery(); return await mastery.callWithMastery(model, messages, { jsonMode: true, jsonSchema: schema }); } export default OpenRouterMastery; //# sourceMappingURL=openrouter-mastery.js.map