mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🧩 feat: Support Alternate API Keys for Plugins (#1760)
* refactor(DALL-E): retrieve env variables at runtime and not from memory * feat(plugins): add alternate env variable handling to allow setting one api key for multiple plugins * docs: update docs
This commit is contained in:
parent
927ce5395b
commit
39caeb2027
10 changed files with 328 additions and 113 deletions
|
|
@ -30,6 +30,14 @@ const getOpenAIKey = async (options, user) => {
|
|||
return openAIApiKey || (await getUserPluginAuthValue(user, 'OPENAI_API_KEY'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the availability and authentication of tools for a user based on environment variables or user-specific plugin authentication values.
|
||||
* Tools without required authentication or with valid authentication are considered valid.
|
||||
*
|
||||
* @param {Object} user The user object for whom to validate tool access.
|
||||
* @param {Array<string>} tools An array of tool identifiers to validate. Defaults to an empty array.
|
||||
* @returns {Promise<Array<string>>} A promise that resolves to an array of valid tool identifiers.
|
||||
*/
|
||||
const validateTools = async (user, tools = []) => {
|
||||
try {
|
||||
const validToolsSet = new Set(tools);
|
||||
|
|
@ -37,16 +45,34 @@ const validateTools = async (user, tools = []) => {
|
|||
validToolsSet.has(tool.pluginKey),
|
||||
);
|
||||
|
||||
/**
|
||||
* Validates the credentials for a given auth field or set of alternate auth fields for a tool.
|
||||
* If valid admin or user authentication is found, the function returns early. Otherwise, it removes the tool from the set of valid tools.
|
||||
*
|
||||
* @param {string} authField The authentication field or fields (separated by "||" for alternates) to validate.
|
||||
* @param {string} toolName The identifier of the tool being validated.
|
||||
*/
|
||||
const validateCredentials = async (authField, toolName) => {
|
||||
const adminAuth = process.env[authField];
|
||||
if (adminAuth && adminAuth.length > 0) {
|
||||
return;
|
||||
const fields = authField.split('||');
|
||||
for (const field of fields) {
|
||||
const adminAuth = process.env[field];
|
||||
if (adminAuth && adminAuth.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userAuth = null;
|
||||
try {
|
||||
userAuth = await getUserPluginAuthValue(user, field);
|
||||
} catch (err) {
|
||||
if (field === fields[fields.length - 1] && !userAuth) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (userAuth && userAuth.length > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const userAuth = await getUserPluginAuthValue(user, authField);
|
||||
if (userAuth && userAuth.length > 0) {
|
||||
return;
|
||||
}
|
||||
validToolsSet.delete(toolName);
|
||||
};
|
||||
|
||||
|
|
@ -63,20 +89,55 @@ const validateTools = async (user, tools = []) => {
|
|||
return Array.from(validToolsSet.values());
|
||||
} catch (err) {
|
||||
logger.error('[validateTools] There was a problem validating tools', err);
|
||||
throw new Error(err);
|
||||
throw new Error('There was a problem validating tools');
|
||||
}
|
||||
};
|
||||
|
||||
const loadToolWithAuth = async (userId, authFields, ToolConstructor, options = {}) => {
|
||||
/**
|
||||
* Initializes a tool with authentication values for the given user, supporting alternate authentication fields.
|
||||
* Authentication fields can have alternates separated by "||", and the first defined variable will be used.
|
||||
*
|
||||
* @param {string} userId The user ID for which the tool is being loaded.
|
||||
* @param {Array<string>} authFields Array of strings representing the authentication fields. Supports alternate fields delimited by "||".
|
||||
* @param {typeof import('langchain/tools').Tool} ToolConstructor The constructor function for the tool to be initialized.
|
||||
* @param {Object} options Optional parameters to be passed to the tool constructor alongside authentication values.
|
||||
* @returns {Function} An Async function that, when called, asynchronously initializes and returns an instance of the tool with authentication.
|
||||
*/
|
||||
const loadToolWithAuth = (userId, authFields, ToolConstructor, options = {}) => {
|
||||
return async function () {
|
||||
let authValues = {};
|
||||
|
||||
for (const authField of authFields) {
|
||||
let authValue = process.env[authField];
|
||||
if (!authValue) {
|
||||
authValue = await getUserPluginAuthValue(userId, authField);
|
||||
/**
|
||||
* Finds the first non-empty value for the given authentication field, supporting alternate fields.
|
||||
* @param {string[]} fields Array of strings representing the authentication fields. Supports alternate fields delimited by "||".
|
||||
* @returns {Promise<{ authField: string, authValue: string} | null>} An object containing the authentication field and value, or null if not found.
|
||||
*/
|
||||
const findAuthValue = async (fields) => {
|
||||
for (const field of fields) {
|
||||
let value = process.env[field];
|
||||
if (value) {
|
||||
return { authField: field, authValue: value };
|
||||
}
|
||||
try {
|
||||
value = await getUserPluginAuthValue(userId, field);
|
||||
} catch (err) {
|
||||
if (field === fields[fields.length - 1] && !value) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (value) {
|
||||
return { authField: field, authValue: value };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
for (let authField of authFields) {
|
||||
const fields = authField.split('||');
|
||||
const result = await findAuthValue(fields);
|
||||
if (result) {
|
||||
authValues[result.authField] = result.authValue;
|
||||
}
|
||||
authValues[authField] = authValue;
|
||||
}
|
||||
|
||||
return new ToolConstructor({ ...options, ...authValues, userId });
|
||||
|
|
@ -194,7 +255,7 @@ const loadTools = async ({
|
|||
|
||||
if (toolConstructors[tool]) {
|
||||
const options = toolOptions[tool] || {};
|
||||
const toolInstance = await loadToolWithAuth(
|
||||
const toolInstance = loadToolWithAuth(
|
||||
user,
|
||||
toolAuthFields[tool],
|
||||
toolConstructors[tool],
|
||||
|
|
@ -250,6 +311,7 @@ const loadTools = async ({
|
|||
};
|
||||
|
||||
module.exports = {
|
||||
loadToolWithAuth,
|
||||
validateTools,
|
||||
loadTools,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue