@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
JavaScript
/**
* 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'
];
}
}