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

114 lines (113 loc) 4.43 kB
/** * OpenRouter API Client - SOURCE_OF_TRUTH Implementation * * Simple OpenRouter API wrapper for consensus tool per exact design specifications. * Only used for consensus pipeline - no other functionality. * Now includes health monitoring integration for bulletproof error handling. */ import { globalHealthMonitor } from './health-monitor.js'; /** * Call OpenRouter API with model, messages, and API key * Per SOURCE_OF_TRUTH: Function callOpenRouter(model, messages, apiKey) */ export async function callOpenRouter(model, messages, apiKey) { // Validate inputs if (!model || !messages || !apiKey) { throw new Error('callOpenRouter: model, messages, and apiKey are required'); } if (!apiKey.startsWith('sk-or-')) { throw new Error('Invalid OpenRouter API key format. Key must start with sk-or-'); } // Prepare request const requestBody = { model: model, messages: messages, temperature: 0.7, max_tokens: 4000 }; const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, 'HTTP-Referer': 'https://hivetechs.io', 'X-Title': 'Hive.AI Consensus Pipeline' }; try { // Add model to health monitoring if not already tracked const [provider, modelName] = model.includes('/') ? model.split('/') : ['openrouter', model]; globalHealthMonitor.addTestModel(model); // Make API call to OpenRouter const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: headers, body: JSON.stringify(requestBody) }); // Handle non-OK responses if (!response.ok) { const errorText = await response.text(); let errorMessage = `OpenRouter API error (${response.status})`; try { const errorData = JSON.parse(errorText); if (errorData.error?.message) { errorMessage = errorData.error.message; } } catch { // Use status-based error message if JSON parsing fails if (response.status === 401) { errorMessage = 'Invalid OpenRouter API key. Check your configuration.'; } else if (response.status === 402) { errorMessage = 'Insufficient credits in OpenRouter account.'; } else if (response.status === 404) { errorMessage = `Model "${model}" not found on OpenRouter.`; } else if (response.status === 429) { errorMessage = 'Rate limit exceeded. Please try again later.'; } } throw new Error(errorMessage); } // Parse successful response const data = await response.json(); if (!data.choices || !data.choices[0] || !data.choices[0].message) { throw new Error('Invalid response format from OpenRouter API'); } return { content: data.choices[0].message.content, model: data.model || model, usage: data.usage }; } catch (error) { // Re-throw with better error messages if (error.name === 'TypeError' && error.message.includes('fetch')) { throw new Error('Network error: Unable to connect to OpenRouter API'); } if (error.message.includes('JSON')) { throw new Error('OpenRouter API returned invalid response format'); } // Pass through our custom errors throw error; } } /** * Test OpenRouter API connectivity with a simple call * Uses the first available model from OpenRouter data */ export async function testOpenRouterConnection(apiKey, testModel) { try { if (!testModel) { throw new Error('No test model provided. OpenRouter model selection required.'); } const testMessages = [ { role: 'user', content: 'Say "test successful"' } ]; const response = await callOpenRouter(testModel, testMessages, apiKey); return response.content.toLowerCase().includes('test successful'); } catch (error) { console.error('OpenRouter connection test failed:', error.message); return false; } }