mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-15 15:08:10 +01:00
📦 fix: npm warnings; chore: bump deprecated packages (#4707)
* chore: bump langchain deps to address vulnerability warnings * chore: bump community package and install textsplitters package * fix: update expected result in tokenSplit tests for accuracy * chore: remove CodeSherpa tools * chore: remove E2B tools and loadToolSuite * chore: remove CodeBrew tool and update related references * chore: remove HumanTool and ChatTool, update tool references * chore: remove Zapier tool from manifest.json and update SerpAPI * chore: remove basic tools * chore: update import path for RecursiveCharacterTextSplitter * chore: update import path for DynamicStructuredTool * chore: remove extractionChain.js and update tool filtering logic * chore: npm audit fix * chore: bump google packages * chore: update DALL-E tool to DALL-E-3 and adjust authentication logic * ci: update message classes * chore: elliptic npm audit fix * chore: update CallbackManager import and remove deprecated tool handling logic * chore: imports order * chore: remove unused code --------- Co-authored-by: Max Sanna <max@maxsanna.com>
This commit is contained in:
parent
d012da0065
commit
95201908e9
40 changed files with 1446 additions and 3109 deletions
|
|
@ -1,32 +1,22 @@
|
|||
const { Tools } = require('librechat-data-provider');
|
||||
const { ZapierToolKit } = require('langchain/agents');
|
||||
const { Calculator } = require('langchain/tools/calculator');
|
||||
const { SerpAPI, ZapierNLAWrapper } = require('langchain/tools');
|
||||
const { SerpAPI } = require('@langchain/community/tools/serpapi');
|
||||
const { Calculator } = require('@langchain/community/tools/calculator');
|
||||
const { createCodeExecutionTool, EnvVar } = require('@librechat/agents');
|
||||
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
|
||||
const {
|
||||
availableTools,
|
||||
// Basic Tools
|
||||
CodeBrew,
|
||||
AzureAISearch,
|
||||
GoogleSearchAPI,
|
||||
WolframAlphaAPI,
|
||||
OpenAICreateImage,
|
||||
StableDiffusionAPI,
|
||||
// Structured Tools
|
||||
DALLE3,
|
||||
E2BTools,
|
||||
CodeSherpa,
|
||||
StructuredSD,
|
||||
StructuredACS,
|
||||
CodeSherpaTools,
|
||||
TraversaalSearch,
|
||||
StructuredWolfram,
|
||||
TavilySearchResults,
|
||||
} = require('../');
|
||||
const { primeFiles } = require('~/server/services/Files/Code/process');
|
||||
const createFileSearchTool = require('./createFileSearchTool');
|
||||
const { loadToolSuite } = require('./loadToolSuite');
|
||||
const { loadSpecs } = require('./loadSpecs');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
|
|
@ -152,52 +142,23 @@ const loadToolWithAuth = (userId, authFields, ToolConstructor, options = {}) =>
|
|||
const loadTools = async ({
|
||||
user,
|
||||
model,
|
||||
functions = null,
|
||||
functions = true,
|
||||
returnMap = false,
|
||||
tools = [],
|
||||
options = {},
|
||||
skipSpecs = false,
|
||||
}) => {
|
||||
const toolConstructors = {
|
||||
tavily_search_results_json: TavilySearchResults,
|
||||
calculator: Calculator,
|
||||
google: GoogleSearchAPI,
|
||||
wolfram: functions ? StructuredWolfram : WolframAlphaAPI,
|
||||
'dall-e': OpenAICreateImage,
|
||||
'stable-diffusion': functions ? StructuredSD : StableDiffusionAPI,
|
||||
'azure-ai-search': functions ? StructuredACS : AzureAISearch,
|
||||
CodeBrew: CodeBrew,
|
||||
wolfram: StructuredWolfram,
|
||||
'stable-diffusion': StructuredSD,
|
||||
'azure-ai-search': StructuredACS,
|
||||
traversaal_search: TraversaalSearch,
|
||||
tavily_search_results_json: TavilySearchResults,
|
||||
};
|
||||
|
||||
const customConstructors = {
|
||||
e2b_code_interpreter: async () => {
|
||||
if (!functions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await loadToolSuite({
|
||||
pluginKey: 'e2b_code_interpreter',
|
||||
tools: E2BTools,
|
||||
user,
|
||||
options: {
|
||||
model,
|
||||
...options,
|
||||
},
|
||||
});
|
||||
},
|
||||
codesherpa_tools: async () => {
|
||||
if (!functions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await loadToolSuite({
|
||||
pluginKey: 'codesherpa_tools',
|
||||
tools: CodeSherpaTools,
|
||||
user,
|
||||
options,
|
||||
});
|
||||
},
|
||||
serpapi: async () => {
|
||||
let apiKey = process.env.SERPAPI_API_KEY;
|
||||
if (!apiKey) {
|
||||
|
|
@ -209,21 +170,12 @@ const loadTools = async ({
|
|||
gl: 'us',
|
||||
});
|
||||
},
|
||||
zapier: async () => {
|
||||
let apiKey = process.env.ZAPIER_NLA_API_KEY;
|
||||
if (!apiKey) {
|
||||
apiKey = await getUserPluginAuthValue(user, 'ZAPIER_NLA_API_KEY');
|
||||
}
|
||||
const zapier = new ZapierNLAWrapper({ apiKey });
|
||||
return ZapierToolKit.fromZapierNLAWrapper(zapier);
|
||||
},
|
||||
};
|
||||
|
||||
const requestedTools = {};
|
||||
|
||||
if (functions) {
|
||||
toolConstructors.dalle = DALLE3;
|
||||
toolConstructors.codesherpa = CodeSherpa;
|
||||
}
|
||||
|
||||
const imageGenOptions = {
|
||||
|
|
|
|||
|
|
@ -18,26 +18,20 @@ jest.mock('~/models/User', () => {
|
|||
|
||||
jest.mock('~/server/services/PluginService', () => mockPluginService);
|
||||
|
||||
const { Calculator } = require('langchain/tools/calculator');
|
||||
const { BaseChatModel } = require('langchain/chat_models/openai');
|
||||
const { BaseLLM } = require('@langchain/openai');
|
||||
const { Calculator } = require('@langchain/community/tools/calculator');
|
||||
|
||||
const User = require('~/models/User');
|
||||
const PluginService = require('~/server/services/PluginService');
|
||||
const { validateTools, loadTools, loadToolWithAuth } = require('./handleTools');
|
||||
const {
|
||||
availableTools,
|
||||
OpenAICreateImage,
|
||||
GoogleSearchAPI,
|
||||
StructuredSD,
|
||||
WolframAlphaAPI,
|
||||
} = require('../');
|
||||
const { StructuredSD, availableTools, DALLE3 } = require('../');
|
||||
|
||||
describe('Tool Handlers', () => {
|
||||
let fakeUser;
|
||||
const pluginKey = 'dall-e';
|
||||
const pluginKey = 'dalle';
|
||||
const pluginKey2 = 'wolfram';
|
||||
const ToolClass = DALLE3;
|
||||
const initialTools = [pluginKey, pluginKey2];
|
||||
const ToolClass = OpenAICreateImage;
|
||||
const mockCredential = 'mock-credential';
|
||||
const mainPlugin = availableTools.find((tool) => tool.pluginKey === pluginKey);
|
||||
const authConfigs = mainPlugin.authConfig;
|
||||
|
|
@ -136,7 +130,7 @@ describe('Tool Handlers', () => {
|
|||
beforeAll(async () => {
|
||||
toolFunctions = await loadTools({
|
||||
user: fakeUser._id,
|
||||
model: BaseChatModel,
|
||||
model: BaseLLM,
|
||||
tools: sampleTools,
|
||||
returnMap: true,
|
||||
});
|
||||
|
|
@ -174,10 +168,10 @@ describe('Tool Handlers', () => {
|
|||
});
|
||||
|
||||
it('should initialize an authenticated tool with primary auth field', async () => {
|
||||
process.env.DALLE2_API_KEY = 'mocked_api_key';
|
||||
process.env.DALLE3_API_KEY = 'mocked_api_key';
|
||||
const initToolFunction = loadToolWithAuth(
|
||||
'userId',
|
||||
['DALLE2_API_KEY||DALLE_API_KEY'],
|
||||
['DALLE3_API_KEY||DALLE_API_KEY'],
|
||||
ToolClass,
|
||||
);
|
||||
const authTool = await initToolFunction();
|
||||
|
|
@ -187,11 +181,11 @@ describe('Tool Handlers', () => {
|
|||
});
|
||||
|
||||
it('should initialize an authenticated tool with alternate auth field when primary is missing', async () => {
|
||||
delete process.env.DALLE2_API_KEY; // Ensure the primary key is not set
|
||||
delete process.env.DALLE3_API_KEY; // Ensure the primary key is not set
|
||||
process.env.DALLE_API_KEY = 'mocked_alternate_api_key';
|
||||
const initToolFunction = loadToolWithAuth(
|
||||
'userId',
|
||||
['DALLE2_API_KEY||DALLE_API_KEY'],
|
||||
['DALLE3_API_KEY||DALLE_API_KEY'],
|
||||
ToolClass,
|
||||
);
|
||||
const authTool = await initToolFunction();
|
||||
|
|
@ -200,7 +194,7 @@ describe('Tool Handlers', () => {
|
|||
expect(mockPluginService.getUserPluginAuthValue).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginService.getUserPluginAuthValue).toHaveBeenCalledWith(
|
||||
'userId',
|
||||
'DALLE2_API_KEY',
|
||||
'DALLE3_API_KEY',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -208,7 +202,7 @@ describe('Tool Handlers', () => {
|
|||
mockPluginService.updateUserPluginAuth('userId', 'DALLE_API_KEY', 'dalle', 'mocked_api_key');
|
||||
const initToolFunction = loadToolWithAuth(
|
||||
'userId',
|
||||
['DALLE2_API_KEY||DALLE_API_KEY'],
|
||||
['DALLE3_API_KEY||DALLE_API_KEY'],
|
||||
ToolClass,
|
||||
);
|
||||
const authTool = await initToolFunction();
|
||||
|
|
@ -217,41 +211,6 @@ describe('Tool Handlers', () => {
|
|||
expect(mockPluginService.getUserPluginAuthValue).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should initialize an authenticated tool with singular auth field', async () => {
|
||||
process.env.WOLFRAM_APP_ID = 'mocked_app_id';
|
||||
const initToolFunction = loadToolWithAuth('userId', ['WOLFRAM_APP_ID'], WolframAlphaAPI);
|
||||
const authTool = await initToolFunction();
|
||||
|
||||
expect(authTool).toBeInstanceOf(WolframAlphaAPI);
|
||||
expect(mockPluginService.getUserPluginAuthValue).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize an authenticated tool when env var is set', async () => {
|
||||
process.env.WOLFRAM_APP_ID = 'mocked_app_id';
|
||||
const initToolFunction = loadToolWithAuth('userId', ['WOLFRAM_APP_ID'], WolframAlphaAPI);
|
||||
const authTool = await initToolFunction();
|
||||
|
||||
expect(authTool).toBeInstanceOf(WolframAlphaAPI);
|
||||
expect(mockPluginService.getUserPluginAuthValue).not.toHaveBeenCalledWith(
|
||||
'userId',
|
||||
'WOLFRAM_APP_ID',
|
||||
);
|
||||
});
|
||||
|
||||
it('should fallback to getUserPluginAuthValue when singular env var is missing', async () => {
|
||||
delete process.env.WOLFRAM_APP_ID; // Ensure the environment variable is not set
|
||||
mockPluginService.getUserPluginAuthValue.mockResolvedValue('mocked_user_auth_value');
|
||||
const initToolFunction = loadToolWithAuth('userId', ['WOLFRAM_APP_ID'], WolframAlphaAPI);
|
||||
const authTool = await initToolFunction();
|
||||
|
||||
expect(authTool).toBeInstanceOf(WolframAlphaAPI);
|
||||
expect(mockPluginService.getUserPluginAuthValue).toHaveBeenCalledTimes(1);
|
||||
expect(mockPluginService.getUserPluginAuthValue).toHaveBeenCalledWith(
|
||||
'userId',
|
||||
'WOLFRAM_APP_ID',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for an unauthenticated tool', async () => {
|
||||
try {
|
||||
await loadTool2();
|
||||
|
|
@ -260,27 +219,10 @@ describe('Tool Handlers', () => {
|
|||
expect(error).toBeDefined();
|
||||
}
|
||||
});
|
||||
it('should initialize an authenticated tool through Environment Variables', async () => {
|
||||
let testPluginKey = 'google';
|
||||
let TestClass = GoogleSearchAPI;
|
||||
const plugin = availableTools.find((tool) => tool.pluginKey === testPluginKey);
|
||||
const authConfigs = plugin.authConfig;
|
||||
for (const authConfig of authConfigs) {
|
||||
process.env[authConfig.authField] = mockCredential;
|
||||
}
|
||||
toolFunctions = await loadTools({
|
||||
user: fakeUser._id,
|
||||
model: BaseChatModel,
|
||||
tools: [testPluginKey],
|
||||
returnMap: true,
|
||||
});
|
||||
const Tool = await toolFunctions[testPluginKey]();
|
||||
expect(Tool).toBeInstanceOf(TestClass);
|
||||
});
|
||||
it('returns an empty object when no tools are requested', async () => {
|
||||
toolFunctions = await loadTools({
|
||||
user: fakeUser._id,
|
||||
model: BaseChatModel,
|
||||
model: BaseLLM,
|
||||
returnMap: true,
|
||||
});
|
||||
expect(toolFunctions).toEqual({});
|
||||
|
|
@ -289,7 +231,7 @@ describe('Tool Handlers', () => {
|
|||
process.env.SD_WEBUI_URL = mockCredential;
|
||||
toolFunctions = await loadTools({
|
||||
user: fakeUser._id,
|
||||
model: BaseChatModel,
|
||||
model: BaseLLM,
|
||||
tools: ['stable-diffusion'],
|
||||
functions: true,
|
||||
returnMap: true,
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
|
||||
const { availableTools } = require('../');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Loads a suite of tools with authentication values for a given user, supporting alternate authentication fields.
|
||||
* Authentication fields can have alternates separated by "||", and the first defined variable will be used.
|
||||
*
|
||||
* @param {Object} params Parameters for loading the tool suite.
|
||||
* @param {string} params.pluginKey Key identifying the plugin whose tools are to be loaded.
|
||||
* @param {Array<Function>} params.tools Array of tool constructor functions.
|
||||
* @param {Object} params.user User object for whom the tools are being loaded.
|
||||
* @param {Object} [params.options={}] Optional parameters to be passed to each tool constructor.
|
||||
* @returns {Promise<Array>} A promise that resolves to an array of instantiated tools.
|
||||
*/
|
||||
const loadToolSuite = async ({ pluginKey, tools, user, options = {} }) => {
|
||||
const authConfig = availableTools.find((tool) => tool.pluginKey === pluginKey).authConfig;
|
||||
const suite = [];
|
||||
const authValues = {};
|
||||
|
||||
const findAuthValue = async (authField) => {
|
||||
const fields = authField.split('||');
|
||||
for (const field of fields) {
|
||||
let value = process.env[field];
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
value = await getUserPluginAuthValue(user, field);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Error fetching plugin auth value for ${field}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
for (const auth of authConfig) {
|
||||
const authValue = await findAuthValue(auth.authField);
|
||||
if (authValue !== null) {
|
||||
authValues[auth.authField] = authValue;
|
||||
} else {
|
||||
logger.warn(`[loadToolSuite] No auth value found for ${auth.authField}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const tool of tools) {
|
||||
suite.push(
|
||||
new tool({
|
||||
...authValues,
|
||||
...options,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return suite;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loadToolSuite,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue