llmforge
Version:
One API, every AI model, instant switching. Change from GPT-4 to Gemini to local models with a single config update. LLMForge is the lightweight, TypeScript-first solution for multi-provider AI applications with zero vendor lock-in.
249 lines • 10.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAIClient = void 0;
const http_client_1 = require("../core/http-client");
const expo_1 = require("../strategies/retry/expo");
const ai_builder_1 = require("../builder/ai.builder");
const openai_stream_1 = require("../strategies/stream/openai.stream");
const logger_1 = require("../utils/logger");
const comman_1 = require("./comman");
class OpenAIClient {
constructor(config) {
var _a, _b, _c, _d, _e;
this.retryHandler = new expo_1.RetryHandler({
maxRetries: (_b = (_a = config.retryConfig) === null || _a === void 0 ? void 0 : _a.maxRetries) !== null && _b !== void 0 ? _b : 1,
retryDelay: (_d = (_c = config.retryConfig) === null || _c === void 0 ? void 0 : _c.retryDelay) !== null && _d !== void 0 ? _d : 0,
});
this.httpClient = new http_client_1.HttpClient(config, (_e = config.baseUrl) !== null && _e !== void 0 ? _e : 'https://api.openai.com', this.retryHandler);
this.streamProcessor = new openai_stream_1.OpenAIStreamProcessor();
}
convertToOpenAIRequest(request) {
var _a;
const openAIRequest = {
model: this.httpClient.getConfig().model,
input: comman_1.CommanMethods.omitModelToAssistantInContents(request.contents),
text: {
format: {
type: 'text',
},
},
};
if (request.generationConfig) {
const config = request.generationConfig;
if (config.temperature !== undefined)
openAIRequest.temperature = config.temperature;
if (config.maxOutputTokens !== undefined)
openAIRequest.max_tokens = config.maxOutputTokens;
if (config.topP !== undefined)
openAIRequest.top_p = config.topP;
if (config.stopSequences !== undefined)
openAIRequest.stop = config.stopSequences;
// Handle thinking/reasoning config
if (((_a = config.thinkingConfig) === null || _a === void 0 ? void 0 : _a.thinkingBudget) !== undefined) {
const budget = config.thinkingConfig.thinkingBudget;
openAIRequest.reasoning = {
effort: budget === -1 ? 'medium' : budget > 1000 ? 'high' : 'low',
};
}
// Handle JSON schema format
if (config.responseMimeType === 'application/json') {
openAIRequest.text = {
format: {
type: 'json_schema',
name: 'response_format',
strict: true,
schema: {
type: 'object',
properties: {
content: { type: 'string' },
},
required: ['content'],
additionalProperties: false,
},
},
};
}
}
// Handle tools
if (request.tools && request.tools.length > 0) {
openAIRequest.tools = this.convertToolsToOpenAI(request.tools);
}
// Handle system instruction
if (request.systemInstruction) {
const systemMessage = {
role: 'system',
content: request.systemInstruction.parts.map((part) => part.text).join('\n'),
};
if (openAIRequest.messages) {
openAIRequest.messages.unshift(systemMessage);
}
else {
openAIRequest.messages = [systemMessage];
}
}
return openAIRequest;
}
convertToolsToOpenAI(tools) {
return tools.flatMap(tool => {
if (tool.functionDeclarations) {
return tool.functionDeclarations.map((func) => ({
type: 'function',
function: {
name: func.name,
description: func.description,
parameters: func.parameters,
},
}));
}
return [];
});
}
convertFromOpenAIResponse(response) {
var _a, _b, _c;
logger_1.logger.info('OpenAI Response:', JSON.stringify(response, null, 2));
logger_1.logger.info('OpenAI Response ID:', JSON.stringify((_a = response.output[0]) === null || _a === void 0 ? void 0 : _a.id, null, 2) || 'N/A');
logger_1.logger.info('OpenAI Response Output:', ((_b = response.output[0]) === null || _b === void 0 ? void 0 : _b.content[0].text) || 'N/A');
return {
resp_id: response.id,
output: ((_c = response.output[0]) === null || _c === void 0 ? void 0 : _c.content[0].text) || '',
status: (response === null || response === void 0 ? void 0 : response.status) == 'completed' ? 'success' : (response === null || response === void 0 ? void 0 : response.status) || 'unknown',
created_at: response.created_at,
model: response.model,
usage: response.usage
? {
input_tokens: response.usage.input_tokens,
output_tokens: response.usage.output_tokens,
total_tokens: response.usage.total_tokens,
}
: undefined,
};
}
async generateContent(request) {
ai_builder_1.MessageValidator.validateContents(request.contents);
logger_1.logger.info('Static generation');
const openAIRequest = this.convertToOpenAIRequest(request);
logger_1.logger.info('OpenAI Request:', JSON.stringify(openAIRequest, null, 2));
const response = await this.httpClient.request({
method: 'POST',
body: JSON.stringify(openAIRequest),
headers: {
Authorization: `Bearer ${this.httpClient.getConfig().apiKey}`,
'Content-Type': 'application/json',
},
}, false, `/v1/responses`);
return this.convertFromOpenAIResponse(response);
}
async *generateContentStreamAsync(request) {
logger_1.logger.info('Generating content stream asynchronously for request: %j', request);
try {
ai_builder_1.MessageValidator.validateContents(request.contents);
logger_1.logger.info('Streaming generation (async)');
const openAIRequest = { ...this.convertToOpenAIRequest(request), stream: true };
const response = await this.httpClient.streamRequest({
method: 'POST',
body: JSON.stringify(openAIRequest),
headers: {
Authorization: `Bearer ${this.httpClient.getConfig().apiKey}`,
'Content-Type': 'application/json',
},
}, '/v1/responses');
yield* this.streamProcessor.createAsyncGenerator(comman_1.CommanMethods.convertReadableToResponse(response));
}
catch (error) {
console.error('Error in generateContentStreamAsync:', error);
throw error;
}
}
async chat(message, options = {}) {
const contents = ai_builder_1.ContentBuilder.textOnly(message);
const request = {
contents,
generationConfig: options.generationConfig,
tools: options.tools,
};
if (options.systemInstruction) {
request.systemInstruction = {
parts: [{ text: options.systemInstruction }],
};
}
return this.generateContent(request);
}
async continueConversation(contents, options = {}) {
const request = {
contents,
generationConfig: options.generationConfig,
tools: options.tools,
};
return this.generateContent(request);
}
async callFunction(message, tools, options = {}) {
const request = {
contents: ai_builder_1.ContentBuilder.textOnly(message),
tools,
generationConfig: options.generationConfig,
};
if (options.systemInstruction) {
request.systemInstruction = {
parts: [{ text: options.systemInstruction }],
};
}
return this.generateContent(request);
}
// OpenAI-specific thinking/reasoning mode
// async generateWithThinking(
// message: string,
// thinkingBudget: number = -1,
// options: {
// systemInstruction?: string;
// onThinking?: (thinking: string) => void;
// effort?: 'low' | 'medium' | 'high';
// jsonSchema?: any;
// } = {}
// ): Promise<GenerateContentResponse> {
// const request: GenerateContentRequest = {
// contents: ContentBuilder.textOnly(message),
// generationConfig: {
// thinkingConfig: {
// thinkingBudget,
// },
// },
// };
// if (options.effort) {
// request.generationConfig!.thinkingConfig = {
// thinkingBudget: options.effort === 'high' ? 2000 : options.effort === 'low' ? 100 : 1000,
// };
// }
// if (options.jsonSchema) {
// request.generationConfig!.responseMimeType = 'application/json';
// }
// if (options.systemInstruction) {
// request.systemInstruction = {
// parts: [{ text: options.systemInstruction }],
// };
// }
// return this.generateContentStream(request, {
// onThinking: options.onThinking,
// });
// }
async handleOpenAIRequest(openAIRequest) {
return this.httpClient.request({
method: 'POST',
body: JSON.stringify(openAIRequest),
headers: {
Authorization: `Bearer ${this.httpClient.getConfig().apiKey}`,
'Content-Type': 'application/json',
},
}, false, `/v1/responses`);
}
get stream() {
return this.streamProcessor;
}
updateConfig(newConfig) {
this.httpClient.updateConfig(newConfig);
}
getConfig() {
return this.httpClient.getConfig();
}
}
exports.OpenAIClient = OpenAIClient;
//# sourceMappingURL=openai.adapter.js.map