From 68c6afd00976d8248b235dbe529d8d9cdfd64555 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 17 Aug 2025 23:10:51 -0400 Subject: [PATCH] fix: streamline OpenAI image tools configuration by removing direct appConfig dependency and using function parameters --- .../tools/structured/OpenAIImageTools.js | 12 +++---- api/app/clients/tools/util/handleTools.js | 36 +++++++++++-------- api/server/controllers/tools.js | 4 ++- api/server/services/AppService.js | 2 ++ api/server/services/ToolService.js | 27 +++++++++++--- api/server/services/Tools/search.js | 2 +- api/typedefs.js | 8 ++++- packages/api/src/index.ts | 1 + 8 files changed, 63 insertions(+), 29 deletions(-) diff --git a/api/app/clients/tools/structured/OpenAIImageTools.js b/api/app/clients/tools/structured/OpenAIImageTools.js index f33a58e105..43bcbb794f 100644 --- a/api/app/clients/tools/structured/OpenAIImageTools.js +++ b/api/app/clients/tools/structured/OpenAIImageTools.js @@ -9,7 +9,6 @@ const { logAxiosError } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { ContentTypes, EImageOutputType } = require('librechat-data-provider'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); -const { getAppConfig } = require('~/server/services/Config/app'); const extractBaseURL = require('~/utils/extractBaseURL'); const { getFiles } = require('~/models/File'); @@ -122,9 +121,11 @@ function createAbortHandler() { * @param {string} fields.IMAGE_GEN_OAI_API_KEY - The OpenAI API key * @param {boolean} [fields.override] - Whether to override the API key check, necessary for app initialization * @param {MongoFile[]} [fields.imageFiles] - The images to be used for editing - * @returns {Promise} - Array of image tools + * @param {string} [fields.imageOutputType] - The image output type configuration + * @param {string} [fields.fileStrategy] - The file storage strategy + * @returns {Array>} - Array of image tools */ -async function createOpenAIImageTools(fields = {}) { +function createOpenAIImageTools(fields = {}) { /** @type {boolean} Used to initialize the Tool without necessary variables. */ const override = fields.override ?? false; /** @type {boolean} */ @@ -132,9 +133,8 @@ async function createOpenAIImageTools(fields = {}) { throw new Error('This tool is only available for agents.'); } const { req } = fields; - const appConfig = await getAppConfig({ role: req?.user?.role }); - const imageOutputType = appConfig?.imageOutputType || EImageOutputType.PNG; - const appFileStrategy = appConfig?.fileStrategy; + const imageOutputType = fields.imageOutputType || EImageOutputType.PNG; + const appFileStrategy = fields.fileStrategy; const getApiKey = () => { const apiKey = process.env.IMAGE_GEN_OAI_API_KEY ?? ''; diff --git a/api/app/clients/tools/util/handleTools.js b/api/app/clients/tools/util/handleTools.js index ff4a4655ba..0b02043d9a 100644 --- a/api/app/clients/tools/util/handleTools.js +++ b/api/app/clients/tools/util/handleTools.js @@ -24,8 +24,8 @@ const { const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process'); const { createFileSearchTool, primeFiles: primeSearchFiles } = require('./fileSearch'); const { getUserPluginAuthValue } = require('~/server/services/PluginService'); -const { getCachedTools, getAppConfig } = require('~/server/services/Config'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); +const { getCachedTools } = require('~/server/services/Config'); const { createMCPTool } = require('~/server/services/MCP'); /** @@ -121,16 +121,19 @@ const getAuthFields = (toolKey) => { /** * - * @param {object} object - * @param {string} object.user - * @param {Pick} [object.agent] - * @param {string} [object.model] - * @param {EModelEndpoint} [object.endpoint] - * @param {LoadToolOptions} [object.options] - * @param {boolean} [object.useSpecs] - * @param {Array} object.tools - * @param {boolean} [object.functions] - * @param {boolean} [object.returnMap] + * @param {object} params + * @param {string} params.user + * @param {Pick} [params.agent] + * @param {string} [params.model] + * @param {EModelEndpoint} [params.endpoint] + * @param {LoadToolOptions} [params.options] + * @param {boolean} [params.useSpecs] + * @param {Array} params.tools + * @param {boolean} [params.functions] + * @param {boolean} [params.returnMap] + * @param {AppConfig['webSearch']} [params.webSearch] + * @param {AppConfig['fileStrategy']} [params.fileStrategy] + * @param {AppConfig['imageOutputType']} [params.imageOutputType] * @returns {Promise<{ loadedTools: Tool[], toolContextMap: Object } | Record>} */ const loadTools = async ({ @@ -142,8 +145,10 @@ const loadTools = async ({ options = {}, functions = true, returnMap = false, + webSearch, + fileStrategy, + imageOutputType, }) => { - const appConfig = await getAppConfig({ role: options?.req?.user?.role }); const toolConstructors = { flux: FluxAPI, calculator: Calculator, @@ -201,6 +206,8 @@ const loadTools = async ({ ...authValues, isAgent: !!agent, req: options.req, + imageOutputType, + fileStrategy, imageFiles, }); }, @@ -216,7 +223,7 @@ const loadTools = async ({ const imageGenOptions = { isAgent: !!agent, req: options.req, - fileStrategy: options.fileStrategy, + fileStrategy, processFileURL: options.processFileURL, returnMetadata: options.returnMetadata, uploadImageBuffer: options.uploadImageBuffer, @@ -273,11 +280,10 @@ const loadTools = async ({ }; continue; } else if (tool === Tools.web_search) { - const webSearchConfig = appConfig?.webSearch; const result = await loadWebSearchAuth({ userId: user, loadAuthValues, - webSearchConfig, + webSearchConfig: webSearch, }); const { onSearchResults, onGetHighlights } = options?.[Tools.web_search] ?? {}; requestedTools[tool] = async () => { diff --git a/api/server/controllers/tools.js b/api/server/controllers/tools.js index 34a6b67c61..3873675678 100644 --- a/api/server/controllers/tools.js +++ b/api/server/controllers/tools.js @@ -158,8 +158,10 @@ const callTool = async (req, res) => { returnMetadata: true, processFileURL, uploadImageBuffer, - fileStrategy: appConfig.fileStrategy, }, + webSearch: appConfig.webSearch, + fileStrategy: appConfig.fileStrategy, + imageOutputType: appConfig.imageOutputType, }); const tool = loadedTools[0]; diff --git a/api/server/services/AppService.js b/api/server/services/AppService.js index ad3b24120f..84adb0e96d 100644 --- a/api/server/services/AppService.js +++ b/api/server/services/AppService.js @@ -75,6 +75,8 @@ const AppService = async () => { adminFilter: filteredTools, adminIncluded: includedTools, directory: paths.structuredTools, + imageOutputType, + fileStrategy, }); await setCachedTools(availableTools, { isGlobal: true }); diff --git a/api/server/services/ToolService.js b/api/server/services/ToolService.js index 2a797cf47a..2ed1b4076e 100644 --- a/api/server/services/ToolService.js +++ b/api/server/services/ToolService.js @@ -50,11 +50,19 @@ const { redactMessage } = require('~/config/parsers'); * * @param {object} params - The parameters for the function. * @param {string} params.directory - The directory path where the tools are located. + * @param {string} [params.imageOutputType] - The image output type configuration. + * @param {string} [params.fileStrategy] - The file storage strategy. * @param {Array} [params.adminFilter=[]] - Array of admin-defined tool keys to exclude from loading. * @param {Array} [params.adminIncluded=[]] - Array of admin-defined tool keys to include from loading. * @returns {Record} An object mapping each tool's plugin key to its instance. */ -function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) { +function loadAndFormatTools({ + directory, + fileStrategy, + imageOutputType, + adminFilter = [], + adminIncluded = [], +}) { const filter = new Set([...adminFilter]); const included = new Set(adminIncluded); const tools = []; @@ -113,9 +121,14 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) } /** Basic Tools & Toolkits; schema: { input: string } */ + const openAIImageTools = createOpenAIImageTools({ + override: true, + imageOutputType, + fileStrategy, + }); const basicToolInstances = [ new Calculator(), - ...createOpenAIImageTools({ override: true }), + ...openAIImageTools, ...createYouTubeTools({ override: true }), ]; for (const toolInstance of basicToolInstances) { @@ -234,9 +247,11 @@ async function processRequiredActions(client, requiredActions) { req: client.req, uploadImageBuffer, openAIApiKey: client.apiKey, - fileStrategy: appConfig.fileStrategy, returnMetadata: true, }, + webSearch: appConfig.webSearch, + fileStrategy: appConfig.fileStrategy, + imageOutputType: appConfig.imageOutputType, }); const ToolMap = loadedTools.reduce((map, tool) => { @@ -519,7 +534,7 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey }) if (!_agentTools || _agentTools.length === 0) { return {}; } - /** @type {ReturnType} */ + /** @type {ReturnType} */ let webSearchCallbacks; if (includesWebSearch) { webSearchCallbacks = createOnSearchResults(res); @@ -538,9 +553,11 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey }) processFileURL, uploadImageBuffer, returnMetadata: true, - fileStrategy: appConfig.fileStrategy, [Tools.web_search]: webSearchCallbacks, }, + webSearch: appConfig.webSearch, + fileStrategy: appConfig.fileStrategy, + imageOutputType: appConfig.imageOutputType, }); const agentTools = []; diff --git a/api/server/services/Tools/search.js b/api/server/services/Tools/search.js index a5c9947b5f..c10c543141 100644 --- a/api/server/services/Tools/search.js +++ b/api/server/services/Tools/search.js @@ -1,6 +1,6 @@ const { nanoid } = require('nanoid'); const { Tools } = require('librechat-data-provider'); -const { logger } = require('~/config'); +const { logger } = require('@librechat/data-schemas'); /** * Creates a function to handle search results and stream them as attachments diff --git a/api/typedefs.js b/api/typedefs.js index b8d2aa3487..275379538b 100644 --- a/api/typedefs.js +++ b/api/typedefs.js @@ -917,7 +917,6 @@ * @typedef {Object} ImageGenOptions * @property {ServerRequest} req - The request object. * @property {boolean} isAgent - Whether the request is from an agent. - * @property {FileSources} fileStrategy - The file strategy to use. * @property {processFileURL} processFileURL - The function to process a file URL. * @property {boolean} returnMetadata - Whether to return metadata. * @property {uploadImageBuffer} uploadImageBuffer - The function to upload an image buffer. @@ -930,6 +929,7 @@ * signal?: AbortSignal, * memory?: ConversationSummaryBufferMemory, * tool_resources?: AgentToolResources, + * web_search?: ReturnType, * }} LoadToolOptions * @memberof typedefs */ @@ -1091,6 +1091,12 @@ * @memberof typedefs */ +/** + * @exports AppConfig + * @typedef {import('@librechat/api').AppConfig} AppConfig + * @memberof typedefs + */ + /** * @exports JsonSchemaType * @typedef {import('@librechat/api').JsonSchemaType} JsonSchemaType diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 1096b019d9..27b84d05e8 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -32,3 +32,4 @@ export * from './web'; /* types */ export type * from './mcp/types'; export type * from './flow/types'; +export type * from './types';