diff --git a/frontend/src/lib/components/copilot/MetadataGen.svelte b/frontend/src/lib/components/copilot/MetadataGen.svelte index bb18b3dc1e..1ef4b2ff08 100644 --- a/frontend/src/lib/components/copilot/MetadataGen.svelte +++ b/frontend/src/lib/components/copilot/MetadataGen.svelte @@ -12,7 +12,7 @@ import { yamlStringifyExceptKeys } from './utils' import type { ChatCompletionMessageParam } from 'openai/resources/index.mjs' import { triggerableByAI } from '$lib/actions/triggerableByAI.svelte' - import { validateToolName } from '$lib/components/graph/renderers/nodes/AIToolNode.svelte' + import { getToolNameError } from '$lib/components/graph/renderers/nodes/AIToolNode.svelte' import { inputBaseClass, inputBorderClass, @@ -117,6 +117,7 @@ Generate a tool name for the script below: elementProps?: Record class?: string onChange?: (content: string) => void + siblingToolNames?: string[] } let { @@ -130,9 +131,16 @@ Generate a tool name for the script below: elementType = 'input', elementProps = {}, class: clazz = '', - onChange = undefined + onChange = undefined, + siblingToolNames = undefined }: Props = $props() + let toolNameError = $derived( + promptConfigName === 'agentToolFunctionName' + ? getToolNameError(content ?? '', undefined, siblingToolNames) + : undefined + ) + let el: HTMLElement | undefined = $state() let generatedContent = $state('') let active = $state(false) @@ -347,16 +355,16 @@ Generate a tool name for the script below: inputBaseClass, inputSizeClasses.md, inputBorderClass({ - error: promptConfigName === 'agentToolFunctionName' && !validateToolName(content ?? '') + error: !!toolNameError }), 'w-full' )} onfocus={() => (focused = true)} onblur={() => (focused = false)} /> - {#if promptConfigName === 'agentToolFunctionName' && !validateToolName(content ?? '')} + {#if toolNameError}

- Invalid tool name, should only contain letters, numbers and underscores + {toolNameError}

{/if} {/if} diff --git a/frontend/src/lib/components/flows/common/FlowCard.svelte b/frontend/src/lib/components/flows/common/FlowCard.svelte index f780331a82..43cabc7aa0 100644 --- a/frontend/src/lib/components/flows/common/FlowCard.svelte +++ b/frontend/src/lib/components/flows/common/FlowCard.svelte @@ -12,6 +12,7 @@ action?: import('svelte').Snippet children?: import('svelte').Snippet isAgentTool?: boolean + siblingToolNames?: string[] } let { @@ -23,7 +24,8 @@ header, action, children, - isAgentTool = false + isAgentTool = false, + siblingToolNames = undefined }: Props = $props() @@ -38,6 +40,7 @@ {flowModuleValue} {action} {isAgentTool} + {siblingToolNames} > {@render header?.()} diff --git a/frontend/src/lib/components/flows/common/FlowCardHeader.svelte b/frontend/src/lib/components/flows/common/FlowCardHeader.svelte index 401bf232b1..a5ff48053e 100644 --- a/frontend/src/lib/components/flows/common/FlowCardHeader.svelte +++ b/frontend/src/lib/components/flows/common/FlowCardHeader.svelte @@ -18,7 +18,7 @@ import { Flag, Lock, RefreshCw, Unlock } from 'lucide-svelte' import { createEventDispatcher, untrack } from 'svelte' import { twMerge } from 'tailwind-merge' - import { validateToolName } from '$lib/components/graph/renderers/nodes/AIToolNode.svelte' + import { getToolNameError } from '$lib/components/graph/renderers/nodes/AIToolNode.svelte' import { DEFAULT_HUB_BASE_URL, PRIVATE_HUB_MIN_VERSION } from '$lib/hub' interface Props { @@ -28,6 +28,7 @@ children?: import('svelte').Snippet action?: import('svelte').Snippet isAgentTool?: boolean + siblingToolNames?: string[] } let { @@ -36,9 +37,14 @@ summary = $bindable(undefined), children, action, - isAgentTool = false + isAgentTool = false, + siblingToolNames = undefined }: Props = $props() + let toolNameError = $derived( + isAgentTool ? getToolNameError(summary ?? '', undefined, siblingToolNames) : undefined + ) + let latestHash: string | undefined = $state(undefined) // Extract version_id from hub path (format: hub/{version_id}/{app}/{summary}) @@ -103,6 +109,7 @@ elementProps={{ placeholder: isAgentTool ? 'Tool name' : 'Summary' }} + {siblingToolNames} /> {:else if flowModuleValue.type === 'script' && 'path' in flowModuleValue && flowModuleValue.path} @@ -173,14 +180,16 @@ /> {/if} - +
+ + {#if toolNameError} +

{toolNameError}

+ {/if} +
{:else if flowModuleValue.type === 'flow'} flow diff --git a/frontend/src/lib/components/flows/content/AgentToolWrapper.svelte b/frontend/src/lib/components/flows/content/AgentToolWrapper.svelte index d624c2132f..9807e96603 100644 --- a/frontend/src/lib/components/flows/content/AgentToolWrapper.svelte +++ b/frontend/src/lib/components/flows/content/AgentToolWrapper.svelte @@ -14,6 +14,7 @@ previousModule?: FlowModule | undefined forceTestTab?: Record highlightArg?: Record + siblingToolNames?: string[] } let { @@ -23,7 +24,8 @@ parentModule = undefined, previousModule = undefined, forceTestTab, - highlightArg + highlightArg, + siblingToolNames = undefined }: Props = $props() @@ -43,6 +45,7 @@ forceTestTab={forceTestTab?.[tool.id]} highlightArg={highlightArg?.[tool.id]} isAgentTool={true} + {siblingToolNames} /> {:else if isMcpTool(tool)} diff --git a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte index c8418e5183..3d9a533f48 100644 --- a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte +++ b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte @@ -101,9 +101,7 @@ ) let canMoveSelected = $derived( resolvedModuleIds.length > 0 && - areContiguousSiblings( - locateModules(resolvedModuleIds, flowStore.val.value.modules ?? []) - ) + areContiguousSiblings(locateModules(resolvedModuleIds, flowStore.val.value.modules ?? [])) ) diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index 4a43c2c38d..d9da29f01e 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -110,6 +110,7 @@ forceTestTab?: boolean highlightArg?: string isAgentTool?: boolean + siblingToolNames?: string[] } let { @@ -125,7 +126,8 @@ savedModule = undefined, forceTestTab = false, highlightArg = undefined, - isAgentTool = false + isAgentTool = false, + siblingToolNames = undefined }: Props = $props() let workspaceScriptTag: string | undefined = $state(undefined) @@ -237,7 +239,9 @@ } let forceReload = $state(0) - let editorPanelSize = $state(untrack(() => noEditor) ? 0 : flowModule.value.type == 'script' ? 30 : 50) + let editorPanelSize = $state( + untrack(() => noEditor) ? 0 : flowModule.value.type == 'script' ? 30 : 50 + ) let editorSettingsPanelSize = $state(100 - untrack(() => editorPanelSize)) let stepHistoryLoader = getStepHistoryLoaderContext() @@ -726,6 +730,7 @@ }} bind:summary={flowModule.summary} {isAgentTool} + {siblingToolNames} > {#snippet header()} diff --git a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte index cb50e8a327..c58819d59c 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte @@ -305,6 +305,7 @@ {enableAi} {forceTestTab} {highlightArg} + siblingToolNames={flowModule.value.tools.map((t) => t.summary ?? '')} /> {/if} {/each} diff --git a/frontend/src/lib/components/graph/graphBuilder.svelte.ts b/frontend/src/lib/components/graph/graphBuilder.svelte.ts index 1d01b2e252..192b5c0c34 100644 --- a/frontend/src/lib/components/graph/graphBuilder.svelte.ts +++ b/frontend/src/lib/components/graph/graphBuilder.svelte.ts @@ -300,6 +300,7 @@ export type AiToolN = { data: { tool: string type?: string + nameError?: string eventHandlers: GraphEventHandlers moduleId: string insertable: boolean diff --git a/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte index 28e9d58fa4..1913d927c8 100644 --- a/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte +++ b/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte @@ -1,10 +1,29 @@