🔦 fix: Tool resource files not visible in event-driven mode (#11610)

* fix: Execute code files not visible in event-driven mode

Fixes regression from #11588 where primeResources became non-mutating
but callers weren't updated to use returned values.

Changes:
- Add tool_resources to InitializedAgent type and return object
- Prime execute_code files in loadToolDefinitionsWrapper
- Pass tool_resources to loadToolDefinitionsWrapper
- Capture and return toolContextMap from loadToolsForExecution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: Reorganize imports and enhance tool loading logic in ToolService.js

- Moved domainSeparatorRegex declaration to a more appropriate location for clarity.
- Reorganized import statements for better readability and maintainability.
- Removed unused variables and streamlined the loadToolsForExecution function by eliminating the regularToolContextMap, improving performance and code clarity.
- Updated loadActionToolsForExecution to ensure consistent handling of domain separator regex.

This refactor improves the overall structure and efficiency of the ToolService module.

* fix: file search tool priming in loadToolDefinitionsWrapper

- Added functionality to prime file search tools within the loadToolDefinitionsWrapper function, enhancing the tool context map for event-driven mode.
- Implemented error handling for the file search priming process to improve robustness and logging.
- Updated the tool context map to include the newly primed file search tool, ensuring it is available for subsequent operations.

This enhancement improves the tool loading capabilities by incorporating file search tools, facilitating better integration and functionality in the application.

* chore: import order

* refactor: Update agent initialization structure for improved clarity and functionality

- Rearranged properties in the InitializedAgent object to enhance readability and maintainability.
- Moved toolRegistry to the correct position and ensured tools and attachments are set appropriately.
- This refactor improves the overall structure of the agent initialization process, facilitating better integration and future enhancements.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Joseph Licata 2026-02-04 04:11:47 -05:00 committed by GitHub
parent 9f23dd38e7
commit 5cf50dd15e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 14 deletions

View file

@ -1,3 +1,5 @@
const { logger } = require('@librechat/data-schemas');
const { tool: toolFn, DynamicStructuredTool } = require('@langchain/core/tools');
const {
sleep,
EnvVar,
@ -7,8 +9,6 @@ const {
Constants: AgentConstants,
createProgrammaticToolCallingTool,
} = require('@librechat/agents');
const { logger } = require('@librechat/data-schemas');
const { tool: toolFn, DynamicStructuredTool } = require('@langchain/core/tools');
const {
sendEvent,
getToolkitKey,
@ -38,22 +38,20 @@ const {
defaultAgentCapabilities,
validateAndParseOpenAPISpec,
} = require('librechat-data-provider');
const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g');
const {
createActionTool,
decryptMetadata,
loadActionSets,
domainParser,
} = require('./ActionService');
const { processFileURL, uploadImageBuffer } = require('~/server/services/Files/process');
const {
getEndpointsConfig,
getCachedTools,
getMCPServerTools,
getCachedTools,
} = require('~/server/services/Config');
const { getFlowStateManager } = require('~/config');
const { getLogStores } = require('~/cache');
const { processFileURL, uploadImageBuffer } = require('~/server/services/Files/process');
const { primeFiles: primeSearchFiles } = require('~/app/clients/tools/util/fileSearch');
const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process');
const { manifestToolMap, toolkits } = require('~/app/clients/tools/manifest');
const { createOnSearchResults } = require('~/server/services/Tools/search');
const { loadAuthValues } = require('~/server/services/Tools/credentials');
@ -62,6 +60,8 @@ const { recordUsage } = require('~/server/services/Threads');
const { loadTools } = require('~/app/clients/tools/util');
const { redactMessage } = require('~/config/parsers');
const { findPluginAuthsByKeys } = require('~/models');
const { getFlowStateManager } = require('~/config');
const { getLogStores } = require('~/cache');
/**
* Processes the required actions by calling the appropriate tools and returning the outputs.
* @param {OpenAIClient} client - OpenAI or StreamRunManager Client.
@ -428,7 +428,7 @@ const isBuiltInTool = (toolName) =>
* hasDeferredTools?: boolean;
* }>}
*/
async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null }) {
async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, tool_resources }) {
if (!agent.tools || agent.tools.length === 0) {
return { toolDefinitions: [] };
}
@ -587,6 +587,7 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null })
const { functionSignatures } = openapiToFunction(validationResult.spec, true);
const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g');
for (const sig of functionSignatures) {
const toolName = `${sig.name}${actionDelimiter}${normalizedDomain}`;
if (!actionToolNames.some((name) => name.replace(domainSeparatorRegex, '_') === toolName)) {
@ -679,9 +680,52 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null })
}
}
/** @type {Record<string, string>} */
const toolContextMap = {};
const hasFileSearch = filteredTools.includes(Tools.file_search);
const hasExecuteCode = filteredTools.includes(Tools.execute_code);
if (hasExecuteCode && tool_resources) {
try {
const authValues = await loadAuthValues({
userId: req.user.id,
authFields: [EnvVar.CODE_API_KEY],
});
const codeApiKey = authValues[EnvVar.CODE_API_KEY];
if (codeApiKey) {
const { toolContext } = await primeCodeFiles(
{ req, tool_resources, agentId: agent.id },
codeApiKey,
);
if (toolContext) {
toolContextMap[Tools.execute_code] = toolContext;
}
}
} catch (error) {
logger.error('[loadToolDefinitionsWrapper] Error priming code files:', error);
}
}
if (hasFileSearch && tool_resources) {
try {
const { toolContext } = await primeSearchFiles({
req,
tool_resources,
agentId: agent.id,
});
if (toolContext) {
toolContextMap[Tools.file_search] = toolContext;
}
} catch (error) {
logger.error('[loadToolDefinitionsWrapper] Error priming search files:', error);
}
}
return {
toolRegistry,
userMCPAuthMap,
toolContextMap,
toolDefinitions,
hasDeferredTools,
};
@ -712,7 +756,7 @@ async function loadAgentTools({
definitionsOnly = true,
}) {
if (definitionsOnly) {
return loadToolDefinitionsWrapper({ req, res, agent, streamId });
return loadToolDefinitionsWrapper({ req, res, agent, streamId, tool_resources });
}
if (!agent.tools || agent.tools.length === 0) {
@ -1112,6 +1156,7 @@ async function loadToolsForExecution({
const actionToolNames = allToolNamesToLoad.filter((name) => name.includes(actionDelimiter));
const regularToolNames = allToolNamesToLoad.filter((name) => !name.includes(actionDelimiter));
/** @type {Record<string, unknown>} */
if (regularToolNames.length > 0) {
const includesWebSearch = regularToolNames.includes(Tools.web_search);
const webSearchCallbacks = includesWebSearch ? createOnSearchResults(res, streamId) : undefined;
@ -1126,10 +1171,10 @@ async function loadToolsForExecution({
options: {
req,
res,
tool_resources,
processFileURL,
uploadImageBuffer,
returnMetadata: true,
tool_resources,
[Tools.web_search]: webSearchCallbacks,
},
webSearch: appConfig?.webSearch,
@ -1270,6 +1315,7 @@ async function loadActionToolsForExecution({
const { action, encrypted, zodSchemas, requestBuilders, functionSignatures } =
processedActionSets.get(currentDomain);
const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g');
const normalizedDomain = currentDomain.replace(domainSeparatorRegex, '_');
const functionName = toolName.replace(`${actionDelimiter}${normalizedDomain}`, '');
const functionSig = functionSignatures.find((sig) => sig.name === functionName);