From d4bdb3be005870d0c6d06f2854641b718689cf06 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 8 Dec 2025 14:57:14 -0500 Subject: [PATCH] refactor: tool classification and calling logic --- api/server/controllers/agents/callbacks.js | 11 +++++----- .../components/Chat/Messages/Content/Part.tsx | 6 +++++- .../Messages/Content/Parts/ExecuteCode.tsx | 2 +- client/src/locales/en/translation.json | 2 +- packages/api/src/tools/classification.ts | 20 ++++++++++--------- packages/data-provider/src/config.ts | 2 ++ 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/api/server/controllers/agents/callbacks.js b/api/server/controllers/agents/callbacks.js index aee419577a..ff70d13d37 100644 --- a/api/server/controllers/agents/callbacks.js +++ b/api/server/controllers/agents/callbacks.js @@ -1,6 +1,7 @@ const { nanoid } = require('nanoid'); -const { sendEvent, GenerationJobManager } = require('@librechat/api'); +const { Constants } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); +const { sendEvent, GenerationJobManager } = require('@librechat/api'); const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider'); const { EnvVar, @@ -441,10 +442,10 @@ function createToolEndCallback({ req, res, artifactPromises, streamId = null }) return; } - { - if (output.name !== Tools.execute_code) { - return; - } + const isCodeTool = + output.name === Tools.execute_code || output.name === Constants.PROGRAMMATIC_TOOL_CALLING; + if (!isCodeTool) { + return; } if (!output.artifact.files) { diff --git a/client/src/components/Chat/Messages/Content/Part.tsx b/client/src/components/Chat/Messages/Content/Part.tsx index bfa2b28fac..4a74e3606f 100644 --- a/client/src/components/Chat/Messages/Content/Part.tsx +++ b/client/src/components/Chat/Messages/Content/Part.tsx @@ -91,7 +91,11 @@ const Part = memo( const isToolCall = 'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL); - if (isToolCall && toolCall.name === Tools.execute_code) { + if ( + isToolCall && + (toolCall.name === Tools.execute_code || + toolCall.name === Constants.PROGRAMMATIC_TOOL_CALLING) + ) { return ( (0); const prevShowCodeRef = useRef(showCode); - const { lang, code } = useParseArgs(args) ?? ({} as ParsedArgs); + const { lang = 'py', code } = useParseArgs(args) ?? ({} as ParsedArgs); const progress = useProgress(initialProgress); useEffect(() => { diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index c144e8bda5..25ca66d492 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -702,7 +702,7 @@ "com_ui_agents_allow_use": "Allow using Agents", "com_ui_all": "all", "com_ui_all_proper": "All", - "com_ui_analyzing": "Analyzing", + "com_ui_analyzing": "Running tools with code", "com_ui_analyzing_finished": "Finished analyzing", "com_ui_api_key": "API Key", "com_ui_archive": "Archive", diff --git a/packages/api/src/tools/classification.ts b/packages/api/src/tools/classification.ts index 4dccb61a04..8044917381 100644 --- a/packages/api/src/tools/classification.ts +++ b/packages/api/src/tools/classification.ts @@ -282,8 +282,10 @@ export interface BuildToolClassificationResult { /** * Checks if an agent is allowed to have classification features based on TOOL_CLASSIFICATION_AGENT_IDS. + * If TOOL_CLASSIFICATION_AGENT_IDS is not set, all agents are allowed (including when no agentId). + * If set, requires agentId to be in the list. * @param agentId - The agent ID to check - * @returns Whether the agent is allowed (true if no restriction set, or agent is in the list) + * @returns Whether the agent is allowed */ export function isAgentAllowedForClassification(agentId?: string): boolean { const allowedAgentIds = parseToolList(process.env.TOOL_CLASSIFICATION_AGENT_IDS); @@ -344,6 +346,14 @@ export async function buildToolClassification( const { loadedTools, userId, agentId, loadAuthValues } = params; const additionalTools: GenericTool[] = []; + /** Check if this agent is allowed to have classification features (requires agentId) */ + if (!isAgentAllowedForClassification(agentId)) { + logger.debug( + `[buildToolClassification] Agent ${agentId ?? 'undefined'} not allowed for classification, skipping`, + ); + return { toolRegistry: undefined, additionalTools }; + } + const mcpTools = loadedTools.filter(isMCPTool); if (mcpTools.length === 0) { return { toolRegistry: undefined, additionalTools }; @@ -355,14 +365,6 @@ export async function buildToolClassification( /** Clean up temporary mcpJsonSchema property from tools now that registry is populated */ cleanupMCPToolSchemas(mcpTools); - /** Check if this agent is allowed to have classification features */ - if (!isAgentAllowedForClassification(agentId)) { - logger.debug( - `[buildToolClassification] Agent ${agentId} not in TOOL_CLASSIFICATION_AGENT_IDS, skipping PTC/ToolSearch`, - ); - return { toolRegistry, additionalTools }; - } - /** * Check if this agent actually has tools that match the patterns. * Only enable PTC if the agent has programmatic tools. diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index d7b44d3c80..45e86d148b 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -1722,6 +1722,8 @@ export enum Constants { LC_TRANSFER_TO_ = 'lc_transfer_to_', /** Placeholder Agent ID for Ephemeral Agents */ EPHEMERAL_AGENT_ID = 'ephemeral', + /** Programmatic Tool Calling tool name */ + PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code', } export enum LocalStorageKeys {