mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-09 22:25:33 -05:00
Compare commits
1 Commits
improvemen
...
sim-609-to
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7210330f75 |
@@ -0,0 +1,410 @@
|
||||
'use client'
|
||||
|
||||
import type React from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { Combobox, Switch } from '@/components/emcn'
|
||||
import {
|
||||
CheckboxList,
|
||||
Code,
|
||||
DocumentSelector,
|
||||
DocumentTagEntry,
|
||||
FileSelectorInput,
|
||||
FileUpload,
|
||||
FolderSelectorInput,
|
||||
KnowledgeBaseSelector,
|
||||
KnowledgeTagFilters,
|
||||
LongInput,
|
||||
ProjectSelectorInput,
|
||||
SheetSelectorInput,
|
||||
ShortInput,
|
||||
SlackSelectorInput,
|
||||
SliderInput,
|
||||
Table,
|
||||
TimeInput,
|
||||
WorkflowSelectorInput,
|
||||
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
|
||||
import type { WandControlHandlers } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block'
|
||||
import type { SubBlockConfig as BlockSubBlockConfig } from '@/blocks/types'
|
||||
import { isPasswordParameter } from '@/tools/params'
|
||||
|
||||
interface ToolSubBlockRendererProps {
|
||||
blockId: string
|
||||
subBlockId: string
|
||||
toolIndex: number
|
||||
subBlock: BlockSubBlockConfig
|
||||
effectiveParamId: string
|
||||
toolParams: Record<string, string> | undefined
|
||||
onParamChange: (toolIndex: number, paramId: string, value: string) => void
|
||||
disabled: boolean
|
||||
previewContextValues?: Record<string, unknown>
|
||||
wandControlRef?: React.MutableRefObject<WandControlHandlers | null>
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a subblock component inside tool-input by bridging the subblock store
|
||||
* with StoredTool.params via a synthetic store key.
|
||||
*
|
||||
* Replaces the 17+ individual SyncWrapper components that previously existed.
|
||||
* Components read/write to the store at a synthetic ID, and two effects
|
||||
* handle bidirectional sync with tool.params.
|
||||
*/
|
||||
export function ToolSubBlockRenderer({
|
||||
blockId,
|
||||
subBlockId,
|
||||
toolIndex,
|
||||
subBlock,
|
||||
effectiveParamId,
|
||||
toolParams,
|
||||
onParamChange,
|
||||
disabled,
|
||||
previewContextValues,
|
||||
wandControlRef,
|
||||
}: ToolSubBlockRendererProps) {
|
||||
const syntheticId = `${subBlockId}-tool-${toolIndex}-${effectiveParamId}`
|
||||
const [storeValue, setStoreValue] = useSubBlockValue(blockId, syntheticId)
|
||||
|
||||
// Gate the component using the same dependsOn logic as SubBlock
|
||||
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
|
||||
disabled,
|
||||
previewContextValues,
|
||||
})
|
||||
|
||||
const toolParamValue = toolParams?.[effectiveParamId] ?? ''
|
||||
|
||||
/** Tracks the last value we wrote to the store from tool.params to avoid echo loops */
|
||||
const lastInitRef = useRef<string>(toolParamValue)
|
||||
/** Tracks the last value we synced back to tool.params from the store */
|
||||
const lastSyncRef = useRef<string>(toolParamValue)
|
||||
|
||||
// Init effect: push tool.params value into the store when it changes externally
|
||||
useEffect(() => {
|
||||
if (toolParamValue !== lastInitRef.current) {
|
||||
lastInitRef.current = toolParamValue
|
||||
lastSyncRef.current = toolParamValue
|
||||
setStoreValue(toolParamValue)
|
||||
}
|
||||
}, [toolParamValue, setStoreValue])
|
||||
|
||||
// Sync effect: when the store changes (user interaction), push back to tool.params
|
||||
useEffect(() => {
|
||||
if (storeValue == null) return
|
||||
const stringValue = typeof storeValue === 'string' ? storeValue : JSON.stringify(storeValue)
|
||||
if (stringValue !== lastSyncRef.current) {
|
||||
lastSyncRef.current = stringValue
|
||||
lastInitRef.current = stringValue
|
||||
onParamChange(toolIndex, effectiveParamId, stringValue)
|
||||
}
|
||||
}, [storeValue, toolIndex, effectiveParamId, onParamChange])
|
||||
|
||||
// Initialize the store on first mount
|
||||
const hasInitializedRef = useRef(false)
|
||||
useEffect(() => {
|
||||
if (!hasInitializedRef.current && toolParamValue) {
|
||||
hasInitializedRef.current = true
|
||||
setStoreValue(toolParamValue)
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const configWithSyntheticId = { ...subBlock, id: syntheticId }
|
||||
|
||||
return renderSubBlockComponent({
|
||||
blockId,
|
||||
syntheticId,
|
||||
config: configWithSyntheticId,
|
||||
subBlock,
|
||||
disabled: finalDisabled,
|
||||
previewContextValues,
|
||||
wandControlRef,
|
||||
toolParamValue,
|
||||
onParamChange: useCallback(
|
||||
(value: string) => onParamChange(toolIndex, effectiveParamId, value),
|
||||
[toolIndex, effectiveParamId, onParamChange]
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
interface RenderContext {
|
||||
blockId: string
|
||||
syntheticId: string
|
||||
config: BlockSubBlockConfig
|
||||
subBlock: BlockSubBlockConfig
|
||||
disabled: boolean
|
||||
previewContextValues?: Record<string, unknown>
|
||||
wandControlRef?: React.MutableRefObject<WandControlHandlers | null>
|
||||
toolParamValue: string
|
||||
onParamChange: (value: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the appropriate component for a subblock type.
|
||||
* Mirrors the switch cases in SubBlock's renderInput(), using
|
||||
* the same component props pattern.
|
||||
*/
|
||||
function renderSubBlockComponent(ctx: RenderContext): React.ReactNode {
|
||||
const {
|
||||
blockId,
|
||||
syntheticId,
|
||||
config,
|
||||
subBlock,
|
||||
disabled,
|
||||
previewContextValues,
|
||||
wandControlRef,
|
||||
toolParamValue,
|
||||
onParamChange,
|
||||
} = ctx
|
||||
|
||||
switch (subBlock.type) {
|
||||
case 'short-input':
|
||||
return (
|
||||
<ShortInput
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
placeholder={subBlock.placeholder}
|
||||
password={subBlock.password || isPasswordParameter(subBlock.id)}
|
||||
config={config}
|
||||
disabled={disabled}
|
||||
wandControlRef={wandControlRef}
|
||||
hideInternalWand={true}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'long-input':
|
||||
return (
|
||||
<LongInput
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
placeholder={subBlock.placeholder}
|
||||
rows={subBlock.rows}
|
||||
config={config}
|
||||
disabled={disabled}
|
||||
wandControlRef={wandControlRef}
|
||||
hideInternalWand={true}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'dropdown':
|
||||
return (
|
||||
<Combobox
|
||||
options={
|
||||
(subBlock.options as { label: string; id: string }[] | undefined)
|
||||
?.filter((option) => option.id !== '')
|
||||
.map((option) => ({
|
||||
label: option.label,
|
||||
value: option.id,
|
||||
})) || []
|
||||
}
|
||||
value={toolParamValue}
|
||||
onChange={onParamChange}
|
||||
placeholder={subBlock.placeholder || 'Select option'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'switch':
|
||||
return (
|
||||
<Switch
|
||||
checked={toolParamValue === 'true' || toolParamValue === 'True'}
|
||||
onCheckedChange={(checked) => onParamChange(checked ? 'true' : 'false')}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'code':
|
||||
return (
|
||||
<Code
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
placeholder={subBlock.placeholder}
|
||||
language={subBlock.language}
|
||||
generationType={subBlock.generationType}
|
||||
value={typeof subBlock.value === 'function' ? subBlock.value({}) : undefined}
|
||||
disabled={disabled}
|
||||
wandConfig={
|
||||
subBlock.wandConfig || {
|
||||
enabled: false,
|
||||
prompt: '',
|
||||
placeholder: '',
|
||||
}
|
||||
}
|
||||
wandControlRef={wandControlRef}
|
||||
hideInternalWand={true}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'channel-selector':
|
||||
case 'user-selector':
|
||||
return (
|
||||
<SlackSelectorInput
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'project-selector':
|
||||
return (
|
||||
<ProjectSelectorInput
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'file-selector':
|
||||
return (
|
||||
<FileSelectorInput
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'sheet-selector':
|
||||
return (
|
||||
<SheetSelectorInput
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'folder-selector':
|
||||
return (
|
||||
<FolderSelectorInput
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'knowledge-base-selector':
|
||||
return <KnowledgeBaseSelector blockId={blockId} subBlock={config} disabled={disabled} />
|
||||
|
||||
case 'document-selector':
|
||||
return (
|
||||
<DocumentSelector
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'document-tag-entry':
|
||||
return (
|
||||
<DocumentTagEntry
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'knowledge-tag-filters':
|
||||
return (
|
||||
<KnowledgeTagFilters
|
||||
blockId={blockId}
|
||||
subBlock={config}
|
||||
disabled={disabled}
|
||||
previewContextValues={previewContextValues}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'table':
|
||||
return (
|
||||
<Table
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
columns={subBlock.columns ?? []}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'slider':
|
||||
return (
|
||||
<SliderInput
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
min={subBlock.min}
|
||||
max={subBlock.max}
|
||||
step={subBlock.step}
|
||||
integer={subBlock.integer}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'checkbox-list':
|
||||
return (
|
||||
<CheckboxList
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
title={subBlock.title ?? ''}
|
||||
options={subBlock.options as { label: string; id: string }[]}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'time-input':
|
||||
return (
|
||||
<TimeInput
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
placeholder={subBlock.placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'file-upload':
|
||||
return (
|
||||
<FileUpload
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
acceptedTypes={subBlock.acceptedTypes || '*'}
|
||||
multiple={subBlock.multiple === true}
|
||||
maxSize={subBlock.maxSize}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'combobox':
|
||||
return (
|
||||
<Combobox
|
||||
options={((subBlock.options as { label: string; id: string }[] | undefined) || []).map(
|
||||
(opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.id,
|
||||
})
|
||||
)}
|
||||
value={toolParamValue}
|
||||
onChange={onParamChange}
|
||||
placeholder={subBlock.placeholder || 'Select option'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'workflow-selector':
|
||||
return <WorkflowSelectorInput blockId={blockId} subBlock={config} disabled={disabled} />
|
||||
|
||||
case 'oauth-input':
|
||||
// OAuth inputs are handled separately by ToolCredentialSelector in the parent
|
||||
return null
|
||||
|
||||
default:
|
||||
return (
|
||||
<ShortInput
|
||||
blockId={blockId}
|
||||
subBlockId={syntheticId}
|
||||
placeholder={subBlock.placeholder}
|
||||
password={isPasswordParameter(subBlock.id)}
|
||||
config={config}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -196,6 +196,8 @@ export interface SubBlockConfig {
|
||||
type: SubBlockType
|
||||
mode?: 'basic' | 'advanced' | 'both' | 'trigger' // Default is 'both' if not specified. 'trigger' means only shown in trigger mode
|
||||
canonicalParamId?: string
|
||||
/** Controls parameter visibility in agent/tool-input context */
|
||||
paramVisibility?: 'user-or-llm' | 'user-only' | 'llm-only' | 'hidden'
|
||||
required?:
|
||||
| boolean
|
||||
| {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
buildCanonicalIndex,
|
||||
type CanonicalIndex,
|
||||
type CanonicalModeOverrides,
|
||||
evaluateSubBlockCondition,
|
||||
getCanonicalValues,
|
||||
isCanonicalPair,
|
||||
@@ -12,7 +13,10 @@ import type { SubBlockConfig as BlockSubBlockConfig } from '@/blocks/types'
|
||||
export {
|
||||
buildCanonicalIndex,
|
||||
type CanonicalIndex,
|
||||
type CanonicalModeOverrides,
|
||||
evaluateSubBlockCondition,
|
||||
isCanonicalPair,
|
||||
resolveCanonicalMode,
|
||||
type SubBlockCondition,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format'
|
||||
import {
|
||||
buildCanonicalIndex,
|
||||
type CanonicalModeOverrides,
|
||||
evaluateSubBlockCondition,
|
||||
isCanonicalPair,
|
||||
resolveCanonicalMode,
|
||||
type SubBlockCondition,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
import type { SubBlockConfig as BlockSubBlockConfig } from '@/blocks/types'
|
||||
import type { SubBlockConfig as BlockSubBlockConfig, GenerationType } from '@/blocks/types'
|
||||
import { safeAssign } from '@/tools/safe-assign'
|
||||
import { isEmptyTagValue } from '@/tools/shared/tags'
|
||||
import type { ParameterVisibility, ToolConfig } from '@/tools/types'
|
||||
import type { OAuthConfig, ParameterVisibility, ToolConfig } from '@/tools/types'
|
||||
import { getTool } from '@/tools/utils'
|
||||
|
||||
const logger = createLogger('ToolsParams')
|
||||
@@ -64,6 +68,14 @@ export interface UIComponentConfig {
|
||||
mode?: 'basic' | 'advanced' | 'both' | 'trigger'
|
||||
/** The actual subblock ID this config was derived from */
|
||||
actualSubBlockId?: string
|
||||
/** Wand configuration for AI assistance */
|
||||
wandConfig?: {
|
||||
enabled: boolean
|
||||
prompt: string
|
||||
generationType?: GenerationType
|
||||
placeholder?: string
|
||||
maintainHistory?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface SubBlockConfig {
|
||||
@@ -327,6 +339,7 @@ export function getToolParametersConfig(
|
||||
canonicalParamId: subBlock.canonicalParamId,
|
||||
mode: subBlock.mode,
|
||||
actualSubBlockId: subBlock.id,
|
||||
wandConfig: subBlock.wandConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -811,3 +824,196 @@ export function formatParameterLabel(paramId: string): string {
|
||||
// Simple case - just capitalize first letter
|
||||
return paramId.charAt(0).toUpperCase() + paramId.slice(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* SubBlock IDs that are "structural" — they control tool routing or auth,
|
||||
* not user-facing parameters. These are excluded from tool-input rendering
|
||||
* unless they have an explicit paramVisibility set.
|
||||
*/
|
||||
const STRUCTURAL_SUBBLOCK_IDS = new Set(['operation', 'authMethod', 'destinationType'])
|
||||
|
||||
/**
|
||||
* SubBlock types that represent auth/credential inputs handled separately
|
||||
* by the tool-input OAuth credential selector.
|
||||
*/
|
||||
const AUTH_SUBBLOCK_TYPES = new Set(['oauth-input'])
|
||||
|
||||
/**
|
||||
* SubBlock types that should never appear in tool-input context.
|
||||
*/
|
||||
const EXCLUDED_SUBBLOCK_TYPES = new Set([
|
||||
'tool-input',
|
||||
'skill-input',
|
||||
'condition-input',
|
||||
'eval-input',
|
||||
'webhook-config',
|
||||
'schedule-info',
|
||||
'trigger-save',
|
||||
'input-format',
|
||||
'response-format',
|
||||
'mcp-server-selector',
|
||||
'mcp-tool-selector',
|
||||
'mcp-dynamic-args',
|
||||
'input-mapping',
|
||||
'variables-input',
|
||||
'messages-input',
|
||||
'router-input',
|
||||
'text',
|
||||
])
|
||||
|
||||
export interface SubBlocksForToolInput {
|
||||
toolConfig: ToolConfig
|
||||
subBlocks: BlockSubBlockConfig[]
|
||||
oauthConfig?: OAuthConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filtered SubBlockConfig[] for rendering in tool-input context.
|
||||
* Uses subblock definitions as the primary source of UI metadata,
|
||||
* getting all features (wandConfig, rich conditions, dependsOn, etc.) for free.
|
||||
*
|
||||
* For blocks without paramVisibility annotations, falls back to inferring
|
||||
* visibility from the tool's param definitions.
|
||||
*/
|
||||
export function getSubBlocksForToolInput(
|
||||
toolId: string,
|
||||
blockType: string,
|
||||
currentValues?: Record<string, unknown>,
|
||||
canonicalModeOverrides?: CanonicalModeOverrides
|
||||
): SubBlocksForToolInput | null {
|
||||
try {
|
||||
const toolConfig = getTool(toolId)
|
||||
if (!toolConfig) {
|
||||
logger.warn(`Tool not found: ${toolId}`)
|
||||
return null
|
||||
}
|
||||
|
||||
const blockConfigs = getBlockConfigurations()
|
||||
const blockConfig = blockConfigs[blockType]
|
||||
if (!blockConfig?.subBlocks?.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const allSubBlocks = blockConfig.subBlocks as BlockSubBlockConfig[]
|
||||
const canonicalIndex = buildCanonicalIndex(allSubBlocks)
|
||||
|
||||
// Build values for condition evaluation
|
||||
const values = currentValues || {}
|
||||
const valuesWithOperation = { ...values }
|
||||
if (valuesWithOperation.operation === undefined) {
|
||||
const parts = toolId.split('_')
|
||||
valuesWithOperation.operation =
|
||||
parts.length >= 3 ? parts.slice(2).join('_') : parts[parts.length - 1]
|
||||
}
|
||||
|
||||
// Build a set of param IDs from the tool config for fallback visibility inference
|
||||
const toolParamIds = new Set(Object.keys(toolConfig.params || {}))
|
||||
const toolParamVisibility: Record<string, ParameterVisibility> = {}
|
||||
for (const [paramId, param] of Object.entries(toolConfig.params || {})) {
|
||||
toolParamVisibility[paramId] =
|
||||
param.visibility ?? (param.required ? 'user-or-llm' : 'user-only')
|
||||
}
|
||||
|
||||
// Track which canonical groups we've already included (to avoid duplicates)
|
||||
const includedCanonicalIds = new Set<string>()
|
||||
|
||||
const filtered: BlockSubBlockConfig[] = []
|
||||
|
||||
for (const sb of allSubBlocks) {
|
||||
// Skip excluded types
|
||||
if (EXCLUDED_SUBBLOCK_TYPES.has(sb.type)) continue
|
||||
|
||||
// Skip trigger-mode-only subblocks
|
||||
if (sb.mode === 'trigger') continue
|
||||
|
||||
// Determine the effective param ID (canonical or subblock id)
|
||||
const effectiveParamId = sb.canonicalParamId || sb.id
|
||||
|
||||
// Resolve paramVisibility: explicit > inferred from tool params > skip
|
||||
let visibility = sb.paramVisibility
|
||||
if (!visibility) {
|
||||
// Infer from structural checks
|
||||
if (STRUCTURAL_SUBBLOCK_IDS.has(sb.id)) {
|
||||
visibility = 'hidden'
|
||||
} else if (AUTH_SUBBLOCK_TYPES.has(sb.type)) {
|
||||
visibility = 'hidden'
|
||||
} else if (
|
||||
sb.password &&
|
||||
(sb.id === 'botToken' || sb.id === 'accessToken' || sb.id === 'apiKey')
|
||||
) {
|
||||
// Auth tokens without explicit paramVisibility are hidden
|
||||
// (they're handled by the OAuth credential selector or structurally)
|
||||
// But only if they don't have a matching tool param
|
||||
if (!toolParamIds.has(sb.id)) {
|
||||
visibility = 'hidden'
|
||||
} else {
|
||||
visibility = toolParamVisibility[sb.id] || 'user-or-llm'
|
||||
}
|
||||
} else if (toolParamIds.has(effectiveParamId)) {
|
||||
// Fallback: infer from tool param visibility
|
||||
visibility = toolParamVisibility[effectiveParamId]
|
||||
} else if (toolParamIds.has(sb.id)) {
|
||||
visibility = toolParamVisibility[sb.id]
|
||||
} else {
|
||||
// SubBlock has no corresponding tool param — skip it
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by visibility: exclude hidden and llm-only
|
||||
if (visibility === 'hidden' || visibility === 'llm-only') continue
|
||||
|
||||
// Evaluate condition against current values
|
||||
if (sb.condition) {
|
||||
const conditionMet = evaluateSubBlockCondition(
|
||||
sb.condition as SubBlockCondition,
|
||||
valuesWithOperation
|
||||
)
|
||||
if (!conditionMet) continue
|
||||
}
|
||||
|
||||
// Handle canonical pairs: only include the active mode variant
|
||||
const canonicalId = canonicalIndex.canonicalIdBySubBlockId[sb.id]
|
||||
if (canonicalId) {
|
||||
const group = canonicalIndex.groupsById[canonicalId]
|
||||
if (group && isCanonicalPair(group)) {
|
||||
if (includedCanonicalIds.has(canonicalId)) continue
|
||||
includedCanonicalIds.add(canonicalId)
|
||||
|
||||
// Determine active mode
|
||||
const mode = resolveCanonicalMode(group, valuesWithOperation, canonicalModeOverrides)
|
||||
if (mode === 'advanced') {
|
||||
// Find the advanced variant
|
||||
const advancedSb = allSubBlocks.find((s) => group.advancedIds.includes(s.id))
|
||||
if (advancedSb) {
|
||||
filtered.push(advancedSb)
|
||||
}
|
||||
} else {
|
||||
// Include basic variant (current sb if it's the basic one)
|
||||
if (group.basicId === sb.id) {
|
||||
filtered.push(sb)
|
||||
} else {
|
||||
const basicSb = allSubBlocks.find((s) => s.id === group.basicId)
|
||||
if (basicSb) {
|
||||
filtered.push(basicSb)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Non-canonical, non-hidden, condition-passing subblock
|
||||
filtered.push(sb)
|
||||
}
|
||||
|
||||
return {
|
||||
toolConfig,
|
||||
subBlocks: filtered,
|
||||
oauthConfig: toolConfig.oauth,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error getting subblocks for tool input:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user