UNPKG

promptly-ai

Version:

A universal template-based prompt management system for LLM applications

249 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAIAdapter = void 0; class OpenAIRequestBuilder { static build(config) { const params = { model: config.model, messages: config.messages.map((msg) => ({ role: msg.role, content: msg.content, })), temperature: config.temperature ?? this.DEFAULT_TEMPERATURE, max_completion_tokens: config.maxTokens ?? this.DEFAULT_MAX_TOKENS, }; // Add reasoning effort for o-series models if (config.maxThinkingTokens && this.isReasoningModel(config.model)) { params.reasoning_effort = this.mapThinkingTokensToReasoningEffort(config.maxThinkingTokens); } return params; } static isReasoningModel(model) { return /^o\d+/i.test(model); } static mapThinkingTokensToReasoningEffort(thinkingTokens) { if (thinkingTokens > 20000) return 'high'; if (thinkingTokens > 10000) return 'medium'; return 'low'; } } OpenAIRequestBuilder.DEFAULT_MAX_TOKENS = 1000; OpenAIRequestBuilder.DEFAULT_TEMPERATURE = 0.7; class OpenAIStreamingStrategy { static shouldUseStreaming(config) { const maxTokens = config.maxTokens ?? 1000; const maxThinkingTokens = config.maxThinkingTokens ?? 0; const totalMaxTokens = maxTokens + maxThinkingTokens; return totalMaxTokens > this.STREAMING_THRESHOLD || maxThinkingTokens > 0; } } OpenAIStreamingStrategy.STREAMING_THRESHOLD = 10000; class OpenAIStreamProcessor { constructor(model) { this.content = ''; this.inputTokens = 0; this.outputTokens = 0; this.thinkingTokens = 0; this.model = model; } async processStream(stream) { for await (const chunk of stream) { this.processChunk(chunk); } const costUsd = OpenAICostCalculator.calculateCost(this.model, this.inputTokens, this.outputTokens); return { content: this.content, inputTokens: this.inputTokens, outputTokens: this.outputTokens, thinkingTokens: this.thinkingTokens, costUsd, }; } processChunk(chunk) { // Process content delta const delta = chunk.choices[0]?.delta; if (delta?.content) { this.content += delta.content; } // Process usage information (typically in the final chunk) if (chunk.usage) { this.inputTokens = chunk.usage.prompt_tokens; this.outputTokens = chunk.usage.completion_tokens; this.thinkingTokens = chunk.usage.thinking_tokens ?? 0; } } } class OpenAICostCalculator { static calculateCost(model, inputTokens, outputTokens) { const pricing = this.PRICING[model]; if (!pricing) { throw new Error(`Pricing not found for model: ${model}. Please add pricing information for this model.`); } return ((inputTokens * pricing.input) + (outputTokens * pricing.output)) / 1000000; } } // OpenAI pricing as of January 2025 (per million tokens) OpenAICostCalculator.PRICING = { // GPT-4.1 models 'gpt-4.1': { input: 2.00, output: 8.00 }, 'gpt-4.1-2025-04-14': { input: 2.00, output: 8.00 }, // GPT-4 Turbo models 'gpt-4-turbo': { input: 10.00, output: 30.00 }, 'gpt-4-turbo-2024-04-09': { input: 10.00, output: 30.00 }, 'gpt-4-turbo-preview': { input: 10.00, output: 30.00 }, 'gpt-4-0125-preview': { input: 10.00, output: 30.00 }, 'gpt-4-1106-preview': { input: 10.00, output: 30.00 }, // GPT-4 models 'gpt-4': { input: 30.00, output: 60.00 }, 'gpt-4-0613': { input: 30.00, output: 60.00 }, 'gpt-4-0314': { input: 30.00, output: 60.00 }, // GPT-4 mini models 'gpt-4-mini': { input: 0.40, output: 1.60 }, 'gpt-4-mini-2025-04-14': { input: 0.40, output: 1.60 }, // GPT-4 nano models 'gpt-4-nano': { input: 0.10, output: 0.40 }, 'gpt-4-nano-2025-04-14': { input: 0.10, output: 0.40 }, // GPT-4.5 preview 'gpt-4.5-preview': { input: 75.00, output: 150.00 }, 'gpt-4.5-preview-2025-02-27': { input: 75.00, output: 150.00 }, // GPT-4o models 'gpt-4o': { input: 2.50, output: 10.00 }, 'gpt-4o-2024-08-06': { input: 2.50, output: 10.00 }, 'gpt-4o-2024-05-13': { input: 5.00, output: 15.00 }, 'gpt-4o-audio-preview': { input: 2.50, output: 10.00 }, 'gpt-4o-audio-preview-2024-10-01': { input: 2.50, output: 10.00 }, 'gpt-4o-realtime-preview': { input: 5.00, output: 20.00 }, 'gpt-4o-realtime-preview-2024-10-01': { input: 5.00, output: 20.00 }, 'gpt-4o-mini': { input: 0.15, output: 0.60 }, 'gpt-4o-mini-2024-07-18': { input: 0.15, output: 0.60 }, 'gpt-4o-mini-audio-preview': { input: 0.15, output: 0.60 }, 'gpt-4o-mini-audio-preview-2024-12-17': { input: 0.15, output: 0.60 }, 'gpt-4o-mini-realtime-preview': { input: 0.60, output: 2.40 }, 'gpt-4o-mini-realtime-preview-2024-12-17': { input: 0.60, output: 2.40 }, // o1 models 'o1': { input: 15.00, output: 60.00 }, 'o1-2024-12-17': { input: 15.00, output: 60.00 }, 'o1-pro': { input: 150.00, output: 600.00 }, 'o1-pro-2024-12-17': { input: 150.00, output: 600.00 }, 'o3-pro': { input: 200.00, output: 800.00 }, 'o3-pro-2025-01-31': { input: 200.00, output: 800.00 }, 'o3': { input: 2.00, output: 8.00 }, 'o3-2025-01-31': { input: 2.00, output: 8.00 }, 'o3-deep-research': { input: 10.00, output: 40.00 }, 'o3-deep-research-2025-01-31': { input: 10.00, output: 40.00 }, 'o4-mini': { input: 1.10, output: 4.40 }, 'o4-mini-2025-01-31': { input: 1.10, output: 4.40 }, 'o4-mini-deep-research': { input: 2.00, output: 8.00 }, 'o4-mini-deep-research-2025-01-31': { input: 2.00, output: 8.00 }, 'o5-mini': { input: 1.10, output: 4.40 }, 'o5-mini-2025-01-31': { input: 1.10, output: 4.40 }, 'o1-mini': { input: 1.10, output: 4.40 }, 'o1-mini-2024-09-12': { input: 1.10, output: 4.40 }, // Codex models 'codex-mini-latest': { input: 1.50, output: 6.00 }, // Search models 'gpt-4o-mini-search-preview': { input: 0.15, output: 0.60 }, 'gpt-4o-mini-search-preview-2025-01-11': { input: 0.15, output: 0.60 }, 'gpt-4o-search-preview': { input: 2.50, output: 10.00 }, 'gpt-4o-search-preview-2025-01-11': { input: 2.50, output: 10.00 }, // Computer use models 'computer-use-preview': { input: 3.00, output: 12.00 }, 'computer-use-preview-2025-01-31': { input: 3.00, output: 12.00 }, // Image models 'gpt-image-1': { input: 5.00, output: 0 }, // Image models typically don't have output tokens // Legacy GPT-3.5 models 'gpt-3.5-turbo': { input: 0.50, output: 1.50 }, 'gpt-3.5-turbo-0125': { input: 0.50, output: 1.50 }, 'gpt-3.5-turbo-1106': { input: 1.00, output: 2.00 }, 'gpt-3.5-turbo-0613': { input: 1.50, output: 2.00 }, 'gpt-3.5-turbo-16k': { input: 3.00, output: 4.00 }, 'gpt-3.5-turbo-16k-0613': { input: 3.00, output: 4.00 }, }; class OpenAIResponseProcessor { static processStandardResponse(response, model) { const content = response.choices[0]?.message?.content; if (!content) { throw new Error('No response from OpenAI'); } const inputTokens = response.usage.prompt_tokens; const outputTokens = response.usage.completion_tokens; const costUsd = OpenAICostCalculator.calculateCost(model, inputTokens, outputTokens); return { content, inputTokens, outputTokens, thinkingTokens: response.usage.thinking_tokens ?? 0, costUsd, }; } } class OpenAIAdapter { constructor(apiKey) { this.name = 'openai'; this.validateApiKey(apiKey); this.client = this.initializeClient(apiKey); } supportsModel(model) { const patterns = [ /^gpt-/i, // gpt-4, gpt-4-turbo, gpt-3.5-turbo, etc. /^o\d+/i, // o1, o3, o4-mini, etc. ]; return patterns.some((pattern) => pattern.test(model)); } async generate(config) { try { const requestParams = OpenAIRequestBuilder.build(config); const useStreaming = OpenAIStreamingStrategy.shouldUseStreaming(config); const result = useStreaming ? await this.generateWithStreaming(requestParams) : await this.generateStandard(requestParams); return this.buildResult(result, config.model); } catch (error) { throw new Error(`OpenAI generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } validateApiKey(apiKey) { if (!apiKey && !process.env.OPENAI_API_KEY) { throw new Error('OpenAI API key is required. Provide it as parameter or set OPENAI_API_KEY environment variable.'); } } initializeClient(apiKey) { try { const OpenAI = require('openai'); return new OpenAI({ apiKey: apiKey || process.env.OPENAI_API_KEY, }); } catch (error) { throw new Error('OpenAI package not found. Install it with: npm install openai'); } } async generateWithStreaming(params) { const stream = await this.client.chat.completions.create({ ...params, stream: true, }); const processor = new OpenAIStreamProcessor(params.model); return processor.processStream(stream); } async generateStandard(params) { const response = await this.client.chat.completions.create(params); return OpenAIResponseProcessor.processStandardResponse(response, params.model); } buildResult(result, model) { return { content: result.content, provider: this.name, model, inputTokens: result.inputTokens, outputTokens: result.outputTokens, thinkingTokens: result.thinkingTokens, costUsd: result.costUsd, }; } } exports.OpenAIAdapter = OpenAIAdapter; //# sourceMappingURL=openai.js.map