fix: streamline OpenAI image tools configuration by removing direct appConfig dependency and using function parameters

This commit is contained in:
Danny Avila 2025-08-17 23:10:51 -04:00
parent 0b5816d1be
commit 68c6afd009
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
8 changed files with 63 additions and 29 deletions

View file

@ -9,7 +9,6 @@ const { logAxiosError } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { ContentTypes, EImageOutputType } = require('librechat-data-provider'); const { ContentTypes, EImageOutputType } = require('librechat-data-provider');
const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { getStrategyFunctions } = require('~/server/services/Files/strategies');
const { getAppConfig } = require('~/server/services/Config/app');
const extractBaseURL = require('~/utils/extractBaseURL'); const extractBaseURL = require('~/utils/extractBaseURL');
const { getFiles } = require('~/models/File'); const { getFiles } = require('~/models/File');
@ -122,9 +121,11 @@ function createAbortHandler() {
* @param {string} fields.IMAGE_GEN_OAI_API_KEY - The OpenAI API key * @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 {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 * @param {MongoFile[]} [fields.imageFiles] - The images to be used for editing
* @returns {Promise<StructuredTool[]>} - Array of image tools * @param {string} [fields.imageOutputType] - The image output type configuration
* @param {string} [fields.fileStrategy] - The file storage strategy
* @returns {Array<ReturnType<tool>>} - Array of image tools
*/ */
async function createOpenAIImageTools(fields = {}) { function createOpenAIImageTools(fields = {}) {
/** @type {boolean} Used to initialize the Tool without necessary variables. */ /** @type {boolean} Used to initialize the Tool without necessary variables. */
const override = fields.override ?? false; const override = fields.override ?? false;
/** @type {boolean} */ /** @type {boolean} */
@ -132,9 +133,8 @@ async function createOpenAIImageTools(fields = {}) {
throw new Error('This tool is only available for agents.'); throw new Error('This tool is only available for agents.');
} }
const { req } = fields; const { req } = fields;
const appConfig = await getAppConfig({ role: req?.user?.role }); const imageOutputType = fields.imageOutputType || EImageOutputType.PNG;
const imageOutputType = appConfig?.imageOutputType || EImageOutputType.PNG; const appFileStrategy = fields.fileStrategy;
const appFileStrategy = appConfig?.fileStrategy;
const getApiKey = () => { const getApiKey = () => {
const apiKey = process.env.IMAGE_GEN_OAI_API_KEY ?? ''; const apiKey = process.env.IMAGE_GEN_OAI_API_KEY ?? '';

View file

@ -24,8 +24,8 @@ const {
const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process'); const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process');
const { createFileSearchTool, primeFiles: primeSearchFiles } = require('./fileSearch'); const { createFileSearchTool, primeFiles: primeSearchFiles } = require('./fileSearch');
const { getUserPluginAuthValue } = require('~/server/services/PluginService'); const { getUserPluginAuthValue } = require('~/server/services/PluginService');
const { getCachedTools, getAppConfig } = require('~/server/services/Config');
const { loadAuthValues } = require('~/server/services/Tools/credentials'); const { loadAuthValues } = require('~/server/services/Tools/credentials');
const { getCachedTools } = require('~/server/services/Config');
const { createMCPTool } = require('~/server/services/MCP'); const { createMCPTool } = require('~/server/services/MCP');
/** /**
@ -121,16 +121,19 @@ const getAuthFields = (toolKey) => {
/** /**
* *
* @param {object} object * @param {object} params
* @param {string} object.user * @param {string} params.user
* @param {Pick<Agent, 'id' | 'provider' | 'model'>} [object.agent] * @param {Pick<Agent, 'id' | 'provider' | 'model'>} [params.agent]
* @param {string} [object.model] * @param {string} [params.model]
* @param {EModelEndpoint} [object.endpoint] * @param {EModelEndpoint} [params.endpoint]
* @param {LoadToolOptions} [object.options] * @param {LoadToolOptions} [params.options]
* @param {boolean} [object.useSpecs] * @param {boolean} [params.useSpecs]
* @param {Array<string>} object.tools * @param {Array<string>} params.tools
* @param {boolean} [object.functions] * @param {boolean} [params.functions]
* @param {boolean} [object.returnMap] * @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<string, any> } | Record<string,Tool>>} * @returns {Promise<{ loadedTools: Tool[], toolContextMap: Object<string, any> } | Record<string,Tool>>}
*/ */
const loadTools = async ({ const loadTools = async ({
@ -142,8 +145,10 @@ const loadTools = async ({
options = {}, options = {},
functions = true, functions = true,
returnMap = false, returnMap = false,
webSearch,
fileStrategy,
imageOutputType,
}) => { }) => {
const appConfig = await getAppConfig({ role: options?.req?.user?.role });
const toolConstructors = { const toolConstructors = {
flux: FluxAPI, flux: FluxAPI,
calculator: Calculator, calculator: Calculator,
@ -201,6 +206,8 @@ const loadTools = async ({
...authValues, ...authValues,
isAgent: !!agent, isAgent: !!agent,
req: options.req, req: options.req,
imageOutputType,
fileStrategy,
imageFiles, imageFiles,
}); });
}, },
@ -216,7 +223,7 @@ const loadTools = async ({
const imageGenOptions = { const imageGenOptions = {
isAgent: !!agent, isAgent: !!agent,
req: options.req, req: options.req,
fileStrategy: options.fileStrategy, fileStrategy,
processFileURL: options.processFileURL, processFileURL: options.processFileURL,
returnMetadata: options.returnMetadata, returnMetadata: options.returnMetadata,
uploadImageBuffer: options.uploadImageBuffer, uploadImageBuffer: options.uploadImageBuffer,
@ -273,11 +280,10 @@ const loadTools = async ({
}; };
continue; continue;
} else if (tool === Tools.web_search) { } else if (tool === Tools.web_search) {
const webSearchConfig = appConfig?.webSearch;
const result = await loadWebSearchAuth({ const result = await loadWebSearchAuth({
userId: user, userId: user,
loadAuthValues, loadAuthValues,
webSearchConfig, webSearchConfig: webSearch,
}); });
const { onSearchResults, onGetHighlights } = options?.[Tools.web_search] ?? {}; const { onSearchResults, onGetHighlights } = options?.[Tools.web_search] ?? {};
requestedTools[tool] = async () => { requestedTools[tool] = async () => {

View file

@ -158,8 +158,10 @@ const callTool = async (req, res) => {
returnMetadata: true, returnMetadata: true,
processFileURL, processFileURL,
uploadImageBuffer, uploadImageBuffer,
fileStrategy: appConfig.fileStrategy,
}, },
webSearch: appConfig.webSearch,
fileStrategy: appConfig.fileStrategy,
imageOutputType: appConfig.imageOutputType,
}); });
const tool = loadedTools[0]; const tool = loadedTools[0];

View file

@ -75,6 +75,8 @@ const AppService = async () => {
adminFilter: filteredTools, adminFilter: filteredTools,
adminIncluded: includedTools, adminIncluded: includedTools,
directory: paths.structuredTools, directory: paths.structuredTools,
imageOutputType,
fileStrategy,
}); });
await setCachedTools(availableTools, { isGlobal: true }); await setCachedTools(availableTools, { isGlobal: true });

View file

@ -50,11 +50,19 @@ const { redactMessage } = require('~/config/parsers');
* *
* @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 {string} [params.imageOutputType] - The image output type configuration.
* @param {string} [params.fileStrategy] - The file storage strategy.
* @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. * @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 = [], adminIncluded = [] }) { function loadAndFormatTools({
directory,
fileStrategy,
imageOutputType,
adminFilter = [],
adminIncluded = [],
}) {
const filter = new Set([...adminFilter]); const filter = new Set([...adminFilter]);
const included = new Set(adminIncluded); const included = new Set(adminIncluded);
const tools = []; const tools = [];
@ -113,9 +121,14 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] })
} }
/** Basic Tools & Toolkits; schema: { input: string } */ /** Basic Tools & Toolkits; schema: { input: string } */
const openAIImageTools = createOpenAIImageTools({
override: true,
imageOutputType,
fileStrategy,
});
const basicToolInstances = [ const basicToolInstances = [
new Calculator(), new Calculator(),
...createOpenAIImageTools({ override: true }), ...openAIImageTools,
...createYouTubeTools({ override: true }), ...createYouTubeTools({ override: true }),
]; ];
for (const toolInstance of basicToolInstances) { for (const toolInstance of basicToolInstances) {
@ -234,9 +247,11 @@ async function processRequiredActions(client, requiredActions) {
req: client.req, req: client.req,
uploadImageBuffer, uploadImageBuffer,
openAIApiKey: client.apiKey, openAIApiKey: client.apiKey,
fileStrategy: appConfig.fileStrategy,
returnMetadata: true, returnMetadata: true,
}, },
webSearch: appConfig.webSearch,
fileStrategy: appConfig.fileStrategy,
imageOutputType: appConfig.imageOutputType,
}); });
const ToolMap = loadedTools.reduce((map, tool) => { const ToolMap = loadedTools.reduce((map, tool) => {
@ -519,7 +534,7 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey })
if (!_agentTools || _agentTools.length === 0) { if (!_agentTools || _agentTools.length === 0) {
return {}; return {};
} }
/** @type {ReturnType<createOnSearchResults>} */ /** @type {ReturnType<typeof createOnSearchResults>} */
let webSearchCallbacks; let webSearchCallbacks;
if (includesWebSearch) { if (includesWebSearch) {
webSearchCallbacks = createOnSearchResults(res); webSearchCallbacks = createOnSearchResults(res);
@ -538,9 +553,11 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey })
processFileURL, processFileURL,
uploadImageBuffer, uploadImageBuffer,
returnMetadata: true, returnMetadata: true,
fileStrategy: appConfig.fileStrategy,
[Tools.web_search]: webSearchCallbacks, [Tools.web_search]: webSearchCallbacks,
}, },
webSearch: appConfig.webSearch,
fileStrategy: appConfig.fileStrategy,
imageOutputType: appConfig.imageOutputType,
}); });
const agentTools = []; const agentTools = [];

View file

@ -1,6 +1,6 @@
const { nanoid } = require('nanoid'); const { nanoid } = require('nanoid');
const { Tools } = require('librechat-data-provider'); 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 * Creates a function to handle search results and stream them as attachments

View file

@ -917,7 +917,6 @@
* @typedef {Object} ImageGenOptions * @typedef {Object} ImageGenOptions
* @property {ServerRequest} req - The request object. * @property {ServerRequest} req - The request object.
* @property {boolean} isAgent - Whether the request is from an agent. * @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 {processFileURL} processFileURL - The function to process a file URL.
* @property {boolean} returnMetadata - Whether to return metadata. * @property {boolean} returnMetadata - Whether to return metadata.
* @property {uploadImageBuffer} uploadImageBuffer - The function to upload an image buffer. * @property {uploadImageBuffer} uploadImageBuffer - The function to upload an image buffer.
@ -930,6 +929,7 @@
* signal?: AbortSignal, * signal?: AbortSignal,
* memory?: ConversationSummaryBufferMemory, * memory?: ConversationSummaryBufferMemory,
* tool_resources?: AgentToolResources, * tool_resources?: AgentToolResources,
* web_search?: ReturnType<typeof import('~/server/services/Tools/search').createOnSearchResults>,
* }} LoadToolOptions * }} LoadToolOptions
* @memberof typedefs * @memberof typedefs
*/ */
@ -1091,6 +1091,12 @@
* @memberof typedefs * @memberof typedefs
*/ */
/**
* @exports AppConfig
* @typedef {import('@librechat/api').AppConfig} AppConfig
* @memberof typedefs
*/
/** /**
* @exports JsonSchemaType * @exports JsonSchemaType
* @typedef {import('@librechat/api').JsonSchemaType} JsonSchemaType * @typedef {import('@librechat/api').JsonSchemaType} JsonSchemaType

View file

@ -32,3 +32,4 @@ export * from './web';
/* types */ /* types */
export type * from './mcp/types'; export type * from './mcp/types';
export type * from './flow/types'; export type * from './flow/types';
export type * from './types';