mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-14 14:38:51 +01:00
* Refresh all S3 avatars for this user's accessible agent set, not the first page * Cleaner debug messages * Log errors as errors * refactor: avatar refresh logic to process agents in batches and improve error handling. Introduced new utility functions for refreshing S3 avatars and updating agent records. Updated tests to cover various scenarios including cache hits, user ownership checks, and error handling. Added constants for maximum refresh limits. * refactor: update avatar refresh logic to allow users with VIEW access to refresh avatars for all accessible agents. Removed checks for agent ownership and author presence, and updated related tests to reflect new behavior. * chore: Remove YouTube toolkit due to #11331 --------- Co-authored-by: Danny Avila <danny@librechat.ai>
132 lines
4.3 KiB
JavaScript
132 lines
4.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { Tool } = require('@langchain/core/tools');
|
|
const { Calculator } = require('@librechat/agents');
|
|
const { logger } = require('@librechat/data-schemas');
|
|
const { zodToJsonSchema } = require('zod-to-json-schema');
|
|
const { Tools, ImageVisionTool } = require('librechat-data-provider');
|
|
const { getToolkitKey, oaiToolkit, geminiToolkit } = require('@librechat/api');
|
|
const { toolkits } = require('~/app/clients/tools/manifest');
|
|
|
|
/**
|
|
* Loads and formats tools from the specified tool directory.
|
|
*
|
|
* The directory is scanned for JavaScript files, excluding any files in the filter set.
|
|
* For each file, it attempts to load the file as a module and instantiate a class, if it's a subclass of `StructuredTool`.
|
|
* Each tool instance is then formatted to be compatible with the OpenAI Assistant.
|
|
* Additionally, instances of LangChain Tools are included in the result.
|
|
*
|
|
* @param {object} params - The parameters for the function.
|
|
* @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.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.
|
|
*/
|
|
function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) {
|
|
const filter = new Set([...adminFilter]);
|
|
const included = new Set(adminIncluded);
|
|
const tools = [];
|
|
/* Structured Tools Directory */
|
|
const files = fs.readdirSync(directory);
|
|
|
|
if (included.size > 0 && adminFilter.length > 0) {
|
|
logger.warn(
|
|
'Both `includedTools` and `filteredTools` are defined; `filteredTools` will be ignored.',
|
|
);
|
|
}
|
|
|
|
for (const file of files) {
|
|
const filePath = path.join(directory, file);
|
|
if (!file.endsWith('.js') || (filter.has(file) && included.size === 0)) {
|
|
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 Tool)) {
|
|
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;
|
|
}
|
|
|
|
if (filter.has(toolInstance.name) && included.size === 0) {
|
|
continue;
|
|
}
|
|
|
|
if (included.size > 0 && !included.has(file) && !included.has(toolInstance.name)) {
|
|
continue;
|
|
}
|
|
|
|
const formattedTool = formatToOpenAIAssistantTool(toolInstance);
|
|
tools.push(formattedTool);
|
|
}
|
|
|
|
const basicToolInstances = [
|
|
new Calculator(),
|
|
...Object.values(oaiToolkit),
|
|
...Object.values(geminiToolkit),
|
|
];
|
|
for (const toolInstance of basicToolInstances) {
|
|
const formattedTool = formatToOpenAIAssistantTool(toolInstance);
|
|
let toolName = formattedTool[Tools.function].name;
|
|
toolName = getToolkitKey({ toolkits, toolName }) ?? toolName;
|
|
if (filter.has(toolName) && included.size === 0) {
|
|
continue;
|
|
}
|
|
|
|
if (included.size > 0 && !included.has(toolName)) {
|
|
continue;
|
|
}
|
|
tools.push(formattedTool);
|
|
}
|
|
|
|
tools.push(ImageVisionTool);
|
|
|
|
return tools.reduce((map, tool) => {
|
|
map[tool.function.name] = tool;
|
|
return map;
|
|
}, {});
|
|
}
|
|
|
|
/**
|
|
* Formats a `StructuredTool` instance into a format that is compatible
|
|
* with OpenAI's ChatCompletionFunctions. It uses the `zodToJsonSchema`
|
|
* function to convert the schema of the `StructuredTool` into a JSON
|
|
* schema, which is then used as the parameters for the OpenAI function.
|
|
*
|
|
* @param {StructuredTool} tool - The StructuredTool to format.
|
|
* @returns {FunctionTool} The OpenAI Assistant Tool.
|
|
*/
|
|
function formatToOpenAIAssistantTool(tool) {
|
|
return {
|
|
type: Tools.function,
|
|
[Tools.function]: {
|
|
name: tool.name,
|
|
description: tool.description,
|
|
parameters: zodToJsonSchema(tool.schema),
|
|
},
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
loadAndFormatTools,
|
|
};
|