UNPKG

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.

411 lines 15.3 kB
"use strict"; 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"); class OpenAIClient { constructor(config) { var _a; this.retryHandler = new expo_1.RetryHandler({ maxRetries: config.maxRetries, retryDelay: config.retryDelay, }); this.httpClient = new http_client_1.HttpClient(config, (_a = config.baseUrl) !== null && _a !== void 0 ? _a : 'https://api.openai.com', this.retryHandler); this.streamProcessor = new openai_stream_1.OpenAIStreamProcessor(); } convertReadableToResponse(readable) { return new Response(readable, { headers: new Headers(), status: 200, statusText: 'OK' }); } convertToOpenAIRequest(request) { var _a; const openAIRequest = { model: this.httpClient.getConfig().model, messages: this.convertContentsToMessages(request.contents), }; // Handle generation config 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; } convertContentsToMessages(contents) { return contents.map(content => { const role = content.role === 'model' ? 'assistant' : (content.role || 'user'); const message = { role: role, content: content.parts.map((part) => { if ('text' in part) { return part.text; } // Handle other part types if needed return ''; }).join('\n') }; return message; }); } 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 []; }); } // Convert OpenAI response to Gemini format convertFromOpenAIResponse(response) { const candidates = response.choices.map((choice) => ({ content: { role: 'model', parts: [{ text: choice.message.content }] }, finishReason: choice.finish_reason, index: choice.index })); return { candidates, usageMetadata: response.usage ? { promptTokenCount: response.usage.prompt_tokens, candidatesTokenCount: response.usage.completion_tokens, totalTokenCount: response.usage.total_tokens } : undefined }; } async generateContent(request) { ai_builder_1.MessageValidator.validateContents(request.contents); const openAIRequest = this.convertToOpenAIRequest(request); // Determine endpoint based on model and features let endpoint = '/v1/chat/completions'; if (openAIRequest.reasoning) { endpoint = '/v1/responses'; // For reasoning models openAIRequest.input = openAIRequest.messages; delete openAIRequest.messages; } const response = await this.httpClient.request(endpoint, { method: 'POST', body: JSON.stringify(openAIRequest), headers: { 'Authorization': `Bearer ${this.httpClient.getConfig().apiKey}`, 'Content-Type': 'application/json' } }); return this.convertFromOpenAIResponse(response); } async generateContentStream(request, options) { ai_builder_1.MessageValidator.validateContents(request.contents); const openAIRequest = { ...this.convertToOpenAIRequest(request), stream: true }; let endpoint = '/v1/chat/completions'; if (openAIRequest.reasoning) { endpoint = '/v1/responses'; openAIRequest.input = openAIRequest.messages; delete openAIRequest.messages; } const response = await this.httpClient.streamRequest(endpoint, { method: 'POST', body: JSON.stringify(openAIRequest), headers: { 'Authorization': `Bearer ${this.httpClient.getConfig().apiKey}`, 'Content-Type': 'application/json' } }); return this.streamProcessor.processStream(this.convertReadableToResponse(response), options); } async *generateContentStreamAsync(request) { ai_builder_1.MessageValidator.validateContents(request.contents); const openAIRequest = { ...this.convertToOpenAIRequest(request), stream: true }; let endpoint = '/v1/chat/completions'; if (openAIRequest.reasoning) { endpoint = '/v1/responses'; openAIRequest.input = openAIRequest.messages; delete openAIRequest.messages; } const response = await this.httpClient.streamRequest(endpoint, { method: 'POST', body: JSON.stringify(openAIRequest), headers: { 'Authorization': `Bearer ${this.httpClient.getConfig().apiKey}`, 'Content-Type': 'application/json' } }); yield* this.streamProcessor.createAsyncGenerator(this.convertReadableToResponse(response)); } 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 chatStream(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.generateContentStream(request, options.streamOptions); } async continueConversation(contents, options = {}) { const request = { contents, generationConfig: options.generationConfig, tools: options.tools, }; return this.generateContent(request); } async analyzeImage(base64Image, mimeType, prompt, options = {}) { const contents = ai_builder_1.ContentBuilder.create() .addImageFromBase64(base64Image, mimeType, prompt) .build(); return this.generateContent({ contents, generationConfig: options.generationConfig, }); } async analyzeDocument(base64Document, mimeType, prompt, options = {}) { const request = { contents: ai_builder_1.ContentBuilder.create() .addDocumentFromBase64(base64Document, mimeType, prompt) .build(), generationConfig: options.generationConfig, }; 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, thinkingBudget = -1, options = {}) { const request = { contents: ai_builder_1.ContentBuilder.textOnly(message), generationConfig: { thinkingConfig: { thinkingBudget, }, }, }; // Override effort if provided if (options.effort) { request.generationConfig.thinkingConfig = { thinkingBudget: options.effort === 'high' ? 2000 : options.effort === 'low' ? 100 : 1000 }; } // Add JSON schema if provided if (options.jsonSchema) { request.generationConfig.responseMimeType = 'application/json'; } if (options.systemInstruction) { request.systemInstruction = { parts: [{ text: options.systemInstruction }], }; } return this.generateContentStream(request, { onThinking: options.onThinking, }); } // OpenAI-specific method for handling the exact curl request format async handleOpenAIRequest(openAIRequest) { let endpoint = '/v1/chat/completions'; // Handle reasoning endpoint if (openAIRequest.reasoning || openAIRequest.input) { endpoint = '/v1/responses'; } return this.httpClient.request(endpoint, { method: 'POST', body: JSON.stringify(openAIRequest), headers: { 'Authorization': `Bearer ${this.httpClient.getConfig().apiKey}`, 'Content-Type': 'application/json' } }); } get stream() { return this.streamProcessor; } updateConfig(newConfig) { this.httpClient.updateConfig(newConfig); } getConfig() { return this.httpClient.getConfig(); } } exports.OpenAIClient = OpenAIClient; // // Usage example for the exact curl request: // export class OpenAIRequestHandler { // private client: OpenAIClient; // constructor(config: Config) { // this.client = new OpenAIClient(config); // } // async handleCurlRequest(): Promise<OpenAIResponse> { // const request: OpenAIRequest = { // model: "o4-mini", // input: [ // { // role: "user", // content: [ // { // type: "input_text", // text: "hii there" // } // ] // }, // { // role: "assistant", // content: [ // { // type: "output_text", // text: "Hello! How can I help you today?" // } // ] // }, // { // role: "user", // content: [ // { // type: "input_text", // text: "what can you help with" // } // ] // }, // { // role: "assistant", // content: [ // { // type: "output_text", // text: "I can help with a wide range of tasks..." // } // ] // }, // { // role: "user", // content: [ // { // type: "input_text", // text: "hii there" // } // ] // } // ], // text: { // format: { // type: "json_schema", // name: "thinking_output", // strict: true, // schema: { // type: "object", // properties: { // thinking: { // type: "string", // description: "Description of the thought process or reasoning." // }, // final_output: { // type: "string", // description: "Final result or conclusion derived from the thinking process." // } // }, // required: ["thinking", "final_output"], // additionalProperties: false // } // } // }, // reasoning: { // effort: "medium" // }, // tools: [], // store: true // }; // return this.client.handleOpenAIRequest(request); // } // } // export * from '../types'; // export * from '../strategies/retry/expo'; // export * from '../core/http-client'; // export * from '../builder/ai.builder'; // export * from '../strategies/stream/openai.stream'; // export { OpenAIRequest, OpenAIResponse, OpenAIMessage } from './openai-types'; //# sourceMappingURL=openai.adapter.js.map