UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with external MCP server integration, multi-provider support, and professional CLI. Connect to 65+ MCP servers for filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major pr

340 lines (339 loc) 13 kB
/** * Azure OpenAI Provider * * Enterprise-grade OpenAI integration through Microsoft Azure. * Supports all OpenAI models with enhanced security and compliance. */ import { AIProviderName } from '../core/types.js'; import { logger } from '../utils/logger.js'; export class AzureOpenAIProvider { name = AIProviderName.AZURE; apiKey; endpoint; deploymentId; apiVersion; constructor() { this.apiKey = this.getApiKey(); this.endpoint = this.getEndpoint(); this.deploymentId = this.getDeploymentId(); this.apiVersion = process.env.AZURE_OPENAI_API_VERSION || '2024-02-15-preview'; logger.debug(`[AzureOpenAIProvider] Initialized with endpoint: ${this.endpoint}, deployment: ${this.deploymentId}`); } getApiKey() { const apiKey = process.env.AZURE_OPENAI_API_KEY; if (!apiKey) { throw new Error('AZURE_OPENAI_API_KEY environment variable is required'); } return apiKey; } getEndpoint() { const endpoint = process.env.AZURE_OPENAI_ENDPOINT; if (!endpoint) { throw new Error('AZURE_OPENAI_ENDPOINT environment variable is required'); } return endpoint.replace(/\/$/, ''); // Remove trailing slash } getDeploymentId() { const deploymentId = process.env.AZURE_OPENAI_DEPLOYMENT_ID; if (!deploymentId) { throw new Error('AZURE_OPENAI_DEPLOYMENT_ID environment variable is required'); } return deploymentId; } getApiUrl(stream = false) { return `${this.endpoint}/openai/deployments/${this.deploymentId}/chat/completions?api-version=${this.apiVersion}`; } async makeRequest(body, stream = false) { const url = this.getApiUrl(stream); const headers = { 'Content-Type': 'application/json', 'api-key': this.apiKey }; logger.debug(`[AzureOpenAIProvider.makeRequest] ${stream ? 'Streaming' : 'Non-streaming'} request to deployment: ${this.deploymentId}`); logger.debug(`[AzureOpenAIProvider.makeRequest] Max tokens: ${body.max_tokens || 'default'}, Temperature: ${body.temperature || 'default'}`); const response = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) }); if (!response.ok) { const errorText = await response.text(); logger.error(`[AzureOpenAIProvider.makeRequest] API error ${response.status}: ${errorText}`); throw new Error(`Azure OpenAI API error ${response.status}: ${errorText}`); } return response; } async generateText(optionsOrPrompt, schema) { logger.debug('[AzureOpenAIProvider.generateText] Starting text generation'); // Parse parameters with backward compatibility const options = typeof optionsOrPrompt === 'string' ? { prompt: optionsOrPrompt } : optionsOrPrompt; const { prompt, temperature = 0.7, maxTokens = 500, systemPrompt = 'You are a helpful AI assistant.' } = options; logger.debug(`[AzureOpenAIProvider.generateText] Prompt: "${prompt.substring(0, 100)}...", Temperature: ${temperature}, Max tokens: ${maxTokens}`); const messages = []; if (systemPrompt) { messages.push({ role: 'system', content: systemPrompt }); } messages.push({ role: 'user', content: prompt }); const requestBody = { messages, temperature, max_tokens: maxTokens }; try { const response = await this.makeRequest(requestBody); const data = await response.json(); logger.debug(`[AzureOpenAIProvider.generateText] Success. Generated ${data.usage.completion_tokens} tokens`); const content = data.choices[0]?.message?.content || ''; return { content, provider: this.name, model: data.model, usage: { promptTokens: data.usage.prompt_tokens, completionTokens: data.usage.completion_tokens, totalTokens: data.usage.total_tokens }, finishReason: data.choices[0]?.finish_reason || 'stop' }; } catch (error) { logger.error('[AzureOpenAIProvider.generateText] Error:', error); throw error; } } async streamText(optionsOrPrompt, schema) { logger.debug('[AzureOpenAIProvider.streamText] Starting text streaming'); // Parse parameters with backward compatibility const options = typeof optionsOrPrompt === 'string' ? { prompt: optionsOrPrompt } : optionsOrPrompt; const { prompt, temperature = 0.7, maxTokens = 500, systemPrompt = 'You are a helpful AI assistant.' } = options; logger.debug(`[AzureOpenAIProvider.streamText] Streaming prompt: "${prompt.substring(0, 100)}..."`); const messages = []; if (systemPrompt) { messages.push({ role: 'system', content: systemPrompt }); } messages.push({ role: 'user', content: prompt }); const requestBody = { messages, temperature, max_tokens: maxTokens, stream: true }; try { const response = await this.makeRequest(requestBody, true); if (!response.body) { throw new Error('No response body received'); } // Return a StreamTextResult-like object return { textStream: this.createAsyncIterable(response.body), text: '', usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, finishReason: 'stop' }; } catch (error) { logger.error('[AzureOpenAIProvider.streamText] Error:', error); throw error; } } async *createAsyncIterable(body) { const reader = body.getReader(); const decoder = new TextDecoder(); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() === '') continue; if (line.startsWith('data: ')) { const data = line.slice(6); if (data.trim() === '[DONE]') continue; try { const chunk = JSON.parse(data); // Extract text content from chunk if (chunk.choices?.[0]?.delta?.content) { yield chunk.choices[0].delta.content; } } catch (parseError) { logger.warn('[AzureOpenAIProvider.createAsyncIterable] Failed to parse chunk:', parseError); continue; } } } } } finally { reader.releaseLock(); } } async *generateTextStream(optionsOrPrompt) { logger.debug('[AzureOpenAIProvider.generateTextStream] Starting text streaming'); // Parse parameters with backward compatibility const options = typeof optionsOrPrompt === 'string' ? { prompt: optionsOrPrompt } : optionsOrPrompt; const { prompt, temperature = 0.7, maxTokens = 500, systemPrompt = 'You are a helpful AI assistant.' } = options; logger.debug(`[AzureOpenAIProvider.generateTextStream] Streaming prompt: "${prompt.substring(0, 100)}..."`); const messages = []; if (systemPrompt) { messages.push({ role: 'system', content: systemPrompt }); } messages.push({ role: 'user', content: prompt }); const requestBody = { messages, temperature, max_tokens: maxTokens, stream: true }; try { const response = await this.makeRequest(requestBody, true); if (!response.body) { throw new Error('No response body received'); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() === '') continue; if (line.startsWith('data: ')) { const data = line.slice(6); if (data.trim() === '[DONE]') continue; try { const chunk = JSON.parse(data); // Extract text content from chunk if (chunk.choices?.[0]?.delta?.content) { yield { content: chunk.choices[0].delta.content, provider: this.name, model: chunk.model || this.deploymentId }; } } catch (parseError) { logger.warn('[AzureOpenAIProvider.generateTextStream] Failed to parse chunk:', parseError); continue; } } } } } finally { reader.releaseLock(); } logger.debug('[AzureOpenAIProvider.generateTextStream] Streaming completed'); } catch (error) { logger.error('[AzureOpenAIProvider.generateTextStream] Error:', error); throw error; } } async testConnection() { logger.debug('[AzureOpenAIProvider.testConnection] Testing connection to Azure OpenAI'); const startTime = Date.now(); try { await this.generateText({ prompt: 'Hello', maxTokens: 5 }); const responseTime = Date.now() - startTime; logger.debug(`[AzureOpenAIProvider.testConnection] Connection test successful (${responseTime}ms)`); return { success: true, responseTime }; } catch (error) { const responseTime = Date.now() - startTime; logger.error(`[AzureOpenAIProvider.testConnection] Connection test failed (${responseTime}ms):`, error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error', responseTime }; } } isConfigured() { try { this.getApiKey(); this.getEndpoint(); this.getDeploymentId(); return true; } catch { return false; } } getRequiredConfig() { return ['AZURE_OPENAI_API_KEY', 'AZURE_OPENAI_ENDPOINT', 'AZURE_OPENAI_DEPLOYMENT_ID']; } getOptionalConfig() { return ['AZURE_OPENAI_API_VERSION']; } getModels() { return [ 'gpt-4', 'gpt-4-turbo', 'gpt-4-32k', 'gpt-35-turbo', 'gpt-35-turbo-16k' ]; } supportsStreaming() { return true; } supportsSchema() { return true; // Azure OpenAI supports JSON mode and function calling } getCapabilities() { return [ 'text-generation', 'streaming', 'conversation', 'system-prompts', 'json-mode', 'function-calling', 'enterprise-security', 'content-filtering' ]; } }