fix(tool-input): allow multiple instances of workflow block or kb tools as agent tools (#2495)

* fix(tool-input): allow multiple instances of workflow block or kb tools as agent tools

* ack PR comments
This commit is contained in:
Waleed
2025-12-19 19:19:42 -08:00
committed by GitHub
parent 93fe68785e
commit 7dc48510dc
6 changed files with 62 additions and 18 deletions

View File

@@ -982,6 +982,11 @@ export function ToolInput({
if (hasMultipleOperations(blockType)) {
return false
}
// Allow multiple instances for workflow and knowledge blocks
// Each instance can target a different workflow/knowledge base
if (blockType === 'workflow' || blockType === 'knowledge') {
return false
}
return selectedTools.some((tool) => tool.toolId === toolId)
}

View File

@@ -720,8 +720,10 @@ const PopoverSearch = React.forwardRef<HTMLDivElement, PopoverSearchProps>(
}
React.useEffect(() => {
setSearchQuery('')
onValueChange?.('')
inputRef.current?.focus()
}, [])
}, [setSearchQuery, onValueChange])
return (
<div ref={ref} className={cn('flex items-center px-[8px] py-[6px]', className)} {...props}>

View File

@@ -479,8 +479,16 @@ export async function transformBlockTool(
const llmSchema = await createLLMToolSchema(toolConfig, userProvidedParams)
// Create unique tool ID by appending resource ID for multi-instance tools
let uniqueToolId = toolConfig.id
if (toolId === 'workflow_executor' && userProvidedParams.workflowId) {
uniqueToolId = `${toolConfig.id}_${userProvidedParams.workflowId}`
} else if (toolId.startsWith('knowledge_') && userProvidedParams.knowledgeBaseId) {
uniqueToolId = `${toolConfig.id}_${userProvidedParams.knowledgeBaseId}`
}
return {
id: toolConfig.id,
id: uniqueToolId,
name: toolConfig.name,
description: toolConfig.description,
params: userProvidedParams,

View File

@@ -16,6 +16,26 @@ import {
const logger = createLogger('Tools')
/**
* Normalizes a tool ID by stripping resource ID suffix (UUID).
* Workflow tools: 'workflow_executor_<uuid>' -> 'workflow_executor'
* Knowledge tools: 'knowledge_search_<uuid>' -> 'knowledge_search'
*/
function normalizeToolId(toolId: string): string {
// Check for workflow_executor_<uuid> pattern
if (toolId.startsWith('workflow_executor_') && toolId.length > 'workflow_executor_'.length) {
return 'workflow_executor'
}
// Check for knowledge_<operation>_<uuid> pattern
const knowledgeOps = ['knowledge_search', 'knowledge_upload_chunk', 'knowledge_create_document']
for (const op of knowledgeOps) {
if (toolId.startsWith(`${op}_`) && toolId.length > op.length + 1) {
return op
}
}
return toolId
}
/**
* Maximum request body size in bytes before we warn/error about size limits.
* Next.js 16 has a default middleware/proxy body limit of 10MB.
@@ -186,20 +206,29 @@ export async function executeTool(
try {
let tool: ToolConfig | undefined
// Normalize tool ID to strip resource suffixes (e.g., workflow_executor_<uuid> -> workflow_executor)
const normalizedToolId = normalizeToolId(toolId)
// If it's a custom tool, use the async version with workflowId
if (toolId.startsWith('custom_')) {
if (normalizedToolId.startsWith('custom_')) {
const workflowId = params._context?.workflowId
tool = await getToolAsync(toolId, workflowId)
tool = await getToolAsync(normalizedToolId, workflowId)
if (!tool) {
logger.error(`[${requestId}] Custom tool not found: ${toolId}`)
logger.error(`[${requestId}] Custom tool not found: ${normalizedToolId}`)
}
} else if (toolId.startsWith('mcp-')) {
return await executeMcpTool(toolId, params, executionContext, requestId, startTimeISO)
} else if (normalizedToolId.startsWith('mcp-')) {
return await executeMcpTool(
normalizedToolId,
params,
executionContext,
requestId,
startTimeISO
)
} else {
// For built-in tools, use the synchronous version
tool = getTool(toolId)
tool = getTool(normalizedToolId)
if (!tool) {
logger.error(`[${requestId}] Built-in tool not found: ${toolId}`)
logger.error(`[${requestId}] Built-in tool not found: ${normalizedToolId}`)
}
}