mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
feat: Add mutual exclusion between structured output and tools in LLM
node
This commit is contained in:
@ -85,6 +85,7 @@ type Props = {
|
||||
required?: boolean
|
||||
onBlur?: () => void
|
||||
onFocus?: () => void
|
||||
disableToolBlocks?: boolean
|
||||
}
|
||||
|
||||
const Editor: FC<Props> = ({
|
||||
@ -129,6 +130,7 @@ const Editor: FC<Props> = ({
|
||||
required,
|
||||
onBlur,
|
||||
onFocus,
|
||||
disableToolBlocks,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
@ -312,6 +314,7 @@ const Editor: FC<Props> = ({
|
||||
editable={!readOnly}
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
isSupportSandbox={isSupportSandbox}
|
||||
disableToolBlocks={disableToolBlocks}
|
||||
toolMetadata={promptMetadata}
|
||||
onToolMetadataChange={onPromptMetadataChange}
|
||||
/>
|
||||
|
||||
@ -13,6 +13,9 @@ const i18nPrefix = 'nodes.llm.computerUse'
|
||||
|
||||
type Props = {
|
||||
readonly: boolean
|
||||
isDisabledByStructuredOutput?: boolean
|
||||
disabled?: boolean
|
||||
disabledTip?: string
|
||||
enabled: boolean
|
||||
onChange: (enabled: boolean) => void
|
||||
nodeId: string
|
||||
@ -22,6 +25,9 @@ type Props = {
|
||||
|
||||
const ComputerUseConfig: FC<Props> = ({
|
||||
readonly,
|
||||
isDisabledByStructuredOutput,
|
||||
disabled,
|
||||
disabledTip,
|
||||
enabled,
|
||||
onChange,
|
||||
nodeId,
|
||||
@ -29,6 +35,8 @@ const ComputerUseConfig: FC<Props> = ({
|
||||
promptTemplateKey,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const disabledByStructuredOutput = isDisabledByStructuredOutput ?? disabled ?? false
|
||||
const isDisabled = readonly || disabledByStructuredOutput
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -46,12 +54,17 @@ const ComputerUseConfig: FC<Props> = ({
|
||||
noXSpacing
|
||||
operations={(
|
||||
<div>
|
||||
<Switch
|
||||
size="md"
|
||||
disabled={readonly}
|
||||
defaultValue={enabled}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Tooltip
|
||||
disabled={!disabledTip}
|
||||
popupContent={disabledTip}
|
||||
>
|
||||
<Switch
|
||||
size="md"
|
||||
disabled={isDisabled}
|
||||
defaultValue={enabled}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
@ -63,7 +76,8 @@ const ComputerUseConfig: FC<Props> = ({
|
||||
</div>
|
||||
<ReferenceToolConfig
|
||||
readonly={readonly}
|
||||
enabled={enabled}
|
||||
isDisabledByStructuredOutput={disabledByStructuredOutput}
|
||||
isComputerUseEnabled={enabled}
|
||||
nodeId={nodeId}
|
||||
toolSettings={toolSettings}
|
||||
promptTemplateKey={promptTemplateKey}
|
||||
|
||||
@ -43,6 +43,7 @@ type Props = {
|
||||
modelConfig?: ModelConfig
|
||||
isSupportSandbox?: boolean
|
||||
onPromptEditorBlur?: () => void
|
||||
disableToolBlocks?: boolean
|
||||
}
|
||||
|
||||
const roleOptions = [
|
||||
@ -88,6 +89,7 @@ const ConfigPromptItem: FC<Props> = ({
|
||||
modelConfig,
|
||||
isSupportSandbox,
|
||||
onPromptEditorBlur,
|
||||
disableToolBlocks,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const workflowStore = useWorkflowStore()
|
||||
@ -158,6 +160,7 @@ const ConfigPromptItem: FC<Props> = ({
|
||||
handleAddVariable={handleAddVariable}
|
||||
isSupportFileVar
|
||||
isSupportSandbox={isSupportSandbox}
|
||||
disableToolBlocks={disableToolBlocks}
|
||||
onBlur={onPromptEditorBlur}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -66,6 +66,7 @@ type Props = {
|
||||
handleAddVariable: (payload: any) => void
|
||||
modelConfig: ModelConfig
|
||||
onPromptEditorBlur?: () => void
|
||||
disableToolBlocks?: boolean
|
||||
}
|
||||
|
||||
const ConfigPrompt: FC<Props> = ({
|
||||
@ -82,6 +83,7 @@ const ConfigPrompt: FC<Props> = ({
|
||||
handleAddVariable,
|
||||
modelConfig,
|
||||
onPromptEditorBlur,
|
||||
disableToolBlocks,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const workflowStore = useWorkflowStore()
|
||||
@ -361,6 +363,7 @@ const ConfigPrompt: FC<Props> = ({
|
||||
modelConfig={modelConfig}
|
||||
isSupportSandbox={isSupportSandbox}
|
||||
onPromptEditorBlur={onPromptEditorBlur}
|
||||
disableToolBlocks={disableToolBlocks}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@ -438,6 +441,7 @@ const ConfigPrompt: FC<Props> = ({
|
||||
modelConfig={modelConfig}
|
||||
isSupportSandbox={isSupportSandbox}
|
||||
onBlur={onPromptEditorBlur}
|
||||
disableToolBlocks={disableToolBlocks}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,40 +1,36 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { LLMNodeType, ToolSetting } from '../types'
|
||||
import type { ToolDependency } from '@/app/components/workflow/nodes/llm/use-node-skills'
|
||||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import type { Locale } from '@/i18n-config/language'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other'
|
||||
import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import { useNodeCurdKit } from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
import { useNodeSkills } from '@/app/components/workflow/nodes/llm/use-node-skills'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { getLanguage } from '@/i18n-config/language'
|
||||
import { consoleClient, consoleQuery } from '@/service/client'
|
||||
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools } from '@/service/use-tools'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { getIconFromMarketPlace } from '@/utils/get-icon'
|
||||
|
||||
type ReferenceToolConfigProps = {
|
||||
readonly: boolean
|
||||
enabled: boolean
|
||||
isDisabledByStructuredOutput?: boolean
|
||||
isComputerUseEnabled?: boolean
|
||||
disabledByStructuredOutput?: boolean
|
||||
computerUseEnabled?: boolean
|
||||
nodeId: string
|
||||
toolSettings?: ToolSetting[]
|
||||
promptTemplateKey: string
|
||||
}
|
||||
|
||||
type ToolDependency = {
|
||||
type: string
|
||||
provider: string
|
||||
tool_name: string
|
||||
}
|
||||
|
||||
type ToolProviderGroup = {
|
||||
id: string
|
||||
actions: ToolDependency[]
|
||||
@ -42,14 +38,18 @@ type ToolProviderGroup = {
|
||||
|
||||
const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
readonly,
|
||||
enabled,
|
||||
isDisabledByStructuredOutput,
|
||||
isComputerUseEnabled,
|
||||
disabledByStructuredOutput,
|
||||
computerUseEnabled,
|
||||
nodeId,
|
||||
toolSettings,
|
||||
promptTemplateKey,
|
||||
}) => {
|
||||
const isDisabled = readonly || !enabled
|
||||
const resolvedIsComputerUseEnabled = isComputerUseEnabled ?? computerUseEnabled ?? false
|
||||
const resolvedIsDisabledByStructuredOutput = isDisabledByStructuredOutput ?? disabledByStructuredOutput ?? false
|
||||
const isReferenceToolsDisabled = readonly || !resolvedIsComputerUseEnabled || resolvedIsDisabledByStructuredOutput
|
||||
const { i18n, t } = useTranslation()
|
||||
const appId = useAppStore(s => s.appDetail?.id)
|
||||
const { handleNodeDataUpdate } = useNodeCurdKit<LLMNodeType>(nodeId)
|
||||
const { theme } = useTheme()
|
||||
const { data: buildInTools } = useAllBuiltInTools()
|
||||
@ -58,34 +58,11 @@ const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
const { data: mcpTools } = useAllMCPTools()
|
||||
const locale = useMemo(() => getLanguage(i18n.language as Locale), [i18n.language])
|
||||
|
||||
const queryKey = useMemo(() => {
|
||||
return [
|
||||
...consoleQuery.workflowDraft.nodeSkills.queryKey({
|
||||
input: {
|
||||
params: {
|
||||
appId: appId ?? '',
|
||||
nodeId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
promptTemplateKey,
|
||||
]
|
||||
}, [appId, nodeId, promptTemplateKey])
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey,
|
||||
queryFn: () => consoleClient.workflowDraft.nodeSkills({
|
||||
params: {
|
||||
appId: appId ?? '',
|
||||
nodeId,
|
||||
},
|
||||
}),
|
||||
enabled: !!appId && !!nodeId,
|
||||
placeholderData: previous => previous,
|
||||
const { toolDependencies, isLoading, isQueryEnabled, hasData } = useNodeSkills({
|
||||
nodeId,
|
||||
promptTemplateKey,
|
||||
})
|
||||
|
||||
const toolDependencies = useMemo<ToolDependency[]>(() => data?.tool_dependencies ?? [], [data?.tool_dependencies])
|
||||
|
||||
const providers = useMemo<ToolProviderGroup[]>(() => {
|
||||
const map = new Map<string, ToolDependency[]>()
|
||||
toolDependencies.forEach((tool) => {
|
||||
@ -214,8 +191,7 @@ const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
}))
|
||||
}, [])
|
||||
|
||||
const isQueryEnabled = !!appId && !!nodeId
|
||||
const isInitialLoading = isQueryEnabled && isLoading && !data
|
||||
const isInitialLoading = isQueryEnabled && isLoading && !hasData
|
||||
const showNoData = !isInitialLoading && providers.length === 0
|
||||
|
||||
const renderProviderIcon = useCallback((providerId: string) => {
|
||||
@ -244,7 +220,7 @@ const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
}, [iconErrorMap, providerIcons])
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-2', isDisabled && 'opacity-50')}>
|
||||
<div className={cn('flex flex-col gap-2', isReferenceToolsDisabled && 'opacity-50')}>
|
||||
{isInitialLoading && [0, 1].map(index => (
|
||||
<div
|
||||
key={`loading-provider-${index}`}
|
||||
@ -296,7 +272,7 @@ const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
key={`${action.type}-${action.provider}-${action.tool_name}`}
|
||||
className={cn(
|
||||
'relative flex h-7 items-center justify-between rounded-md pl-9 pr-2',
|
||||
!isDisabled && 'hover:bg-state-base-hover',
|
||||
!isReferenceToolsDisabled && 'hover:bg-state-base-hover',
|
||||
)}
|
||||
>
|
||||
<div className="absolute left-[15px] top-0 h-full w-[2px] bg-divider-subtle" />
|
||||
@ -307,7 +283,7 @@ const ReferenceToolConfig: FC<ReferenceToolConfigProps> = ({
|
||||
</div>
|
||||
<Switch
|
||||
size="md"
|
||||
disabled={isDisabled}
|
||||
disabled={isReferenceToolsDisabled}
|
||||
defaultValue={resolveToolEnabled(action)}
|
||||
onChange={value => handleToolEnabledChange(action, value)}
|
||||
/>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
import { BoxGroup } from '@/app/components/workflow/nodes/_base/components/layout'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import MaxIterations from './max-iterations'
|
||||
import { useNodeTools } from './use-node-tools'
|
||||
|
||||
@ -11,14 +13,19 @@ type ToolsProps = {
|
||||
tools?: ToolValue[]
|
||||
maxIterations?: number
|
||||
hideMaxIterations?: boolean
|
||||
disabled?: boolean
|
||||
disabledTip?: string
|
||||
}
|
||||
const Tools = ({
|
||||
nodeId,
|
||||
tools = [],
|
||||
maxIterations = 10,
|
||||
hideMaxIterations = false,
|
||||
disabled,
|
||||
disabledTip,
|
||||
}: ToolsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const isDisabled = !!disabled
|
||||
const {
|
||||
handleToolsChange,
|
||||
handleMaxIterationsChange,
|
||||
@ -34,22 +41,32 @@ const Tools = ({
|
||||
className: 'px-0',
|
||||
}}
|
||||
>
|
||||
<MultipleToolSelector
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={[]}
|
||||
availableNodes={[]}
|
||||
value={tools}
|
||||
label={t(`nodes.llm.tools.title`, { ns: 'workflow' })}
|
||||
tooltip={t(`nodes.llm.tools.title`, { ns: 'workflow' })}
|
||||
onChange={handleToolsChange}
|
||||
supportCollapse
|
||||
/>
|
||||
{!hideMaxIterations && (
|
||||
<MaxIterations
|
||||
value={maxIterations}
|
||||
onChange={handleMaxIterationsChange}
|
||||
/>
|
||||
)}
|
||||
<Tooltip
|
||||
disabled={!disabledTip}
|
||||
popupContent={disabledTip}
|
||||
>
|
||||
<div className={cn(isDisabled && 'opacity-50')}>
|
||||
<div className={cn(isDisabled && 'pointer-events-none')}>
|
||||
<MultipleToolSelector
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={[]}
|
||||
availableNodes={[]}
|
||||
value={tools}
|
||||
label={t('nodes.llm.tools.title', { ns: 'workflow' })}
|
||||
tooltip={t('nodes.llm.tools.title', { ns: 'workflow' })}
|
||||
onChange={handleToolsChange}
|
||||
supportCollapse
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
{!hideMaxIterations && (
|
||||
<MaxIterations
|
||||
value={maxIterations}
|
||||
onChange={handleMaxIterationsChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</BoxGroup>
|
||||
)
|
||||
}
|
||||
|
||||
@ -29,6 +29,8 @@ import Tools from './components/tools'
|
||||
import MaxIterations from './components/tools/max-iterations'
|
||||
import { useNodeTools } from './components/tools/use-node-tools'
|
||||
import useConfig from './use-config'
|
||||
import { useNodeSkills } from './use-node-skills'
|
||||
import { useStructuredOutputMutualExclusion } from './use-structured-output-mutual-exclusion'
|
||||
|
||||
const i18nPrefix = 'nodes.llm'
|
||||
|
||||
@ -91,6 +93,27 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
scheduleSkillsRefresh(promptTemplateKey)
|
||||
}, [promptTemplateKey, scheduleSkillsRefresh])
|
||||
|
||||
const { toolDependencies } = useNodeSkills({
|
||||
nodeId: id,
|
||||
promptTemplateKey: skillsRefreshKey,
|
||||
enabled: isSupportSandbox,
|
||||
})
|
||||
|
||||
const {
|
||||
isStructuredOutputBlocked,
|
||||
isComputerUseBlocked,
|
||||
isToolsBlocked,
|
||||
disableToolBlocks,
|
||||
structuredOutputDisabledTip,
|
||||
computerUseDisabledTip,
|
||||
toolsDisabledTip,
|
||||
} = useStructuredOutputMutualExclusion({
|
||||
inputs,
|
||||
readOnly,
|
||||
isSupportSandbox,
|
||||
toolDependencies,
|
||||
})
|
||||
|
||||
const {
|
||||
handleMaxIterationsChange,
|
||||
} = useNodeTools(id)
|
||||
@ -162,6 +185,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
handleAddVariable={handleAddVariable}
|
||||
modelConfig={model}
|
||||
onPromptEditorBlur={handlePromptEditorBlur}
|
||||
disableToolBlocks={disableToolBlocks}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -248,6 +272,8 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
<>
|
||||
<ComputerUseConfig
|
||||
readonly={readOnly}
|
||||
isDisabledByStructuredOutput={isComputerUseBlocked}
|
||||
disabledTip={computerUseDisabledTip}
|
||||
enabled={!!inputs.computer_use}
|
||||
onChange={handleComputerUseChange}
|
||||
nodeId={id}
|
||||
@ -262,6 +288,8 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
tools={inputs.tools}
|
||||
maxIterations={inputs.max_iterations}
|
||||
hideMaxIterations
|
||||
disabled={isToolsBlocked}
|
||||
disabledTip={toolsDisabledTip}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -364,13 +392,19 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
<RiQuestionLine className="size-3.5 text-text-quaternary" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Switch
|
||||
className="ml-2"
|
||||
defaultValue={!!inputs.structured_output_enabled}
|
||||
onChange={handleStructureOutputEnableChange}
|
||||
size="md"
|
||||
disabled={readOnly}
|
||||
/>
|
||||
<Tooltip
|
||||
disabled={!structuredOutputDisabledTip}
|
||||
popupContent={structuredOutputDisabledTip}
|
||||
>
|
||||
<div className="ml-2">
|
||||
<Switch
|
||||
defaultValue={!!inputs.structured_output_enabled}
|
||||
onChange={handleStructureOutputEnableChange}
|
||||
size="md"
|
||||
disabled={isStructuredOutputBlocked}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
|
||||
63
web/app/components/workflow/nodes/llm/use-node-skills.ts
Normal file
63
web/app/components/workflow/nodes/llm/use-node-skills.ts
Normal file
@ -0,0 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useMemo } from 'react'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { consoleClient, consoleQuery } from '@/service/client'
|
||||
|
||||
export type ToolDependency = {
|
||||
type: string
|
||||
provider: string
|
||||
tool_name: string
|
||||
}
|
||||
|
||||
type UseNodeSkillsParams = {
|
||||
nodeId: string
|
||||
promptTemplateKey: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export function useNodeSkills({ nodeId, promptTemplateKey, enabled = true }: UseNodeSkillsParams) {
|
||||
const appId = useAppStore(s => s.appDetail?.id)
|
||||
const isQueryEnabled = enabled && !!appId && !!nodeId
|
||||
|
||||
const queryKey = useMemo(() => {
|
||||
return [
|
||||
...consoleQuery.workflowDraft.nodeSkills.queryKey({
|
||||
input: {
|
||||
params: {
|
||||
appId: appId ?? '',
|
||||
nodeId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
promptTemplateKey,
|
||||
]
|
||||
}, [appId, nodeId, promptTemplateKey])
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey,
|
||||
queryFn: () => consoleClient.workflowDraft.nodeSkills({
|
||||
params: {
|
||||
appId: appId ?? '',
|
||||
nodeId,
|
||||
},
|
||||
}),
|
||||
enabled: isQueryEnabled,
|
||||
placeholderData: previous => previous,
|
||||
})
|
||||
|
||||
const toolDependencies = useMemo<ToolDependency[]>(
|
||||
() => data?.tool_dependencies ?? [],
|
||||
[data?.tool_dependencies],
|
||||
)
|
||||
|
||||
const hasData = !!data
|
||||
|
||||
return {
|
||||
toolDependencies,
|
||||
isLoading,
|
||||
isQueryEnabled,
|
||||
hasData,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import type { LLMNodeType } from './types'
|
||||
import type { ToolDependency } from './use-node-skills'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Params = {
|
||||
inputs: LLMNodeType
|
||||
readOnly: boolean
|
||||
isSupportSandbox: boolean
|
||||
toolDependencies: ToolDependency[]
|
||||
}
|
||||
|
||||
export const useStructuredOutputMutualExclusion = ({
|
||||
inputs,
|
||||
readOnly,
|
||||
isSupportSandbox,
|
||||
toolDependencies,
|
||||
}: Params) => {
|
||||
const { t } = useTranslation()
|
||||
const isStructuredOutputEnabled = !!inputs.structured_output_enabled
|
||||
const hasToolDependencies = isSupportSandbox && toolDependencies.length > 0
|
||||
const hasEnabledTools = (inputs.tools?.length ?? 0) > 0
|
||||
const hasToolConflict = !!inputs.computer_use || hasToolDependencies || hasEnabledTools
|
||||
|
||||
const isStructuredOutputBlocked = readOnly || (hasToolConflict && !isStructuredOutputEnabled)
|
||||
const isComputerUseBlocked = readOnly || (isStructuredOutputEnabled && !inputs.computer_use)
|
||||
const isToolsBlocked = readOnly || isStructuredOutputEnabled
|
||||
const disableToolBlocks = isStructuredOutputEnabled
|
||||
|
||||
const structuredOutputDisabledTip = useMemo(() => {
|
||||
if (readOnly || !isStructuredOutputBlocked)
|
||||
return ''
|
||||
return inputs.computer_use
|
||||
? t('structOutput.disabledByComputerUse', { ns: 'app' })
|
||||
: t('structOutput.disabledByTools', { ns: 'app' })
|
||||
}, [inputs.computer_use, isStructuredOutputBlocked, readOnly, t])
|
||||
|
||||
const computerUseDisabledTip = useMemo(() => {
|
||||
if (readOnly || !isComputerUseBlocked)
|
||||
return ''
|
||||
return t('nodes.llm.computerUse.disabledByStructuredOutput', { ns: 'workflow' })
|
||||
}, [isComputerUseBlocked, readOnly, t])
|
||||
|
||||
const toolsDisabledTip = useMemo(() => {
|
||||
if (readOnly || !isToolsBlocked)
|
||||
return ''
|
||||
return t('nodes.llm.tools.disabledByStructuredOutput', { ns: 'workflow' })
|
||||
}, [isToolsBlocked, readOnly, t])
|
||||
|
||||
return {
|
||||
isStructuredOutputBlocked,
|
||||
isComputerUseBlocked,
|
||||
isToolsBlocked,
|
||||
disableToolBlocks,
|
||||
structuredOutputDisabledTip,
|
||||
computerUseDisabledTip,
|
||||
toolsDisabledTip,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user