UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

543 lines 18.3 kB
"use strict"; /** * OpenAI MCP Provider Implementation * * Provider implementation for OpenAI LLM services */ Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAIProviderFactory = exports.OpenAIProvider = void 0; const types_1 = require("../types"); const base_provider_1 = require("./base-provider"); /** * OpenAI MCP Provider */ class OpenAIProvider extends base_provider_1.BaseLLMProvider { constructor(config) { super(config); this.apiKey = config.apiKey || process.env.OPENAI_API_KEY || ''; this.baseURL = config.endpoint || 'https://api.openai.com/v1'; this.organization = config.customHeaders?.['OpenAI-Organization']; // Set OpenAI-specific capabilities this.capabilities = { completion: true, streaming: true, functionCalling: true, embeddings: true, moderation: true, multimodal: true, codeGeneration: true, jsonMode: true }; // Set OpenAI models this.models = [ { id: 'gpt-4', name: 'GPT-4', description: 'Most capable GPT-4 model', capabilities: ['completion', 'function_calling', 'code_generation'], contextLength: 8192, inputCost: 0.03, outputCost: 0.06, deprecated: false }, { id: 'gpt-4-turbo-preview', name: 'GPT-4 Turbo', description: 'Latest GPT-4 Turbo model', capabilities: ['completion', 'function_calling', 'json_mode'], contextLength: 128000, inputCost: 0.01, outputCost: 0.03, deprecated: false }, { id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo', description: 'Fast and efficient model', capabilities: ['completion', 'function_calling'], contextLength: 4096, inputCost: 0.0005, outputCost: 0.0015, deprecated: false }, { id: 'text-embedding-ada-002', name: 'Text Embedding Ada 002', description: 'Text embedding model', capabilities: ['embeddings'], contextLength: 8191, inputCost: 0.0001, outputCost: 0, deprecated: false } ]; } /** * Initialize the provider */ async initialize() { if (!this.apiKey) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.INVALID_CONFIG, message: 'OpenAI API key is required', timestamp: new Date(), provider: this.id, retryable: false }); } try { // Test the API key with a simple request await this.testConnection(); this.status = types_1.ProviderStatus.AVAILABLE; } catch (error) { this.status = types_1.ProviderStatus.ERROR; throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_ERROR, message: `Failed to initialize OpenAI provider: ${error.message}`, timestamp: new Date(), provider: this.id, retryable: true }); } } /** * Test connection to OpenAI API */ async testConnection() { const response = await fetch(`${this.baseURL}/models`, { headers: this.getHeaders() }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } } /** * Check provider health */ async health() { const start = Date.now(); try { await this.testConnection(); const latency = Date.now() - start; return { status: 'healthy', latency, details: { apiEndpoint: this.baseURL, modelsAvailable: this.models.length } }; } catch (error) { return { status: 'unhealthy', details: { error: error.message, apiEndpoint: this.baseURL } }; } } /** * Send completion request */ async completion(request, context) { const model = request.parameters?.model || this.getDefaultModel(request.type); this.validateModel(model); const requestBody = this.buildCompletionRequest(request, model); try { const response = await fetch(`${this.baseURL}/chat/completions`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(requestBody) }); if (!response.ok) { throw await this.handleAPIError(response); } const data = await response.json(); return this.convertCompletionResponse(request, context, data); } catch (error) { if (error instanceof types_1.MCPError) { throw error; } throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_ERROR, message: `OpenAI API error: ${error.message}`, timestamp: new Date(), provider: this.id, requestId: request.id, retryable: true }); } } /** * Send streaming completion request */ async *stream(request, context) { const model = request.parameters?.model || this.getDefaultModel(request.type); this.validateModel(model); const requestBody = this.buildCompletionRequest(request, model, true); try { const response = await fetch(`${this.baseURL}/chat/completions`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(requestBody) }); if (!response.ok) { throw await this.handleAPIError(response); } if (!response.body) { throw new Error('No response body for streaming'); } 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.startsWith('data: ')) { const data = line.slice(6); if (data === '[DONE]') { return; } try { const chunk = JSON.parse(data); const responseChunk = this.convertStreamChunk(request, chunk); if (responseChunk) { yield responseChunk; } } catch (parseError) { // Skip invalid JSON chunks continue; } } } } } finally { reader.releaseLock(); } } catch (error) { if (error instanceof types_1.MCPError) { throw error; } throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_ERROR, message: `OpenAI streaming error: ${error.message}`, timestamp: new Date(), provider: this.id, requestId: request.id, retryable: true }); } } /** * Generate embeddings */ async embed(request, context) { const model = request.parameters?.model || 'text-embedding-ada-002'; const requestBody = { input: request.prompt, model: model }; try { const response = await fetch(`${this.baseURL}/embeddings`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(requestBody) }); if (!response.ok) { throw await this.handleAPIError(response); } const data = await response.json(); return this.convertEmbeddingResponse(request, context, data); } catch (error) { if (error instanceof types_1.MCPError) { throw error; } throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_ERROR, message: `OpenAI embedding error: ${error.message}`, timestamp: new Date(), provider: this.id, requestId: request.id, retryable: true }); } } /** * Moderate content */ async moderate(request, context) { const requestBody = { input: request.prompt }; try { const response = await fetch(`${this.baseURL}/moderations`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(requestBody) }); if (!response.ok) { throw await this.handleAPIError(response); } const data = await response.json(); return this.convertModerationResponse(request, context, data); } catch (error) { if (error instanceof types_1.MCPError) { throw error; } throw new types_1.MCPError({ code: types_1.MCPErrorCode.PROVIDER_ERROR, message: `OpenAI moderation error: ${error.message}`, timestamp: new Date(), provider: this.id, requestId: request.id, retryable: true }); } } /** * Build completion request body */ buildCompletionRequest(request, model, stream = false) { const messages = this.buildMessages(request); const functions = request.functions ? this.convertFunctions(request.functions) : undefined; const requestBody = { model, messages, stream, max_tokens: request.parameters?.maxTokens, temperature: request.parameters?.temperature, top_p: request.parameters?.topP, frequency_penalty: request.parameters?.frequencyPenalty, presence_penalty: request.parameters?.presencePenalty, stop: request.parameters?.stop }; // Add function calling support if (functions && functions.length > 0) { requestBody.functions = functions; if (request.type === types_1.LLMRequestType.FUNCTION_CALL) { requestBody.function_call = 'auto'; } } // Add JSON mode support if (request.parameters?.responseFormat?.type === 'json_object') { requestBody.response_format = { type: 'json_object' }; } // Remove undefined values Object.keys(requestBody).forEach(key => { if (requestBody[key] === undefined) { delete requestBody[key]; } }); return requestBody; } /** * Build messages array for OpenAI API */ buildMessages(request) { const messages = []; // Add context messages if available if (request.context?.history) { for (const msg of request.context.history) { messages.push({ role: msg.role === 'function' ? 'assistant' : msg.role, content: msg.content }); } } // Add current prompt messages.push({ role: 'user', content: request.prompt }); return messages; } /** * Convert function definitions to OpenAI format */ convertFunctions(functions) { return functions.map(fn => ({ name: fn.name, description: fn.description, parameters: fn.parameters })); } /** * Convert OpenAI completion response to standard format */ convertCompletionResponse(request, context, data) { const choice = data.choices[0]; const baseResponse = this.createBaseResponse(request, context); let functionCall; if (choice.message.function_call) { functionCall = { name: choice.message.function_call.name, arguments: JSON.parse(choice.message.function_call.arguments || '{}'), id: `call-${Date.now()}` }; } const usage = { promptTokens: data.usage.prompt_tokens, completionTokens: data.usage.completion_tokens, totalTokens: data.usage.total_tokens, model: data.model, provider: this.id, cost: this.calculateCost(data.usage.prompt_tokens, data.usage.completion_tokens, data.model) }; return { ...baseResponse, content: choice.message.content, functionCall, usage, model: data.model }; } /** * Convert OpenAI stream chunk to standard format */ convertStreamChunk(request, chunk) { const choice = chunk.choices[0]; if (!choice?.delta?.content) { return null; } return { id: chunk.id, requestId: request.id, delta: choice.delta.content, finished: choice.finish_reason === 'stop' }; } /** * Convert OpenAI embedding response to standard format */ convertEmbeddingResponse(request, context, data) { const baseResponse = this.createBaseResponse(request, context); const usage = { promptTokens: data.usage.prompt_tokens, completionTokens: 0, totalTokens: data.usage.total_tokens, model: data.model, provider: this.id, cost: this.calculateCost(data.usage.prompt_tokens, 0, data.model) }; return { ...baseResponse, embedding: data.data[0]?.embedding, usage, model: data.model }; } /** * Convert OpenAI moderation response to standard format */ convertModerationResponse(request, context, data) { const baseResponse = this.createBaseResponse(request, context); const result = data.results[0]; return { ...baseResponse, moderationResult: { flagged: result.flagged, categories: result.categories, categoryScores: result.category_scores }, model: data.model }; } /** * Get HTTP headers for OpenAI API */ getHeaders() { const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': 'anon-identity-mcp/1.0.0' }; if (this.organization) { headers['OpenAI-Organization'] = this.organization; } // Add custom headers from config if (this.config.customHeaders) { Object.assign(headers, this.config.customHeaders); } return headers; } /** * Handle OpenAI API errors */ async handleAPIError(response) { let errorData; try { errorData = await response.json(); } catch { errorData = { error: { message: response.statusText } }; } const errorMessage = errorData.error?.message || 'Unknown OpenAI API error'; let errorCode = types_1.MCPErrorCode.PROVIDER_ERROR; switch (response.status) { case 401: errorCode = types_1.MCPErrorCode.UNAUTHORIZED; break; case 403: errorCode = types_1.MCPErrorCode.FORBIDDEN; break; case 404: errorCode = types_1.MCPErrorCode.MODEL_NOT_FOUND; break; case 429: errorCode = types_1.MCPErrorCode.RATE_LIMITED; break; case 500: case 502: case 503: errorCode = types_1.MCPErrorCode.PROVIDER_UNAVAILABLE; break; } return new types_1.MCPError({ code: errorCode, message: errorMessage, timestamp: new Date(), provider: this.id, retryable: [500, 502, 503, 429].includes(response.status), details: { status: response.status, headers: Object.fromEntries(response.headers.entries()), body: errorData } }); } } exports.OpenAIProvider = OpenAIProvider; /** * OpenAI Provider Factory */ class OpenAIProviderFactory { static createProvider(config) { return new OpenAIProvider(config); } static validateConfig(config) { if (!config.apiKey && !process.env.OPENAI_API_KEY) { return false; } return true; } static getProviderType() { return 'openai'; } } exports.OpenAIProviderFactory = OpenAIProviderFactory; //# sourceMappingURL=openai-provider.js.map