mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
⚙️ 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:
parent
89899164ed
commit
6fc664e4a3
9 changed files with 94 additions and 79 deletions
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue