LibreChat/api/server/services/Config/loadConfigModels.js
Danny Avila 2e519f9b57
🤖 feat: Custom Endpoint Agents (experimental) (#4627)
* wip: first pass, custom endpoint agents

* chore: imports

* chore: consolidate exports

* fix: imports

* feat: convert message.content array to strings for legacy format handling (deepseek/groq)

* refactor: normalize ollama endpoint name

* refactor: update mocking in isDomainAllowed.spec.js

* refactor: update deepseekModels in tokens.js and tokens.spec.js
2024-11-04 12:59:04 -05:00

121 lines
3.7 KiB
JavaScript

const { Providers } = require('@librechat/agents');
const { EModelEndpoint, extractEnvVariable } = require('librechat-data-provider');
const { fetchModels } = require('~/server/services/ModelService');
const { getCustomConfig } = require('./getCustomConfig');
const { isUserProvided } = require('~/server/utils');
/**
* @param {string} name
* @returns {string}
*/
function normalizeEndpointName(name = '') {
return name.toLowerCase() === Providers.OLLAMA ? Providers.OLLAMA : name;
}
/**
* Load config endpoints from the cached configuration object
* @function loadConfigModels
* @param {Express.Request} req - The Express request object.
*/
async function loadConfigModels(req) {
const customConfig = await getCustomConfig();
if (!customConfig) {
return {};
}
const { endpoints = {} } = customConfig ?? {};
const modelsConfig = {};
const azureEndpoint = endpoints[EModelEndpoint.azureOpenAI];
const azureConfig = req.app.locals[EModelEndpoint.azureOpenAI];
const { modelNames } = azureConfig ?? {};
if (modelNames && azureEndpoint) {
modelsConfig[EModelEndpoint.azureOpenAI] = modelNames;
}
if (modelNames && azureEndpoint && azureEndpoint.plugins) {
modelsConfig[EModelEndpoint.gptPlugins] = modelNames;
}
if (azureEndpoint?.assistants && azureConfig.assistantModels) {
modelsConfig[EModelEndpoint.azureAssistants] = azureConfig.assistantModels;
}
if (!Array.isArray(endpoints[EModelEndpoint.custom])) {
return modelsConfig;
}
const customEndpoints = endpoints[EModelEndpoint.custom].filter(
(endpoint) =>
endpoint.baseURL &&
endpoint.apiKey &&
endpoint.name &&
endpoint.models &&
(endpoint.models.fetch || endpoint.models.default),
);
/**
* @type {Record<string, string[]>}
* Map for promises keyed by unique combination of baseURL and apiKey */
const fetchPromisesMap = {};
/**
* @type {Record<string, string[]>}
* Map to associate unique keys with endpoint names; note: one key may can correspond to multiple endpoints */
const uniqueKeyToEndpointsMap = {};
/**
* @type {Record<string, Partial<TEndpoint>>}
* Map to associate endpoint names to their configurations */
const endpointsMap = {};
for (let i = 0; i < customEndpoints.length; i++) {
const endpoint = customEndpoints[i];
const { models, name: configName, baseURL, apiKey } = endpoint;
const name = normalizeEndpointName(configName);
endpointsMap[name] = endpoint;
const API_KEY = extractEnvVariable(apiKey);
const BASE_URL = extractEnvVariable(baseURL);
const uniqueKey = `${BASE_URL}__${API_KEY}`;
modelsConfig[name] = [];
if (models.fetch && !isUserProvided(API_KEY) && !isUserProvided(BASE_URL)) {
fetchPromisesMap[uniqueKey] =
fetchPromisesMap[uniqueKey] ||
fetchModels({
user: req.user.id,
baseURL: BASE_URL,
apiKey: API_KEY,
name,
userIdQuery: models.userIdQuery,
});
uniqueKeyToEndpointsMap[uniqueKey] = uniqueKeyToEndpointsMap[uniqueKey] || [];
uniqueKeyToEndpointsMap[uniqueKey].push(name);
continue;
}
if (Array.isArray(models.default)) {
modelsConfig[name] = models.default;
}
}
const fetchedData = await Promise.all(Object.values(fetchPromisesMap));
const uniqueKeys = Object.keys(fetchPromisesMap);
for (let i = 0; i < fetchedData.length; i++) {
const currentKey = uniqueKeys[i];
const modelData = fetchedData[i];
const associatedNames = uniqueKeyToEndpointsMap[currentKey];
for (const name of associatedNames) {
const endpoint = endpointsMap[name];
modelsConfig[name] = !modelData?.length ? endpoint.models.default ?? [] : modelData;
}
}
return modelsConfig;
}
module.exports = loadConfigModels;