promptly-ai
Version:
A universal template-based prompt management system for LLM applications
249 lines • 10.3 kB
JavaScript
"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