mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
195 lines
7.6 KiB
TypeScript
195 lines
7.6 KiB
TypeScript
'use client'
|
|
import type { FC } from 'react'
|
|
import type { Item } from '@/app/components/base/select'
|
|
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
|
import type { Node, NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types'
|
|
import { RiCheckLine } from '@remixicon/react'
|
|
import { memo, useCallback, useMemo, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { SimpleSelect } from '@/app/components/base/select'
|
|
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
|
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
|
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
|
|
import Tab, { TabType } from '@/app/components/workflow/nodes/_base/components/workflow-panel/tab'
|
|
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
|
import { cn } from '@/utils/classnames'
|
|
|
|
type ConfigPanelProps = {
|
|
agentName: string
|
|
extractorNodeId: string
|
|
mentionConfig: MentionConfig
|
|
availableNodes: Node[]
|
|
availableVars: NodeOutPutVar[]
|
|
onMentionConfigChange: (config: MentionConfig) => void
|
|
}
|
|
|
|
const ConfigPanel: FC<ConfigPanelProps> = ({
|
|
agentName,
|
|
extractorNodeId,
|
|
mentionConfig,
|
|
availableNodes,
|
|
availableVars,
|
|
onMentionConfigChange,
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const [tabType, setTabType] = useState<TabType>(TabType.settings)
|
|
|
|
const resolvedExtractorId = mentionConfig.extractor_node_id || extractorNodeId
|
|
|
|
const selectedOutput = useMemo<ValueSelector>(() => {
|
|
if (!resolvedExtractorId || !mentionConfig.output_selector?.length)
|
|
return []
|
|
|
|
return [resolvedExtractorId, ...(mentionConfig.output_selector || [])]
|
|
}, [mentionConfig.output_selector, resolvedExtractorId])
|
|
|
|
const handleOutputVarChange = useCallback((value: ValueSelector | string) => {
|
|
const selector = Array.isArray(value) ? value : []
|
|
const nextExtractorId = selector[0] || resolvedExtractorId
|
|
const nextOutputSelector = selector.length > 1 ? selector.slice(1) : []
|
|
|
|
onMentionConfigChange({
|
|
...mentionConfig,
|
|
extractor_node_id: nextExtractorId,
|
|
output_selector: nextOutputSelector,
|
|
})
|
|
}, [mentionConfig, onMentionConfigChange, resolvedExtractorId])
|
|
|
|
const whenOutputNoneOptions = useMemo(() => ([
|
|
{
|
|
value: 'raise_error',
|
|
name: t('subGraphModal.whenOutputNone.error', { ns: 'workflow' }),
|
|
description: t('subGraphModal.whenOutputNone.errorDesc', { ns: 'workflow' }),
|
|
},
|
|
{
|
|
value: 'use_default',
|
|
name: t('subGraphModal.whenOutputNone.default', { ns: 'workflow' }),
|
|
description: t('subGraphModal.whenOutputNone.defaultDesc', { ns: 'workflow' }),
|
|
},
|
|
]), [t])
|
|
const selectedWhenOutputNoneOption = useMemo(() => (
|
|
whenOutputNoneOptions.find(item => item.value === mentionConfig.null_strategy) ?? whenOutputNoneOptions[0]
|
|
), [mentionConfig.null_strategy, whenOutputNoneOptions])
|
|
|
|
const handleNullStrategyChange = useCallback((item: Item) => {
|
|
if (typeof item.value !== 'string')
|
|
return
|
|
onMentionConfigChange({
|
|
...mentionConfig,
|
|
null_strategy: item.value as MentionConfig['null_strategy'],
|
|
})
|
|
}, [mentionConfig, onMentionConfigChange])
|
|
|
|
const handleDefaultValueChange = useCallback((value: string) => {
|
|
const trimmed = value.trim()
|
|
let nextValue: unknown = value
|
|
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
try {
|
|
nextValue = JSON.parse(trimmed)
|
|
}
|
|
catch {
|
|
nextValue = value
|
|
}
|
|
}
|
|
|
|
onMentionConfigChange({
|
|
...mentionConfig,
|
|
default_value: nextValue,
|
|
})
|
|
}, [mentionConfig, onMentionConfigChange])
|
|
const defaultValue = mentionConfig.default_value ?? ''
|
|
const shouldFormatDefaultValue = typeof defaultValue !== 'string'
|
|
|
|
return (
|
|
<div className="flex h-full flex-col">
|
|
<div className="px-4 pb-2 pt-4">
|
|
<div className="system-lg-semibold text-text-primary">
|
|
{t('subGraphModal.internalStructure', { ns: 'workflow' })}
|
|
</div>
|
|
<div className="system-sm-regular text-text-tertiary">
|
|
{t('subGraphModal.internalStructureDesc', { ns: 'workflow', name: agentName })}
|
|
</div>
|
|
</div>
|
|
<div className="px-4 pb-2">
|
|
<Tab value={tabType} onChange={setTabType} />
|
|
</div>
|
|
{tabType === TabType.lastRun && (
|
|
<div className="flex flex-1 items-center justify-center p-4">
|
|
<p className="system-sm-regular text-text-tertiary">
|
|
{t('subGraphModal.noRunHistory', { ns: 'workflow' })}
|
|
</p>
|
|
</div>
|
|
)}
|
|
{tabType === TabType.settings && (
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="space-y-4 px-4 py-4">
|
|
<Field title={t('subGraphModal.outputVariables', { ns: 'workflow' })}>
|
|
<VarReferencePicker
|
|
nodeId={extractorNodeId}
|
|
readonly={false}
|
|
isShowNodeName
|
|
value={selectedOutput}
|
|
onChange={handleOutputVarChange}
|
|
availableNodes={availableNodes}
|
|
availableVars={availableVars}
|
|
/>
|
|
</Field>
|
|
</div>
|
|
<div className="space-y-4 px-4 py-4">
|
|
<Field
|
|
title={t('subGraphModal.whenOutputIsNone', { ns: 'workflow' })}
|
|
operations={(
|
|
<div className="flex items-center">
|
|
<SimpleSelect
|
|
items={whenOutputNoneOptions}
|
|
defaultValue={mentionConfig.null_strategy}
|
|
allowSearch={false}
|
|
notClearable
|
|
wrapperClassName="min-w-[160px]"
|
|
onSelect={handleNullStrategyChange}
|
|
renderOption={({ item, selected }) => (
|
|
<div className="flex items-start gap-2">
|
|
<div className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
|
|
{selected && (
|
|
<RiCheckLine className="h-4 w-4 text-[14px] text-text-accent" />
|
|
)}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<div className="system-sm-medium text-text-secondary">{item.name}</div>
|
|
<div className="system-xs-regular mt-0.5 text-text-tertiary">{item.description}</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
/>
|
|
</div>
|
|
)}
|
|
>
|
|
<div className="space-y-2">
|
|
{selectedWhenOutputNoneOption?.description && (
|
|
<div className="system-xs-regular text-text-tertiary">
|
|
{selectedWhenOutputNoneOption.description}
|
|
</div>
|
|
)}
|
|
{mentionConfig.null_strategy === 'use_default' && (
|
|
<div className={cn('overflow-hidden rounded-lg border border-components-input-border-active bg-components-input-bg-normal p-1')}>
|
|
<CodeEditor
|
|
noWrapper
|
|
language={CodeLanguage.json}
|
|
value={defaultValue}
|
|
onChange={handleDefaultValueChange}
|
|
isJSONStringifyBeauty={shouldFormatDefaultValue}
|
|
className="min-h-[160px]"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Field>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(ConfigPanel)
|