⚙️ feat: includedTools and script changes (#2690)

* chore: add email to npm scripts (user-stats and list-balances)

* feat: included tools

* chore: update console terminal links

* chore: add back typing
This commit is contained in:
Danny Avila 2024-05-13 10:07:10 -04:00 committed by GitHub
parent 89899164ed
commit 6fc664e4a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 94 additions and 79 deletions

View file

@ -55,24 +55,26 @@ const getAvailablePluginsController = async (req, res) => {
return; return;
} }
/** @type {{ filteredTools: string[] }} */ /** @type {{ filteredTools: string[], includedTools: string[] }} */
const { filteredTools = [] } = req.app.locals; const { filteredTools = [], includedTools = [] } = req.app.locals;
const pluginManifest = await fs.readFile(req.app.locals.paths.pluginManifest, 'utf8'); const pluginManifest = await fs.readFile(req.app.locals.paths.pluginManifest, 'utf8');
const jsonData = JSON.parse(pluginManifest); const jsonData = JSON.parse(pluginManifest);
/** @type {TPlugin[]} */
const uniquePlugins = filterUniquePlugins(jsonData); const uniquePlugins = filterUniquePlugins(jsonData);
const authenticatedPlugins = uniquePlugins.map((plugin) => { let authenticatedPlugins = [];
if (isPluginAuthenticated(plugin)) { for (const plugin of uniquePlugins) {
return { ...plugin, authenticated: true }; authenticatedPlugins.push(
} else { isPluginAuthenticated(plugin) ? { ...plugin, authenticated: true } : plugin,
return plugin; );
} }
});
let plugins = await addOpenAPISpecs(authenticatedPlugins); let plugins = await addOpenAPISpecs(authenticatedPlugins);
plugins = plugins.filter((plugin) => !filteredTools.includes(plugin.pluginKey));
if (includedTools.length > 0) {
plugins = plugins.filter((plugin) => includedTools.includes(plugin.pluginKey));
} else {
plugins = plugins.filter((plugin) => !filteredTools.includes(plugin.pluginKey));
}
await cache.set(CacheKeys.PLUGINS, plugins); await cache.set(CacheKeys.PLUGINS, plugins);
res.status(200).json(plugins); res.status(200).json(plugins);

View file

@ -21,6 +21,7 @@ const AppService = async (app) => {
const configDefaults = getConfigDefaults(); const configDefaults = getConfigDefaults();
const filteredTools = config.filteredTools; const filteredTools = config.filteredTools;
const includedTools = config.includedTools;
const fileStrategy = config.fileStrategy ?? configDefaults.fileStrategy; const fileStrategy = config.fileStrategy ?? configDefaults.fileStrategy;
const imageOutputType = config?.imageOutputType ?? configDefaults.imageOutputType; const imageOutputType = config?.imageOutputType ?? configDefaults.imageOutputType;
@ -37,23 +38,26 @@ const AppService = async (app) => {
const availableTools = loadAndFormatTools({ const availableTools = loadAndFormatTools({
directory: paths.structuredTools, directory: paths.structuredTools,
adminFilter: filteredTools, adminFilter: filteredTools,
adminIncluded: includedTools,
}); });
const socialLogins = const socialLogins =
config?.registration?.socialLogins ?? configDefaults?.registration?.socialLogins; config?.registration?.socialLogins ?? configDefaults?.registration?.socialLogins;
const interfaceConfig = loadDefaultInterface(config, configDefaults); const interfaceConfig = loadDefaultInterface(config, configDefaults);
if (!Object.keys(config).length) { const defaultLocals = {
app.locals = { paths,
paths, fileStrategy,
fileStrategy, socialLogins,
socialLogins, filteredTools,
filteredTools, includedTools,
availableTools, availableTools,
imageOutputType, imageOutputType,
interfaceConfig, interfaceConfig,
}; };
if (!Object.keys(config).length) {
app.locals = defaultLocals;
return; return;
} }
@ -79,13 +83,7 @@ const AppService = async (app) => {
} }
app.locals = { app.locals = {
paths, ...defaultLocals,
socialLogins,
fileStrategy,
filteredTools,
availableTools,
imageOutputType,
interfaceConfig,
modelSpecs: config.modelSpecs, modelSpecs: config.modelSpecs,
fileConfig: config?.fileConfig, fileConfig: config?.fileConfig,
secureImageLinks: config?.secureImageLinks, secureImageLinks: config?.secureImageLinks,

View file

@ -37,7 +37,7 @@ async function loadCustomConfig() {
if (!customConfig) { if (!customConfig) {
i === 0 && i === 0 &&
logger.info( logger.info(
'Custom config file missing or YAML format invalid.\n\nCheck out the latest config file guide for configurable options and features.\nhttps://docs.librechat.ai/install/configuration/custom_config.html\n\n', 'Custom config file missing or YAML format invalid.\n\nCheck out the latest config file guide for configurable options and features.\nhttps://www.librechat.ai/docs/configuration/librechat_yaml\n\n',
); );
i === 0 && i++; i === 0 && i++;
return null; return null;
@ -72,7 +72,7 @@ Please specify a correct \`imageOutputType\` value (case-sensitive).
- ${EImageOutputType.WEBP} - ${EImageOutputType.WEBP}
Refer to the latest config file guide for more information: Refer to the latest config file guide for more information:
https://docs.librechat.ai/install/configuration/custom_config.html`, https://www.librechat.ai/docs/configuration/librechat_yaml`,
); );
} }
if (!result.success) { if (!result.success) {

View file

@ -39,57 +39,65 @@ const filteredTools = new Set([
* @param {object} params - The parameters for the function. * @param {object} params - The parameters for the function.
* @param {string} params.directory - The directory path where the tools are located. * @param {string} params.directory - The directory path where the tools are located.
* @param {Array<string>} [params.adminFilter=[]] - Array of admin-defined tool keys to exclude from loading. * @param {Array<string>} [params.adminFilter=[]] - Array of admin-defined tool keys to exclude from loading.
* @param {Array<string>} [params.adminIncluded=[]] - Array of admin-defined tool keys to include from loading.
* @returns {Record<string, FunctionTool>} An object mapping each tool's plugin key to its instance. * @returns {Record<string, FunctionTool>} An object mapping each tool's plugin key to its instance.
*/ */
function loadAndFormatTools({ directory, adminFilter = [] }) { function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) {
const filter = new Set([...adminFilter, ...filteredTools]); const filter = new Set([...adminFilter, ...filteredTools]);
const included = new Set(adminIncluded);
const tools = []; const tools = [];
/* Structured Tools Directory */ /* Structured Tools Directory */
const files = fs.readdirSync(directory); const files = fs.readdirSync(directory);
for (const file of files) { if (included.size > 0 && adminFilter.length > 0) {
if (file.endsWith('.js') && !filter.has(file)) { logger.warn(
const filePath = path.join(directory, file); 'Both `includedTools` and `filteredTools` are defined; `filteredTools` will be ignored.',
let ToolClass = null; );
try {
ToolClass = require(filePath);
} catch (error) {
logger.error(`[loadAndFormatTools] Error loading tool from ${filePath}:`, error);
continue;
}
if (!ToolClass) {
continue;
}
if (ToolClass.prototype instanceof StructuredTool) {
/** @type {StructuredTool | null} */
let toolInstance = null;
try {
toolInstance = new ToolClass({ override: true });
} catch (error) {
logger.error(
`[loadAndFormatTools] Error initializing \`${file}\` tool; if it requires authentication, is the \`override\` field configured?`,
error,
);
continue;
}
if (!toolInstance) {
continue;
}
const formattedTool = formatToOpenAIAssistantTool(toolInstance);
tools.push(formattedTool);
}
}
} }
/** for (const file of files) {
* Basic Tools; schema: { input: string } const filePath = path.join(directory, file);
*/ if (!file.endsWith('.js') || (filter.has(file) && included.size === 0)) {
const basicToolInstances = [new Calculator()]; continue;
}
let ToolClass = null;
try {
ToolClass = require(filePath);
} catch (error) {
logger.error(`[loadAndFormatTools] Error loading tool from ${filePath}:`, error);
continue;
}
if (!ToolClass || !(ToolClass.prototype instanceof StructuredTool)) {
continue;
}
if (included.size > 0 && !included.has(file)) {
continue;
}
let toolInstance = null;
try {
toolInstance = new ToolClass({ override: true });
} catch (error) {
logger.error(
`[loadAndFormatTools] Error initializing \`${file}\` tool; if it requires authentication, is the \`override\` field configured?`,
error,
);
continue;
}
if (!toolInstance) {
continue;
}
const formattedTool = formatToOpenAIAssistantTool(toolInstance);
tools.push(formattedTool);
}
/** Basic Tools; schema: { input: string } */
const basicToolInstances = [new Calculator()];
for (const toolInstance of basicToolInstances) { for (const toolInstance of basicToolInstances) {
const formattedTool = formatToOpenAIAssistantTool(toolInstance); const formattedTool = formatToOpenAIAssistantTool(toolInstance);
tools.push(formattedTool); tools.push(formattedTool);

View file

@ -99,7 +99,12 @@ function checkAzureVariables() {
function checkConfig(config) { function checkConfig(config) {
if (config.version !== Constants.CONFIG_VERSION) { if (config.version !== Constants.CONFIG_VERSION) {
logger.info( logger.info(
`\nOutdated Config version: ${config.version}. Current version: ${Constants.CONFIG_VERSION}\n\nCheck out the latest config file guide for new options and features.\nhttps://docs.librechat.ai/install/configuration/custom_config.html\n\n`, `\nOutdated Config version: ${config.version}
Latest version: ${Constants.CONFIG_VERSION}
Check out the Config changelogs for the latest options and features added.
https://www.librechat.ai/changelog\n\n`,
); );
} }
} }

View file

@ -19,9 +19,9 @@ const connect = require('./connect');
for (const user of users) { for (const user of users) {
let balance = await Balance.findOne({ user: user._id }); let balance = await Balance.findOne({ user: user._id });
if (balance !== null) { if (balance !== null) {
console.green(`User ${user.name} has a balance of ${balance.tokenCredits}`); console.green(`User ${user.name} (${user.email}) has a balance of ${balance.tokenCredits}`);
} else { } else {
console.yellow(`User ${user.name} has no balance`); console.yellow(`User ${user.name} (${user.email}) has no balance`);
} }
} }

View file

@ -24,6 +24,7 @@ const connect = require('./connect');
userData.push({ userData.push({
User: user.name, User: user.name,
Email: user.email,
Conversations: conversationsCount, Conversations: conversationsCount,
Messages: messagesCount, Messages: messagesCount,
}); });

View file

@ -1,5 +1,5 @@
# For more information, see the Configuration Guide: # For more information, see the Configuration Guide:
# https://docs.librechat.ai/install/configuration/custom_config.html # https://www.librechat.ai/docs/configuration/librechat_yaml
# Configuration version (required) # Configuration version (required)
version: 1.0.9 version: 1.0.9
@ -148,4 +148,4 @@ endpoints:
# serverFileSizeLimit: 100 # Global server file size limit in MB # serverFileSizeLimit: 100 # Global server file size limit in MB
# avatarSizeLimit: 2 # Limit for user avatar image size in MB # avatarSizeLimit: 2 # Limit for user avatar image size in MB
# See the Custom Configuration Guide for more information: # See the Custom Configuration Guide for more information:
# https://docs.librechat.ai/install/configuration/custom_config.html # https://www.librechat.ai/docs/configuration/librechat_yaml

View file

@ -245,6 +245,7 @@ export const configSchema = z.object({
cache: z.boolean().default(true), cache: z.boolean().default(true),
secureImageLinks: z.boolean().optional(), secureImageLinks: z.boolean().optional(),
imageOutputType: z.nativeEnum(EImageOutputType).default(EImageOutputType.PNG), imageOutputType: z.nativeEnum(EImageOutputType).default(EImageOutputType.PNG),
includedTools: z.array(z.string()).optional(),
filteredTools: z.array(z.string()).optional(), filteredTools: z.array(z.string()).optional(),
interface: z interface: z
.object({ .object({
@ -677,7 +678,7 @@ export enum Constants {
/** Key for the app's version. */ /** Key for the app's version. */
VERSION = 'v0.7.2', VERSION = 'v0.7.2',
/** Key for the Custom Config's version (librechat.yaml). */ /** Key for the Custom Config's version (librechat.yaml). */
CONFIG_VERSION = '1.0.9', CONFIG_VERSION = '1.1.0',
/** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */ /** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */
NO_PARENT = '00000000-0000-0000-0000-000000000000', NO_PARENT = '00000000-0000-0000-0000-000000000000',
/** Fixed, encoded domain length for Azure OpenAI Assistants Function name parsing. */ /** Fixed, encoded domain length for Azure OpenAI Assistants Function name parsing. */