mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
* wip: first pass for azure endpoint schema * refactor: azure config to return groupMap and modelConfigMap * wip: naming and schema changes * refactor(errorsToString): move to data-provider * feat: rename to azureGroups, add additional tests, tests all expected outcomes, return errors * feat(AppService): load Azure groups * refactor(azure): use imported types, write `mapModelToAzureConfig` * refactor: move `extractEnvVariable` to data-provider * refactor(validateAzureGroups): throw on duplicate groups or models; feat(mapModelToAzureConfig): throw if env vars not present, add tests * refactor(AppService): ensure each model is properly configured on startup * refactor: deprecate azureOpenAI environment variables in favor of librechat.yaml config * feat: use helper functions to handle and order enabled/default endpoints; initialize azureOpenAI from config file * refactor: redefine types as well as load azureOpenAI models from config file * chore(ci): fix test description naming * feat(azureOpenAI): use validated model grouping for request authentication * chore: bump data-provider following rebase * chore: bump config file version noting significant changes * feat: add title options and switch azure configs for titling and vision requests * feat: enable azure plugins from config file * fix(ci): pass tests * chore(.env.example): mark `PLUGINS_USE_AZURE` as deprecated * fix(fetchModels): early return if apiKey not passed * chore: fix azure config typing * refactor(mapModelToAzureConfig): return baseURL and headers as well as azureOptions * feat(createLLM): use `azureOpenAIBasePath` * feat(parsers): resolveHeaders * refactor(extractBaseURL): handle invalid input * feat(OpenAIClient): handle headers and baseURL for azureConfig * fix(ci): pass `OpenAIClient` tests * chore: extract env var for azureOpenAI group config, baseURL * docs: azureOpenAI config setup docs * feat: safe check of potential conflicting env vars that map to unique placeholders * fix: reset apiKey when model switches from originally requested model (vision or title) * chore: linting * docs: CONFIG_PATH notes in custom_config.md
110 lines
3.2 KiB
JavaScript
110 lines
3.2 KiB
JavaScript
const {
|
|
EModelEndpoint,
|
|
mapModelToAzureConfig,
|
|
resolveHeaders,
|
|
} = require('librechat-data-provider');
|
|
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
|
const { getAzureCredentials } = require('~/utils');
|
|
const { isEnabled } = require('~/server/utils');
|
|
const { PluginsClient } = require('~/app');
|
|
|
|
const initializeClient = async ({ req, res, endpointOption }) => {
|
|
const {
|
|
PROXY,
|
|
OPENAI_API_KEY,
|
|
AZURE_API_KEY,
|
|
PLUGINS_USE_AZURE,
|
|
OPENAI_REVERSE_PROXY,
|
|
AZURE_OPENAI_BASEURL,
|
|
OPENAI_SUMMARIZE,
|
|
DEBUG_PLUGINS,
|
|
} = process.env;
|
|
|
|
const { key: expiresAt, model: modelName } = req.body;
|
|
const contextStrategy = isEnabled(OPENAI_SUMMARIZE) ? 'summarize' : null;
|
|
|
|
let useAzure = isEnabled(PLUGINS_USE_AZURE);
|
|
let endpoint = useAzure ? EModelEndpoint.azureOpenAI : EModelEndpoint.openAI;
|
|
|
|
/** @type {false | TAzureConfig} */
|
|
const azureConfig = req.app.locals[EModelEndpoint.azureOpenAI];
|
|
useAzure = useAzure || azureConfig?.plugins;
|
|
|
|
if (useAzure && endpoint !== EModelEndpoint.azureOpenAI) {
|
|
endpoint = EModelEndpoint.azureOpenAI;
|
|
}
|
|
|
|
const baseURLOptions = {
|
|
[EModelEndpoint.openAI]: OPENAI_REVERSE_PROXY,
|
|
[EModelEndpoint.azureOpenAI]: AZURE_OPENAI_BASEURL,
|
|
};
|
|
|
|
const reverseProxyUrl = baseURLOptions[endpoint] ?? null;
|
|
|
|
const clientOptions = {
|
|
contextStrategy,
|
|
debug: isEnabled(DEBUG_PLUGINS),
|
|
reverseProxyUrl,
|
|
proxy: PROXY ?? null,
|
|
req,
|
|
res,
|
|
...endpointOption,
|
|
};
|
|
|
|
const credentials = {
|
|
[EModelEndpoint.openAI]: OPENAI_API_KEY,
|
|
[EModelEndpoint.azureOpenAI]: AZURE_API_KEY,
|
|
};
|
|
|
|
const isUserProvided = credentials[endpoint] === 'user_provided';
|
|
|
|
let userKey = null;
|
|
if (expiresAt && isUserProvided) {
|
|
checkUserKeyExpiry(
|
|
expiresAt,
|
|
'Your OpenAI API key has expired. Please provide your API key again.',
|
|
);
|
|
userKey = await getUserKey({
|
|
userId: req.user.id,
|
|
name: endpoint,
|
|
});
|
|
}
|
|
|
|
let apiKey = isUserProvided ? userKey : credentials[endpoint];
|
|
if (useAzure && azureConfig) {
|
|
const { modelGroupMap, groupMap } = azureConfig;
|
|
const {
|
|
azureOptions,
|
|
baseURL,
|
|
headers = {},
|
|
} = mapModelToAzureConfig({
|
|
modelName,
|
|
modelGroupMap,
|
|
groupMap,
|
|
});
|
|
clientOptions.azure = azureOptions;
|
|
clientOptions.titleConvo = azureConfig.titleConvo;
|
|
clientOptions.titleModel = azureConfig.titleModel;
|
|
clientOptions.titleMethod = azureConfig.titleMethod ?? 'completion';
|
|
clientOptions.reverseProxyUrl = baseURL ?? clientOptions.reverseProxyUrl;
|
|
clientOptions.headers = resolveHeaders({ ...headers, ...(clientOptions.headers ?? {}) });
|
|
|
|
apiKey = clientOptions.azure.azureOpenAIApiKey;
|
|
} else if (useAzure || (apiKey && apiKey.includes('{"azure') && !clientOptions.azure)) {
|
|
clientOptions.azure = isUserProvided ? JSON.parse(userKey) : getAzureCredentials();
|
|
apiKey = clientOptions.azure.azureOpenAIApiKey;
|
|
}
|
|
|
|
if (!apiKey) {
|
|
throw new Error(`${endpoint} API key not provided.`);
|
|
}
|
|
|
|
const client = new PluginsClient(apiKey, clientOptions);
|
|
return {
|
|
client,
|
|
azure: clientOptions.azure,
|
|
openAIApiKey: apiKey,
|
|
};
|
|
};
|
|
|
|
module.exports = initializeClient;
|