const { CacheKeys, ErrorTypes, envVarRegex, EModelEndpoint, FetchTokenConfig, extractEnvVariable, } = require('librechat-data-provider'); const { getUserKeyValues, checkUserKeyExpiry } = require('~/server/services/UserService'); const getCustomConfig = require('~/server/services/Config/getCustomConfig'); const { fetchModels } = require('~/server/services/ModelService'); const getLogStores = require('~/cache/getLogStores'); const { isUserProvided } = require('~/server/utils'); const { OpenAIClient } = require('~/app'); const { PROXY } = process.env; const initializeClient = async ({ req, res, endpointOption }) => { const { key: expiresAt, endpoint } = req.body; const customConfig = await getCustomConfig(); if (!customConfig) { throw new Error(`Config not found for the ${endpoint} custom endpoint.`); } const { endpoints = {} } = customConfig; const customEndpoints = endpoints[EModelEndpoint.custom] ?? []; const endpointConfig = customEndpoints.find((endpointConfig) => endpointConfig.name === endpoint); const CUSTOM_API_KEY = extractEnvVariable(endpointConfig.apiKey); const CUSTOM_BASE_URL = extractEnvVariable(endpointConfig.baseURL); let resolvedHeaders = {}; if (endpointConfig.headers && typeof endpointConfig.headers === 'object') { Object.keys(endpointConfig.headers).forEach((key) => { resolvedHeaders[key] = extractEnvVariable(endpointConfig.headers[key]); }); } if (CUSTOM_API_KEY.match(envVarRegex)) { throw new Error(`Missing API Key for ${endpoint}.`); } if (CUSTOM_BASE_URL.match(envVarRegex)) { throw new Error(`Missing Base URL for ${endpoint}.`); } const userProvidesKey = isUserProvided(CUSTOM_API_KEY); const userProvidesURL = isUserProvided(CUSTOM_BASE_URL); let userValues = null; if (expiresAt && (userProvidesKey || userProvidesURL)) { checkUserKeyExpiry(expiresAt, endpoint); userValues = await getUserKeyValues({ userId: req.user.id, name: endpoint }); } let apiKey = userProvidesKey ? userValues?.apiKey : CUSTOM_API_KEY; let baseURL = userProvidesURL ? userValues?.baseURL : CUSTOM_BASE_URL; if (userProvidesKey & !apiKey) { throw new Error( JSON.stringify({ type: ErrorTypes.NO_USER_KEY, }), ); } if (userProvidesURL && !baseURL) { throw new Error( JSON.stringify({ type: ErrorTypes.NO_BASE_URL, }), ); } if (!apiKey) { throw new Error(`${endpoint} API key not provided.`); } if (!baseURL) { throw new Error(`${endpoint} Base URL not provided.`); } const cache = getLogStores(CacheKeys.TOKEN_CONFIG); const tokenKey = !endpointConfig.tokenConfig && (userProvidesKey || userProvidesURL) ? `${endpoint}:${req.user.id}` : endpoint; let endpointTokenConfig = !endpointConfig.tokenConfig && FetchTokenConfig[endpoint.toLowerCase()] && (await cache.get(tokenKey)); if ( FetchTokenConfig[endpoint.toLowerCase()] && endpointConfig && endpointConfig.models.fetch && !endpointTokenConfig ) { await fetchModels({ apiKey, baseURL, name: endpoint, user: req.user.id, tokenKey }); endpointTokenConfig = await cache.get(tokenKey); } const customOptions = { headers: resolvedHeaders, addParams: endpointConfig.addParams, dropParams: endpointConfig.dropParams, titleConvo: endpointConfig.titleConvo, titleModel: endpointConfig.titleModel, forcePrompt: endpointConfig.forcePrompt, summaryModel: endpointConfig.summaryModel, modelDisplayLabel: endpointConfig.modelDisplayLabel, titleMethod: endpointConfig.titleMethod ?? 'completion', contextStrategy: endpointConfig.summarize ? 'summarize' : null, directEndpoint: endpointConfig.directEndpoint, titleMessageRole: endpointConfig.titleMessageRole, endpointTokenConfig, }; const clientOptions = { reverseProxyUrl: baseURL ?? null, proxy: PROXY ?? null, req, res, ...customOptions, ...endpointOption, }; const client = new OpenAIClient(apiKey, clientOptions); return { client, openAIApiKey: apiKey, }; }; module.exports = initializeClient;