UNPKG

jorel

Version:

The easiest way to use LLMs, including streams, images, documents, tools and various agent scenarios.

199 lines (198 loc) 8.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAIProvider = void 0; const openai_1 = require("openai"); const providers_1 = require("../../providers"); const shared_1 = require("../../shared"); const tools_1 = require("../../tools"); const convert_inputs_1 = require("./convert-inputs"); const convert_llm_message_1 = require("./convert-llm-message"); /** Provides access to OpenAI and other compatible services */ class OpenAIProvider { constructor(options = {}) { if (options.azure) { this.name = options.name || OpenAIProvider.defaultName + "-azure"; this.client = new openai_1.AzureOpenAI({ endpoint: options.apiUrl || process.env.AZURE_OPENAI_ENDPOINT, apiKey: options.apiKey || process.env.AZURE_OPENAI_API_KEY, apiVersion: options.apiVersion || process.env.AZURE_OPENAI_API_VERSION || "2024-12-01-preview", maxRetries: options.maxRetries || 3, timeout: options.timeout, }); this.isAzure = true; } else { this.name = options.name || OpenAIProvider.defaultName; this.client = new openai_1.OpenAI({ apiKey: options.apiKey || process.env.OPENAI_API_KEY, baseURL: options.apiUrl || process.env.OPENAI_API_URL, maxRetries: options.maxRetries, timeout: options.timeout, }); this.isAzure = false; } } async generateResponse(model, messages, config = {}) { const start = Date.now(); const temperature = config.temperature ?? undefined; const response = await this.client.chat.completions.create({ model, messages: await (0, convert_llm_message_1.convertLlmMessagesToOpenAiMessages)(messages), temperature, response_format: (0, convert_inputs_1.jsonResponseToOpenAi)(config.json, config.jsonDescription), max_tokens: config.maxTokens, parallel_tool_calls: config.tools && config.tools.hasTools ? config.tools.allowParallelCalls : undefined, tool_choice: (0, convert_inputs_1.toolChoiceToOpenAi)(config.toolChoice), tools: config.tools?.asLlmFunctions, reasoning_effort: config.reasoningEffort, verbosity: config.verbosity, }); const durationMs = Date.now() - start; const inputTokens = response.usage?.prompt_tokens; const outputTokens = response.usage?.completion_tokens; const message = response.choices[0].message; const toolCalls = message.tool_calls?.map((call) => { if (call.type === "custom") { throw new Error(`Unsupported tool call type: ${call.type}`); } return { id: (0, shared_1.generateUniqueId)(), request: { id: call.id, function: { name: call.function.name, arguments: tools_1.LlmToolKit.deserialize(call.function.arguments), }, }, approvalState: config.tools?.getTool(call.function.name)?.requiresConfirmation ? "requiresApproval" : "noApprovalRequired", executionState: "pending", result: null, error: null, }; }); const provider = this.name; return { ...(0, providers_1.generateAssistantMessage)(message.content, toolCalls), meta: { model, provider, temperature, durationMs, inputTokens, outputTokens, }, }; } async *generateResponseStream(model, messages, config = {}) { const start = Date.now(); const temperature = config.temperature ?? undefined; const response = await this.client.chat.completions.create({ model, messages: await (0, convert_llm_message_1.convertLlmMessagesToOpenAiMessages)(messages), temperature, response_format: (0, convert_inputs_1.jsonResponseToOpenAi)(config.json, config.jsonDescription), max_tokens: config.maxTokens, stream: true, tools: config.tools?.asLlmFunctions, parallel_tool_calls: config.tools && config.tools.hasTools ? config.tools.allowParallelCalls : undefined, tool_choice: (0, convert_inputs_1.toolChoiceToOpenAi)(config.toolChoice), stream_options: { include_usage: true, }, reasoning_effort: config.reasoningEffort, verbosity: config.verbosity, }); let inputTokens; let outputTokens; const _toolCalls = []; let content = ""; for await (const chunk of response) { const delta = (0, shared_1.firstEntry)(chunk.choices)?.delta; if (delta?.content) { content += delta.content; yield { type: "chunk", content: delta.content }; } if (delta?.tool_calls) { for (const toolCall of delta.tool_calls) { const _toolCall = _toolCalls[toolCall.index] || { id: "", function: { name: "", arguments: "" } }; if (toolCall.id) _toolCall.id += toolCall.id; if (toolCall.function) { if (toolCall.function.name) _toolCall.function.name += toolCall.function.name; if (toolCall.function.arguments) _toolCall.function.arguments += toolCall.function.arguments; } _toolCalls[toolCall.index] = _toolCall; } } if (chunk.usage) { inputTokens = chunk.usage?.prompt_tokens; outputTokens = chunk.usage?.completion_tokens; } } const durationMs = Date.now() - start; const provider = this.name; const toolCalls = _toolCalls.map((call) => { return { id: (0, shared_1.generateUniqueId)(), request: { id: call.id, function: { name: call.function.name, arguments: tools_1.LlmToolKit.deserialize(call.function.arguments), }, }, approvalState: config.tools?.getTool(call.function.name)?.requiresConfirmation ? "requiresApproval" : "noApprovalRequired", executionState: "pending", result: null, error: null, }; }); const meta = { model, provider, temperature, durationMs, inputTokens, outputTokens, }; if (_toolCalls.length > 0) { yield { type: "response", role: "assistant_with_tools", content, toolCalls, meta, }; } else { yield { type: "response", role: "assistant", content, meta, }; } } async getAvailableModels() { const models = await this.client.models.list(); return models.data.map((model) => model.id); } async createEmbedding(model, text) { const response = await this.client.embeddings.create({ model, input: text, }); if (!response || !response.data || !response.data || response.data.length === 0) { throw new Error("Failed to create embedding"); } return response.data[0].embedding; } } exports.OpenAIProvider = OpenAIProvider; OpenAIProvider.defaultName = "openai";