Feat: conversation variable & variable assigner node (#7222)
Signed-off-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: -LAN- <laipz8200@outlook.com>
This commit is contained in:
@@ -64,6 +64,7 @@ const AddVariablePopupWithPosition = ({
|
||||
} as any,
|
||||
],
|
||||
hideEnv: true,
|
||||
hideChatVar: true,
|
||||
isChatMode,
|
||||
filterVar: filterVar(outputType as VarType),
|
||||
})
|
||||
|
||||
@@ -18,6 +18,8 @@ import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
payload: InputVar
|
||||
@@ -56,22 +58,24 @@ const FormItem: FC<Props> = ({
|
||||
}, [value, onChange])
|
||||
const nodeKey = (() => {
|
||||
if (typeof payload.label === 'object') {
|
||||
const { nodeType, nodeName, variable } = payload.label
|
||||
const { nodeType, nodeName, variable, isChatVar } = payload.label
|
||||
return (
|
||||
<div className='h-full flex items-center'>
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon type={nodeType || BlockEnum.Start} />
|
||||
{!isChatVar && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon type={nodeType || BlockEnum.Start} />
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700 max-w-[150px] truncate' title={nodeName}>
|
||||
{nodeName}
|
||||
</div>
|
||||
<Line3 className='mr-0.5'></Line3>
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700 max-w-[150px] truncate' title={nodeName}>
|
||||
{nodeName}
|
||||
</div>
|
||||
<Line3 className='mr-0.5'></Line3>
|
||||
</div>
|
||||
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
<Variable02 className='w-3.5 h-3.5' />
|
||||
<div className='ml-0.5 text-xs font-medium max-w-[150px] truncate' title={variable} >
|
||||
{!isChatVar && <Variable02 className='w-3.5 h-3.5' />}
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div className={cn('ml-0.5 text-xs font-medium max-w-[150px] truncate', isChatVar && 'text-text-secondary')} title={variable} >
|
||||
{variable}
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,7 +90,12 @@ const FormItem: FC<Props> = ({
|
||||
const isIterator = type === InputVarType.iterator
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
{!isArrayLikeType && <div className='h-8 leading-8 text-[13px] font-medium text-gray-700 truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>}
|
||||
{!isArrayLikeType && (
|
||||
<div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
|
||||
<div className='truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>
|
||||
{!payload.required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
|
||||
</div>
|
||||
)}
|
||||
<div className='grow'>
|
||||
{
|
||||
type === InputVarType.textInput && (
|
||||
|
||||
@@ -15,7 +15,7 @@ const CODE_EDITOR_LINE_HEIGHT = 18
|
||||
|
||||
export type Props = {
|
||||
value?: string | object
|
||||
placeholder?: string
|
||||
placeholder?: JSX.Element | string
|
||||
onChange?: (value: string) => void
|
||||
title?: JSX.Element
|
||||
language: CodeLanguage
|
||||
@@ -167,7 +167,7 @@ const CodeEditor: FC<Props> = ({
|
||||
}}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
{!outPutValue && <div className='pointer-events-none absolute left-[36px] top-0 leading-[18px] text-[13px] font-normal text-gray-300'>{placeholder}</div>}
|
||||
{!outPutValue && !isFocus && <div className='pointer-events-none absolute left-[36px] top-0 leading-[18px] text-[13px] font-normal text-gray-300'>{placeholder}</div>}
|
||||
</>
|
||||
)
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ type Props = {
|
||||
justVar?: boolean
|
||||
nodesOutputVars?: NodeOutPutVar[]
|
||||
availableNodes?: Node[]
|
||||
insertVarTipToLeft?: boolean
|
||||
}
|
||||
|
||||
const Editor: FC<Props> = ({
|
||||
@@ -40,6 +41,7 @@ const Editor: FC<Props> = ({
|
||||
readOnly,
|
||||
nodesOutputVars,
|
||||
availableNodes = [],
|
||||
insertVarTipToLeft,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -106,12 +108,12 @@ const Editor: FC<Props> = ({
|
||||
{/* to patch Editor not support dynamic change editable status */}
|
||||
{readOnly && <div className='absolute inset-0 z-10'></div>}
|
||||
{isFocus && (
|
||||
<div className='absolute z-10 top-[-9px] right-1'>
|
||||
<div className={cn('absolute z-10', insertVarTipToLeft ? 'top-1.5 left-[-12px]' : ' top-[-9px] right-1')}>
|
||||
<TooltipPlus
|
||||
popupContent={`${t('workflow.common.insertVarTip')}`}
|
||||
>
|
||||
<div className='p-0.5 rounded-[5px] shadow-lg cursor-pointer bg-white hover:bg-gray-100 border-[0.5px] border-black/5'>
|
||||
<Variable02 className='w-3.5 h-3.5 text-gray-500' />
|
||||
<Variable02 className='w-3.5 h-3.5 text-components-button-secondary-accent-text' />
|
||||
</div>
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ const OptionCard: FC<Props> = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center px-2 h-8 rounded-md system-sm-regular bg-components-option-card-option-bg border border-components-option-card-option-bg text-text-secondary cursor-default',
|
||||
'flex items-center px-2 h-8 rounded-md system-sm-regular bg-components-option-card-option-bg border border-components-option-card-option-border text-text-secondary cursor-default',
|
||||
(!selected && !disabled) && 'hover:bg-components-option-card-option-bg-hover hover:border-components-option-card-option-border-hover hover:shadow-xs cursor-pointer',
|
||||
selected && 'bg-components-option-card-option-selected-bg border-[1.5px] border-components-option-card-option-selected-border system-sm-medium shadow-xs',
|
||||
disabled && 'text-text-disabled',
|
||||
|
||||
@@ -5,10 +5,10 @@ import cn from 'classnames'
|
||||
import { useWorkflow } from '../../../hooks'
|
||||
import { BlockEnum } from '../../../types'
|
||||
import { VarBlockIcon } from '../../../block-icon'
|
||||
import { getNodeInfoById, isENV, isSystemVar } from './variable/utils'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isSystemVar } from './variable/utils'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
type Props = {
|
||||
nodeId: string
|
||||
value: string
|
||||
@@ -42,13 +42,14 @@ const ReadonlyInputWithSelectVar: FC<Props> = ({
|
||||
const value = vars[index].split('.')
|
||||
const isSystem = isSystemVar(value)
|
||||
const isEnv = isENV(value)
|
||||
const isChatVar = isConversationVar(value)
|
||||
const node = (isSystem ? startNode : getNodeInfoById(availableNodes, value[0]))?.data
|
||||
const varName = `${isSystem ? 'sys.' : ''}${value[value.length - 1]}`
|
||||
|
||||
return (<span key={index}>
|
||||
<span className='relative top-[-3px] leading-[16px]'>{str}</span>
|
||||
<div className=' inline-flex h-[16px] items-center px-1.5 rounded-[5px] bg-white'>
|
||||
{!isEnv && (
|
||||
{!isEnv && !isChatVar && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
@@ -61,9 +62,10 @@ const ReadonlyInputWithSelectVar: FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5' />}
|
||||
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5' />}
|
||||
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', isEnv && 'text-gray-900')} title={varName}>{varName}</div>
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && 'text-gray-900')} title={varName}>{varName}</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>)
|
||||
|
||||
@@ -10,6 +10,7 @@ type Item = {
|
||||
label: string
|
||||
}
|
||||
type Props = {
|
||||
className?: string
|
||||
trigger?: JSX.Element
|
||||
DropDownIcon?: any
|
||||
noLeft?: boolean
|
||||
@@ -27,6 +28,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const TypeSelector: FC<Props> = ({
|
||||
className,
|
||||
trigger,
|
||||
DropDownIcon = ChevronSelectorVertical,
|
||||
noLeft,
|
||||
@@ -50,11 +52,12 @@ const TypeSelector: FC<Props> = ({
|
||||
setHide()
|
||||
}, ref)
|
||||
return (
|
||||
<div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative')} ref={ref}>
|
||||
<div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative select-none', className)} ref={ref}>
|
||||
{trigger
|
||||
? (
|
||||
<div
|
||||
onClick={toggleShow}
|
||||
className={cn(!readonly && 'cursor-pointer')}
|
||||
>
|
||||
{trigger}
|
||||
</div>
|
||||
@@ -63,13 +66,13 @@ const TypeSelector: FC<Props> = ({
|
||||
<div
|
||||
onClick={toggleShow}
|
||||
className={cn(showOption && 'bg-black/5', 'flex items-center h-5 pl-1 pr-0.5 rounded-md text-xs font-semibold text-gray-700 cursor-pointer hover:bg-black/5')}>
|
||||
<div className={cn(triggerClassName, 'text-xs font-semibold', uppercase && 'uppercase', noValue && 'text-gray-400')}>{!noValue ? item?.label : placeholder}</div>
|
||||
<div className={cn('text-sm font-semibold', uppercase && 'uppercase', noValue && 'text-gray-400', triggerClassName)}>{!noValue ? item?.label : placeholder}</div>
|
||||
{!readonly && <DropDownIcon className='w-3 h-3 ' />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(showOption && !readonly) && (
|
||||
<div className={cn(popupClassName, 'absolute z-10 top-[24px] w-[120px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white')}>
|
||||
<div className={cn('absolute z-10 top-[24px] w-[120px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white select-none', popupClassName)}>
|
||||
{list.map(item => (
|
||||
<div
|
||||
key={item.value}
|
||||
|
||||
@@ -10,8 +10,8 @@ import type {
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type VariableTagProps = {
|
||||
@@ -30,12 +30,13 @@ const VariableTag = ({
|
||||
return nodes.find(node => node.id === valueSelector[0])
|
||||
}, [nodes, valueSelector])
|
||||
const isEnv = isENV(valueSelector)
|
||||
const isChatVar = isConversationVar(valueSelector)
|
||||
|
||||
const variableName = isSystemVar(valueSelector) ? valueSelector.slice(0).join('.') : valueSelector.slice(1).join('.')
|
||||
|
||||
return (
|
||||
<div className='inline-flex items-center px-1.5 max-w-full h-6 text-xs rounded-md border-[0.5px] border-[rgba(16, 2440,0.08)] bg-white shadow-xs'>
|
||||
{!isEnv && (
|
||||
{!isEnv && !isChatVar && (
|
||||
<>
|
||||
{node && (
|
||||
<VarBlockIcon
|
||||
@@ -54,8 +55,9 @@ const VariableTag = ({
|
||||
</>
|
||||
)}
|
||||
{isEnv && <Env className='shrink-0 mr-0.5 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div
|
||||
className={cn('truncate text-text-accent font-medium', isEnv && 'text-text-secondary')}
|
||||
className={cn('truncate text-text-accent font-medium', (isEnv || isChatVar) && 'text-text-secondary')}
|
||||
title={variableName}
|
||||
>
|
||||
{variableName}
|
||||
|
||||
@@ -9,14 +9,14 @@ import type { Var } from '@/app/components/workflow/types'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
|
||||
type Props = {
|
||||
schema: CredentialFormSchema
|
||||
schema: Partial<CredentialFormSchema>
|
||||
readonly: boolean
|
||||
value: string
|
||||
onChange: (value: string | number, varKindType: VarKindType, varInfo?: Var) => void
|
||||
}
|
||||
|
||||
const ConstantField: FC<Props> = ({
|
||||
schema,
|
||||
schema = {} as CredentialFormSchema,
|
||||
readonly,
|
||||
value,
|
||||
onChange,
|
||||
@@ -47,7 +47,7 @@ const ConstantField: FC<Props> = ({
|
||||
{schema.type === FormTypeEnum.textNumber && (
|
||||
<input
|
||||
type='number'
|
||||
className='w-full h-8 leading-8 pl-0.5 bg-transparent text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
|
||||
className='w-full h-8 leading-8 p-2 rounded-lg bg-gray-100 text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
|
||||
value={value}
|
||||
onChange={handleStaticChange}
|
||||
readOnly={readonly}
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { ParameterExtractorNodeType } from '../../../parameter-extractor/ty
|
||||
import type { IterationNodeType } from '../../../iteration/types'
|
||||
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
|
||||
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
|
||||
import type { EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { ConversationVariable, EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types'
|
||||
import {
|
||||
HTTP_REQUEST_OUTPUT_STRUCT,
|
||||
@@ -38,6 +38,10 @@ export const isENV = (valueSelector: ValueSelector) => {
|
||||
return valueSelector[0] === 'env'
|
||||
}
|
||||
|
||||
export const isConversationVar = (valueSelector: ValueSelector) => {
|
||||
return valueSelector[0] === 'conversation'
|
||||
}
|
||||
|
||||
const inputVarTypeToVarType = (type: InputVarType): VarType => {
|
||||
if (type === InputVarType.number)
|
||||
return VarType.number
|
||||
@@ -246,13 +250,32 @@ const formatItem = (
|
||||
}) as Var[]
|
||||
break
|
||||
}
|
||||
|
||||
case 'conversation': {
|
||||
res.vars = data.chatVarList.map((chatVar: ConversationVariable) => {
|
||||
return {
|
||||
variable: `conversation.${chatVar.name}`,
|
||||
type: chatVar.value_type,
|
||||
des: chatVar.description,
|
||||
}
|
||||
}) as Var[]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const selector = [id]
|
||||
res.vars = res.vars.filter((v) => {
|
||||
const { children } = v
|
||||
if (!children)
|
||||
return filterVar(v, selector)
|
||||
if (!children) {
|
||||
return filterVar(v, (() => {
|
||||
const variableArr = v.variable.split('.')
|
||||
const [first, ..._other] = variableArr
|
||||
if (first === 'sys' || first === 'env' || first === 'conversation')
|
||||
return variableArr
|
||||
|
||||
return [...selector, ...variableArr]
|
||||
})())
|
||||
}
|
||||
|
||||
const obj = findExceptVarInObject(v, filterVar, selector)
|
||||
return obj?.children && obj?.children.length > 0
|
||||
@@ -271,6 +294,7 @@ export const toNodeOutputVars = (
|
||||
isChatMode: boolean,
|
||||
filterVar = (_payload: Var, _selector: ValueSelector) => true,
|
||||
environmentVariables: EnvironmentVariable[] = [],
|
||||
conversationVariables: ConversationVariable[] = [],
|
||||
): NodeOutPutVar[] => {
|
||||
// ENV_NODE data format
|
||||
const ENV_NODE = {
|
||||
@@ -281,9 +305,19 @@ export const toNodeOutputVars = (
|
||||
envList: environmentVariables,
|
||||
},
|
||||
}
|
||||
// CHAT_VAR_NODE data format
|
||||
const CHAT_VAR_NODE = {
|
||||
id: 'conversation',
|
||||
data: {
|
||||
title: 'CONVERSATION',
|
||||
type: 'conversation',
|
||||
chatVarList: conversationVariables,
|
||||
},
|
||||
}
|
||||
const res = [
|
||||
...nodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node.data.type)),
|
||||
...(environmentVariables.length > 0 ? [ENV_NODE] : []),
|
||||
...((isChatMode && conversationVariables.length) > 0 ? [CHAT_VAR_NODE] : []),
|
||||
].map((node) => {
|
||||
return {
|
||||
...formatItem(node, isChatMode, filterVar),
|
||||
@@ -348,6 +382,7 @@ export const getVarType = ({
|
||||
isChatMode,
|
||||
isConstant,
|
||||
environmentVariables = [],
|
||||
conversationVariables = [],
|
||||
}:
|
||||
{
|
||||
valueSelector: ValueSelector
|
||||
@@ -357,6 +392,7 @@ export const getVarType = ({
|
||||
isChatMode: boolean
|
||||
isConstant?: boolean
|
||||
environmentVariables?: EnvironmentVariable[]
|
||||
conversationVariables?: ConversationVariable[]
|
||||
}): VarType => {
|
||||
if (isConstant)
|
||||
return VarType.string
|
||||
@@ -366,6 +402,7 @@ export const getVarType = ({
|
||||
isChatMode,
|
||||
undefined,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
)
|
||||
|
||||
const isIterationInnerVar = parentNode?.data.type === BlockEnum.Iteration
|
||||
@@ -388,6 +425,7 @@ export const getVarType = ({
|
||||
}
|
||||
const isSystem = isSystemVar(valueSelector)
|
||||
const isEnv = isENV(valueSelector)
|
||||
const isChatVar = isConversationVar(valueSelector)
|
||||
const startNode = availableNodes.find((node: any) => {
|
||||
return node.data.type === BlockEnum.Start
|
||||
})
|
||||
@@ -400,7 +438,7 @@ export const getVarType = ({
|
||||
|
||||
let type: VarType = VarType.string
|
||||
let curr: any = targetVar.vars
|
||||
if (isSystem || isEnv) {
|
||||
if (isSystem || isEnv || isChatVar) {
|
||||
return curr.find((v: any) => v.variable === (valueSelector as ValueSelector).join('.'))?.type
|
||||
}
|
||||
else {
|
||||
@@ -426,6 +464,7 @@ export const toNodeAvailableVars = ({
|
||||
beforeNodes,
|
||||
isChatMode,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
filterVar,
|
||||
}: {
|
||||
parentNode?: Node | null
|
||||
@@ -435,6 +474,8 @@ export const toNodeAvailableVars = ({
|
||||
isChatMode: boolean
|
||||
// env
|
||||
environmentVariables?: EnvironmentVariable[]
|
||||
// chat var
|
||||
conversationVariables?: ConversationVariable[]
|
||||
filterVar: (payload: Var, selector: ValueSelector) => boolean
|
||||
}): NodeOutPutVar[] => {
|
||||
const beforeNodesOutputVars = toNodeOutputVars(
|
||||
@@ -442,6 +483,7 @@ export const toNodeAvailableVars = ({
|
||||
isChatMode,
|
||||
filterVar,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
)
|
||||
const isInIteration = parentNode?.data.type === BlockEnum.Iteration
|
||||
if (isInIteration) {
|
||||
@@ -453,6 +495,7 @@ export const toNodeAvailableVars = ({
|
||||
availableNodes: beforeNodes,
|
||||
isChatMode,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
})
|
||||
const iterationVar = {
|
||||
nodeId: iterationNode?.id,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import produce from 'immer'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import VarReferencePopup from './var-reference-popup'
|
||||
import { getNodeInfoById, isENV, isSystemVar } from './utils'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isSystemVar } from './utils'
|
||||
import ConstantField from './constant-field'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
@@ -17,7 +17,7 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setti
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
|
||||
import AddButton from '@/app/components/base/button/add-button'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
const TRIGGER_DEFAULT_WIDTH = 227
|
||||
|
||||
type Props = {
|
||||
@@ -49,7 +50,8 @@ type Props = {
|
||||
availableNodes?: Node[]
|
||||
availableVars?: NodeOutPutVar[]
|
||||
isAddBtnTrigger?: boolean
|
||||
schema?: CredentialFormSchema
|
||||
schema?: Partial<CredentialFormSchema>
|
||||
valueTypePlaceHolder?: string
|
||||
}
|
||||
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
@@ -57,7 +59,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
readonly,
|
||||
className,
|
||||
isShowNodeName,
|
||||
value,
|
||||
value = [],
|
||||
onOpen = () => { },
|
||||
onChange,
|
||||
isSupportConstantValue,
|
||||
@@ -68,6 +70,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
availableVars,
|
||||
isAddBtnTrigger,
|
||||
schema,
|
||||
valueTypePlaceHolder,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const store = useStoreApi()
|
||||
@@ -99,7 +102,6 @@ const VarReferencePicker: FC<Props> = ({
|
||||
|
||||
const [varKindType, setVarKindType] = useState<VarKindType>(defaultVarKindType)
|
||||
const isConstant = isSupportConstantValue && varKindType === VarKindType.constant
|
||||
|
||||
const outputVars = useMemo(() => {
|
||||
if (availableVars)
|
||||
return availableVars
|
||||
@@ -215,6 +217,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
})
|
||||
|
||||
const isEnv = isENV(value as ValueSelector)
|
||||
const isChatVar = isConversationVar(value as ValueSelector)
|
||||
|
||||
// 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 17 buff
|
||||
const availableWidth = triggerWidth - 56
|
||||
@@ -227,6 +230,8 @@ const VarReferencePicker: FC<Props> = ({
|
||||
return [maxNodeNameWidth, maxVarNameWidth, maxTypeWidth]
|
||||
})()
|
||||
|
||||
const WrapElem = isSupportConstantValue ? 'div' : PortalToFollowElemTrigger
|
||||
const VarPickerWrap = !isSupportConstantValue ? 'div' : PortalToFollowElemTrigger
|
||||
return (
|
||||
<div className={cn(className, !readonly && 'cursor-pointer')}>
|
||||
<PortalToFollowElem
|
||||
@@ -234,7 +239,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
onOpenChange={setOpen}
|
||||
placement={isAddBtnTrigger ? 'bottom-end' : 'bottom-start'}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => {
|
||||
<WrapElem onClick={() => {
|
||||
if (readonly)
|
||||
return
|
||||
!isConstant ? setOpen(!open) : setControlFocus(Date.now())
|
||||
@@ -245,23 +250,28 @@ const VarReferencePicker: FC<Props> = ({
|
||||
<AddButton onClick={() => { }}></AddButton>
|
||||
</div>
|
||||
)
|
||||
: (<div ref={triggerRef} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8 p-1 rounded-lg bg-gray-100 border')}>
|
||||
: (<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8', !isSupportConstantValue && 'p-1 rounded-lg bg-gray-100 border')}>
|
||||
{isSupportConstantValue
|
||||
? <div onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setOpen(false)
|
||||
setControlFocus(Date.now())
|
||||
}} className='mr-1 flex items-center space-x-1'>
|
||||
}} className='h-full mr-1 flex items-center space-x-1'>
|
||||
<TypeSelector
|
||||
noLeft
|
||||
triggerClassName='!text-xs'
|
||||
trigger={
|
||||
<div className='flex items-center h-8 px-2 radius-md bg-components-input-bg-normal'>
|
||||
<div className='mr-1 system-sm-regular text-components-input-text-filled'>{varKindTypes.find(item => item.value === varKindType)?.label}</div>
|
||||
<RiArrowDownSLine className='w-4 h-4 text-text-quaternary' />
|
||||
</div>
|
||||
}
|
||||
popupClassName='top-8'
|
||||
readonly={readonly}
|
||||
DropDownIcon={RiArrowDownSLine}
|
||||
value={varKindType}
|
||||
options={varKindTypes}
|
||||
onChange={handleVarKindTypeChange}
|
||||
showChecked
|
||||
/>
|
||||
<div className='h-4 w-px bg-black/5'></div>
|
||||
</div>
|
||||
: (!hasValue && <div className='ml-1.5 mr-1'>
|
||||
<Variable02 className='w-3.5 h-3.5 text-gray-400' />
|
||||
@@ -276,38 +286,51 @@ const VarReferencePicker: FC<Props> = ({
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
|
||||
{hasValue
|
||||
? (
|
||||
<>
|
||||
{isShowNodeName && !isEnv && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
className='!text-gray-900'
|
||||
type={outputVarNode?.type || BlockEnum.Start}
|
||||
/>
|
||||
<VarPickerWrap
|
||||
onClick={() => {
|
||||
if (readonly)
|
||||
return
|
||||
!isConstant ? setOpen(!open) : setControlFocus(Date.now())
|
||||
}}
|
||||
className='grow h-full'
|
||||
>
|
||||
<div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center pl-1 py-1 rounded-lg bg-gray-100')}>
|
||||
<div className={cn('h-full items-center px-1.5 rounded-[5px]', hasValue ? 'bg-white inline-flex' : 'flex')}>
|
||||
{hasValue
|
||||
? (
|
||||
<>
|
||||
{isShowNodeName && !isEnv && !isChatVar && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
className='!text-gray-900'
|
||||
type={outputVarNode?.type || BlockEnum.Start}
|
||||
/>
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700 truncate' title={outputVarNode?.title} style={{
|
||||
maxWidth: maxNodeNameWidth,
|
||||
}}>{outputVarNode?.title}</div>
|
||||
<Line3 className='mr-0.5'></Line3>
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
{!hasValue && <Variable02 className='w-3.5 h-3.5' />}
|
||||
{isEnv && <Env className='w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div className={cn('ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && '!text-text-secondary')} title={varName} style={{
|
||||
maxWidth: maxVarNameWidth,
|
||||
}}>{varName}</div>
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700 truncate' title={outputVarNode?.title} style={{
|
||||
maxWidth: maxNodeNameWidth,
|
||||
}}>{outputVarNode?.title}</div>
|
||||
<Line3 className='mr-0.5'></Line3>
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
{!hasValue && <Variable02 className='w-3.5 h-3.5' />}
|
||||
{isEnv && <Env className='w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
<div className={cn('ml-0.5 text-xs font-medium truncate', isEnv && '!text-gray-900')} title={varName} style={{
|
||||
maxWidth: maxVarNameWidth,
|
||||
}}>{varName}</div>
|
||||
</div>
|
||||
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize truncate' title={type} style={{
|
||||
maxWidth: maxTypeWidth,
|
||||
}}>{type}</div>
|
||||
</>
|
||||
)
|
||||
: <div className='text-[13px] font-normal text-gray-400'>{t('workflow.common.setVarValuePlaceholder')}</div>}
|
||||
</div>
|
||||
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize truncate' title={type} style={{
|
||||
maxWidth: maxTypeWidth,
|
||||
}}>{type}</div>
|
||||
</>
|
||||
)
|
||||
: <div className='text-[13px] font-normal text-gray-400'>{t('workflow.common.setVarValuePlaceholder')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</VarPickerWrap>
|
||||
)}
|
||||
{(hasValue && !readonly) && (<div
|
||||
className='invisible group-hover/wrap:visible absolute h-5 right-1 top-[50%] translate-y-[-50%] group p-1 rounded-md hover:bg-black/5 cursor-pointer'
|
||||
@@ -315,8 +338,15 @@ const VarReferencePicker: FC<Props> = ({
|
||||
>
|
||||
<RiCloseLine className='w-3.5 h-3.5 text-gray-500 group-hover:text-gray-800' />
|
||||
</div>)}
|
||||
{!hasValue && valueTypePlaceHolder && (
|
||||
<Badge
|
||||
className=' absolute right-1 top-[50%] translate-y-[-50%] capitalize'
|
||||
text={valueTypePlaceHolder}
|
||||
uppercase={false}
|
||||
/>
|
||||
)}
|
||||
</div>)}
|
||||
</PortalToFollowElemTrigger>
|
||||
</WrapElem>
|
||||
<PortalToFollowElemContent style={{
|
||||
zIndex: 100,
|
||||
}}>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { checkKeys } from '@/utils/var'
|
||||
|
||||
type ObjectChildrenProps = {
|
||||
@@ -51,6 +51,7 @@ const Item: FC<ItemProps> = ({
|
||||
const isObj = itemData.type === VarType.object && itemData.children && itemData.children.length > 0
|
||||
const isSys = itemData.variable.startsWith('sys.')
|
||||
const isEnv = itemData.variable.startsWith('env.')
|
||||
const isChatVar = itemData.variable.startsWith('conversation.')
|
||||
const itemRef = useRef(null)
|
||||
const [isItemHovering, setIsItemHovering] = useState(false)
|
||||
const _ = useHover(itemRef, {
|
||||
@@ -79,7 +80,7 @@ const Item: FC<ItemProps> = ({
|
||||
}, [isHovering])
|
||||
const handleChosen = (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (isSys || isEnv) { // system variable or environment variable
|
||||
if (isSys || isEnv || isChatVar) { // system variable | environment variable | conversation variable
|
||||
onChange([...objPath, ...itemData.variable.split('.')], itemData)
|
||||
}
|
||||
else {
|
||||
@@ -100,13 +101,21 @@ const Item: FC<ItemProps> = ({
|
||||
isHovering && (isObj ? 'bg-primary-50' : 'bg-gray-50'),
|
||||
'relative w-full flex items-center h-6 pl-3 rounded-md cursor-pointer')
|
||||
}
|
||||
// style={{ width: itemWidth || 252 }}
|
||||
onClick={handleChosen}
|
||||
>
|
||||
<div className='flex items-center w-0 grow'>
|
||||
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{!isEnv ? itemData.variable : itemData.variable.replace('env.', '')}</div>
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
{!isEnv && !isChatVar && (
|
||||
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable}</div>
|
||||
)}
|
||||
{isEnv && (
|
||||
<div title={itemData.variable} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable.replace('env.', '')}</div>
|
||||
)}
|
||||
{isChatVar && (
|
||||
<div title={itemData.des} className='ml-1 w-0 grow truncate text-[13px] font-normal text-gray-900'>{itemData.variable.replace('conversation.', '')}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='ml-1 shrink-0 text-xs font-normal text-gray-500 capitalize'>{itemData.type}</div>
|
||||
{isObj && (
|
||||
@@ -211,7 +220,7 @@ const VarReferenceVars: FC<Props> = ({
|
||||
const [searchText, setSearchText] = useState('')
|
||||
|
||||
const filteredVars = vars.filter((v) => {
|
||||
const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.'))
|
||||
const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.'))
|
||||
return children.length > 0
|
||||
}).filter((node) => {
|
||||
if (!searchText)
|
||||
@@ -222,7 +231,7 @@ const VarReferenceVars: FC<Props> = ({
|
||||
})
|
||||
return children.length > 0
|
||||
}).map((node) => {
|
||||
let vars = node.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.'))
|
||||
let vars = node.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.'))
|
||||
if (searchText) {
|
||||
const searchTextLower = searchText.toLowerCase()
|
||||
if (!node.title.toLowerCase().includes(searchTextLower))
|
||||
|
||||
@@ -24,6 +24,7 @@ export const useNodeHelpLink = (nodeType: BlockEnum) => {
|
||||
[BlockEnum.TemplateTransform]: 'template',
|
||||
[BlockEnum.VariableAssigner]: 'variable_assigner',
|
||||
[BlockEnum.VariableAggregator]: 'variable_assigner',
|
||||
[BlockEnum.Assigner]: 'variable_assignment',
|
||||
[BlockEnum.Iteration]: 'iteration',
|
||||
[BlockEnum.ParameterExtractor]: 'parameter_extractor',
|
||||
[BlockEnum.HttpRequest]: 'http_request',
|
||||
@@ -43,6 +44,7 @@ export const useNodeHelpLink = (nodeType: BlockEnum) => {
|
||||
[BlockEnum.TemplateTransform]: 'template',
|
||||
[BlockEnum.VariableAssigner]: 'variable-assigner',
|
||||
[BlockEnum.VariableAggregator]: 'variable-assigner',
|
||||
[BlockEnum.Assigner]: 'variable-assignment',
|
||||
[BlockEnum.Iteration]: 'iteration',
|
||||
[BlockEnum.ParameterExtractor]: 'parameter-extractor',
|
||||
[BlockEnum.HttpRequest]: 'http-request',
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
useNodeDataUpdate,
|
||||
useWorkflow,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
import { getNodeInfoById, isENV, isSystemVar, toNodeOutputVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isSystemVar, toNodeOutputVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
|
||||
import type { CommonNodeType, InputVar, ValueSelector, Var, Variable } from '@/app/components/workflow/types'
|
||||
import { BlockEnum, InputVarType, NodeRunningStatus, VarType } from '@/app/components/workflow/types'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { getIterationSingleNodeRunUrl, singleNodeRun } from '@/service/workflow'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import LLMDefault from '@/app/components/workflow/nodes/llm/default'
|
||||
@@ -95,12 +95,13 @@ const useOneStepRun = <T>({
|
||||
}: Params<T>) => {
|
||||
const { t } = useTranslation()
|
||||
const { getBeforeNodesInSameBranch, getBeforeNodesInSameBranchIncludeParent } = useWorkflow() as any
|
||||
const conversationVariables = useStore(s => s.conversationVariables)
|
||||
const isChatMode = useIsChatMode()
|
||||
const isIteration = data.type === BlockEnum.Iteration
|
||||
|
||||
const availableNodes = getBeforeNodesInSameBranch(id)
|
||||
const availableNodesIncludeParent = getBeforeNodesInSameBranchIncludeParent(id)
|
||||
const allOutputVars = toNodeOutputVars(availableNodes, isChatMode)
|
||||
const allOutputVars = toNodeOutputVars(availableNodes, isChatMode, undefined, undefined, conversationVariables)
|
||||
const getVar = (valueSelector: ValueSelector): Var | undefined => {
|
||||
let res: Var | undefined
|
||||
const isSystem = valueSelector[0] === 'sys'
|
||||
@@ -116,7 +117,8 @@ const useOneStepRun = <T>({
|
||||
|
||||
valueSelector.slice(1).forEach((key, i) => {
|
||||
const isLast = i === valueSelector.length - 2
|
||||
curr = curr?.find((v: any) => v.variable === key)
|
||||
// conversation variable is start with 'conversation.'
|
||||
curr = curr?.find((v: any) => v.variable.replace('conversation.', '') === key)
|
||||
if (isLast) {
|
||||
res = curr
|
||||
}
|
||||
@@ -369,6 +371,7 @@ const useOneStepRun = <T>({
|
||||
nodeType: varInfo?.type,
|
||||
nodeName: varInfo?.title || availableNodesIncludeParent[0]?.data.title, // default start node title
|
||||
variable: isSystemVar(item) ? item.join('.') : item[item.length - 1],
|
||||
isChatVar: isConversationVar(item),
|
||||
},
|
||||
variable: `#${item.join('.')}#`,
|
||||
value_selector: item,
|
||||
|
||||
46
web/app/components/workflow/nodes/assigner/default.ts
Normal file
46
web/app/components/workflow/nodes/assigner/default.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { BlockEnum } from '../../types'
|
||||
import type { NodeDefault } from '../../types'
|
||||
import { type AssignerNodeType, WriteMode } from './types'
|
||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
|
||||
const i18nPrefix = 'workflow.errorMsg'
|
||||
|
||||
const nodeDefault: NodeDefault<AssignerNodeType> = {
|
||||
defaultValue: {
|
||||
assigned_variable_selector: [],
|
||||
write_mode: WriteMode.Overwrite,
|
||||
input_variable_selector: [],
|
||||
},
|
||||
getAvailablePrevNodes(isChatMode: boolean) {
|
||||
const nodes = isChatMode
|
||||
? ALL_CHAT_AVAILABLE_BLOCKS
|
||||
: ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End)
|
||||
return nodes
|
||||
},
|
||||
getAvailableNextNodes(isChatMode: boolean) {
|
||||
const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS
|
||||
return nodes
|
||||
},
|
||||
checkValid(payload: AssignerNodeType, t: any) {
|
||||
let errorMessages = ''
|
||||
const {
|
||||
assigned_variable_selector: assignedVarSelector,
|
||||
write_mode: writeMode,
|
||||
input_variable_selector: toAssignerVarSelector,
|
||||
} = payload
|
||||
|
||||
if (!errorMessages && !assignedVarSelector?.length)
|
||||
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') })
|
||||
|
||||
if (!errorMessages && writeMode !== WriteMode.Clear) {
|
||||
if (!toAssignerVarSelector?.length)
|
||||
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.variable') })
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: !errorMessages,
|
||||
errorMessage: errorMessages,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default nodeDefault
|
||||
47
web/app/components/workflow/nodes/assigner/node.tsx
Normal file
47
web/app/components/workflow/nodes/assigner/node.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useNodes } from 'reactflow'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NodeVariableItem from '../variable-assigner/components/node-variable-item'
|
||||
import { type AssignerNodeType } from './types'
|
||||
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.assigner'
|
||||
|
||||
const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({
|
||||
data,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const nodes: Node[] = useNodes()
|
||||
const { assigned_variable_selector: variable, write_mode: writeMode } = data
|
||||
|
||||
if (!variable || variable.length === 0)
|
||||
return null
|
||||
|
||||
const isSystem = isSystemVar(variable)
|
||||
const isEnv = isENV(variable)
|
||||
const isChatVar = isConversationVar(variable)
|
||||
|
||||
const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0])
|
||||
const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.')
|
||||
return (
|
||||
<div className='relative px-3'>
|
||||
<div className='mb-1 system-2xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.assignedVariable`)}</div>
|
||||
<NodeVariableItem
|
||||
node={node as Node}
|
||||
isEnv={isEnv}
|
||||
isChatVar={isChatVar}
|
||||
varName={varName}
|
||||
className='bg-workflow-block-parma-bg'
|
||||
/>
|
||||
<div className='my-2 flex justify-between items-center h-[22px] px-[5px] bg-workflow-block-parma-bg radius-sm'>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.writeMode`)}</div>
|
||||
<div className='system-xs-medium text-text-secondary'>{t(`${i18nPrefix}.${writeMode}`)}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(NodeComponent)
|
||||
87
web/app/components/workflow/nodes/assigner/panel.tsx
Normal file
87
web/app/components/workflow/nodes/assigner/panel.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||
import OptionCard from '../_base/components/option-card'
|
||||
import useConfig from './use-config'
|
||||
import { WriteMode } from './types'
|
||||
import type { AssignerNodeType } from './types'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import { type NodePanelProps } from '@/app/components/workflow/types'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.assigner'
|
||||
|
||||
const Panel: FC<NodePanelProps<AssignerNodeType>> = ({
|
||||
id,
|
||||
data,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
readOnly,
|
||||
inputs,
|
||||
handleAssignedVarChanges,
|
||||
isSupportAppend,
|
||||
writeModeTypes,
|
||||
handleWriteModeChange,
|
||||
filterAssignedVar,
|
||||
filterToAssignedVar,
|
||||
handleToAssignedVarChange,
|
||||
toAssignedVarType,
|
||||
} = useConfig(id, data)
|
||||
|
||||
return (
|
||||
<div className='mt-2'>
|
||||
<div className='px-4 pb-4 space-y-4'>
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.assignedVariable`)}
|
||||
>
|
||||
<VarReferencePicker
|
||||
readonly={readOnly}
|
||||
nodeId={id}
|
||||
isShowNodeName
|
||||
value={inputs.assigned_variable_selector || []}
|
||||
onChange={handleAssignedVarChanges}
|
||||
filterVar={filterAssignedVar}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.writeMode`)}
|
||||
tooltip={t(`${i18nPrefix}.writeModeTip`)!}
|
||||
>
|
||||
<div className={cn('grid gap-2 grid-cols-3')}>
|
||||
{writeModeTypes.map(type => (
|
||||
<OptionCard
|
||||
key={type}
|
||||
title={t(`${i18nPrefix}.${type}`)}
|
||||
onSelect={handleWriteModeChange(type)}
|
||||
selected={inputs.write_mode === type}
|
||||
disabled={!isSupportAppend && type === WriteMode.Append}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Field>
|
||||
{inputs.write_mode !== WriteMode.Clear && (
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.setVariable`)}
|
||||
>
|
||||
<VarReferencePicker
|
||||
readonly={readOnly}
|
||||
nodeId={id}
|
||||
isShowNodeName
|
||||
value={inputs.input_variable_selector || []}
|
||||
onChange={handleToAssignedVarChange}
|
||||
filterVar={filterToAssignedVar}
|
||||
valueTypePlaceHolder={toAssignedVarType}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Panel)
|
||||
13
web/app/components/workflow/nodes/assigner/types.ts
Normal file
13
web/app/components/workflow/nodes/assigner/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
|
||||
|
||||
export enum WriteMode {
|
||||
Overwrite = 'over-write',
|
||||
Append = 'append',
|
||||
Clear = 'clear',
|
||||
}
|
||||
|
||||
export type AssignerNodeType = CommonNodeType & {
|
||||
assigned_variable_selector: ValueSelector
|
||||
write_mode: WriteMode
|
||||
input_variable_selector: ValueSelector
|
||||
}
|
||||
144
web/app/components/workflow/nodes/assigner/use-config.ts
Normal file
144
web/app/components/workflow/nodes/assigner/use-config.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import produce from 'immer'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { isEqual } from 'lodash-es'
|
||||
import { VarType } from '../../types'
|
||||
import type { ValueSelector, Var } from '../../types'
|
||||
import { type AssignerNodeType, WriteMode } from './types'
|
||||
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useNodesReadOnly,
|
||||
useWorkflow,
|
||||
useWorkflowVariables,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
|
||||
const useConfig = (id: string, payload: AssignerNodeType) => {
|
||||
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||
const isChatMode = useIsChatMode()
|
||||
|
||||
const store = useStoreApi()
|
||||
const { getBeforeNodesInSameBranch } = useWorkflow()
|
||||
|
||||
const {
|
||||
getNodes,
|
||||
} = store.getState()
|
||||
const currentNode = getNodes().find(n => n.id === id)
|
||||
const isInIteration = payload.isInIteration
|
||||
const iterationNode = isInIteration ? getNodes().find(n => n.id === currentNode!.parentId) : null
|
||||
const availableNodes = useMemo(() => {
|
||||
return getBeforeNodesInSameBranch(id)
|
||||
}, [getBeforeNodesInSameBranch, id])
|
||||
const { inputs, setInputs } = useNodeCrud<AssignerNodeType>(id, payload)
|
||||
|
||||
const { getCurrentVariableType } = useWorkflowVariables()
|
||||
const assignedVarType = getCurrentVariableType({
|
||||
parentNode: iterationNode,
|
||||
valueSelector: inputs.assigned_variable_selector || [],
|
||||
availableNodes,
|
||||
isChatMode,
|
||||
isConstant: false,
|
||||
})
|
||||
|
||||
const isSupportAppend = useCallback((varType: VarType) => {
|
||||
return [VarType.arrayString, VarType.arrayNumber, VarType.arrayObject].includes(varType)
|
||||
}, [])
|
||||
|
||||
const isCurrSupportAppend = useMemo(() => isSupportAppend(assignedVarType), [assignedVarType, isSupportAppend])
|
||||
|
||||
const handleAssignedVarChanges = useCallback((variable: ValueSelector | string) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.assigned_variable_selector = variable as ValueSelector
|
||||
draft.input_variable_selector = []
|
||||
|
||||
const newVarType = getCurrentVariableType({
|
||||
parentNode: iterationNode,
|
||||
valueSelector: draft.assigned_variable_selector || [],
|
||||
availableNodes,
|
||||
isChatMode,
|
||||
isConstant: false,
|
||||
})
|
||||
|
||||
if (inputs.write_mode === WriteMode.Append && !isSupportAppend(newVarType))
|
||||
draft.write_mode = WriteMode.Overwrite
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs, getCurrentVariableType, iterationNode, availableNodes, isChatMode, isSupportAppend])
|
||||
|
||||
const writeModeTypes = [WriteMode.Overwrite, WriteMode.Append, WriteMode.Clear]
|
||||
|
||||
const handleWriteModeChange = useCallback((writeMode: WriteMode) => {
|
||||
return () => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.write_mode = writeMode
|
||||
if (inputs.write_mode === WriteMode.Clear)
|
||||
draft.input_variable_selector = []
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const toAssignedVarType = useMemo(() => {
|
||||
const { write_mode } = inputs
|
||||
if (write_mode === WriteMode.Overwrite)
|
||||
return assignedVarType
|
||||
if (write_mode === WriteMode.Append) {
|
||||
if (assignedVarType === VarType.arrayString)
|
||||
return VarType.string
|
||||
if (assignedVarType === VarType.arrayNumber)
|
||||
return VarType.number
|
||||
if (assignedVarType === VarType.arrayObject)
|
||||
return VarType.object
|
||||
}
|
||||
return VarType.string
|
||||
}, [assignedVarType, inputs])
|
||||
|
||||
const filterAssignedVar = useCallback((varPayload: Var, selector: ValueSelector) => {
|
||||
return selector.join('.').startsWith('conversation')
|
||||
}, [])
|
||||
|
||||
const filterToAssignedVar = useCallback((varPayload: Var, selector: ValueSelector) => {
|
||||
if (isEqual(selector, inputs.assigned_variable_selector))
|
||||
return false
|
||||
|
||||
if (inputs.write_mode === WriteMode.Overwrite) {
|
||||
return varPayload.type === assignedVarType
|
||||
}
|
||||
else if (inputs.write_mode === WriteMode.Append) {
|
||||
switch (assignedVarType) {
|
||||
case VarType.arrayString:
|
||||
return varPayload.type === VarType.string
|
||||
case VarType.arrayNumber:
|
||||
return varPayload.type === VarType.number
|
||||
case VarType.arrayObject:
|
||||
return varPayload.type === VarType.object
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, [inputs.assigned_variable_selector, inputs.write_mode, assignedVarType])
|
||||
|
||||
const handleToAssignedVarChange = useCallback((value: ValueSelector | string) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.input_variable_selector = value as ValueSelector
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
return {
|
||||
readOnly,
|
||||
inputs,
|
||||
handleAssignedVarChanges,
|
||||
assignedVarType,
|
||||
isSupportAppend: isCurrSupportAppend,
|
||||
writeModeTypes,
|
||||
handleWriteModeChange,
|
||||
filterAssignedVar,
|
||||
filterToAssignedVar,
|
||||
handleToAssignedVarChange,
|
||||
toAssignedVarType,
|
||||
}
|
||||
}
|
||||
|
||||
export default useConfig
|
||||
5
web/app/components/workflow/nodes/assigner/utils.ts
Normal file
5
web/app/components/workflow/nodes/assigner/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { AssignerNodeType } from './types'
|
||||
|
||||
export const checkNodeValid = (payload: AssignerNodeType) => {
|
||||
return true
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import ToolNode from './tool/node'
|
||||
import ToolPanel from './tool/panel'
|
||||
import VariableAssignerNode from './variable-assigner/node'
|
||||
import VariableAssignerPanel from './variable-assigner/panel'
|
||||
import AssignerNode from './assigner/node'
|
||||
import AssignerPanel from './assigner/panel'
|
||||
import ParameterExtractorNode from './parameter-extractor/node'
|
||||
import ParameterExtractorPanel from './parameter-extractor/panel'
|
||||
import IterationNode from './iteration/node'
|
||||
@@ -42,6 +44,7 @@ export const NodeComponentMap: Record<string, ComponentType<any>> = {
|
||||
[BlockEnum.HttpRequest]: HttpNode,
|
||||
[BlockEnum.Tool]: ToolNode,
|
||||
[BlockEnum.VariableAssigner]: VariableAssignerNode,
|
||||
[BlockEnum.Assigner]: AssignerNode,
|
||||
[BlockEnum.VariableAggregator]: VariableAssignerNode,
|
||||
[BlockEnum.ParameterExtractor]: ParameterExtractorNode,
|
||||
[BlockEnum.Iteration]: IterationNode,
|
||||
@@ -61,6 +64,7 @@ export const PanelComponentMap: Record<string, ComponentType<any>> = {
|
||||
[BlockEnum.Tool]: ToolPanel,
|
||||
[BlockEnum.VariableAssigner]: VariableAssignerPanel,
|
||||
[BlockEnum.VariableAggregator]: VariableAssignerPanel,
|
||||
[BlockEnum.Assigner]: AssignerPanel,
|
||||
[BlockEnum.ParameterExtractor]: ParameterExtractorPanel,
|
||||
[BlockEnum.Iteration]: IterationPanel,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react'
|
||||
import cn from 'classnames'
|
||||
import type { EndNodeType } from './types'
|
||||
import type { NodeProps, Variable } from '@/app/components/workflow/types'
|
||||
import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useWorkflow,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
|
||||
const Node: FC<NodeProps<EndNodeType>> = ({
|
||||
@@ -44,6 +44,7 @@ const Node: FC<NodeProps<EndNodeType>> = ({
|
||||
const node = getNode(value_selector[0])
|
||||
const isSystem = isSystemVar(value_selector)
|
||||
const isEnv = isENV(value_selector)
|
||||
const isChatVar = isConversationVar(value_selector)
|
||||
const varName = isSystem ? `sys.${value_selector[value_selector.length - 1]}` : value_selector[value_selector.length - 1]
|
||||
const varType = getCurrentVariableType({
|
||||
valueSelector: value_selector,
|
||||
@@ -53,7 +54,7 @@ const Node: FC<NodeProps<EndNodeType>> = ({
|
||||
return (
|
||||
<div key={index} className='flex items-center h-6 justify-between bg-gray-100 rounded-md px-1 space-x-1 text-xs font-normal text-gray-700'>
|
||||
<div className='flex items-center text-xs font-medium text-gray-500'>
|
||||
{!isEnv && (
|
||||
{!isEnv && !isChatVar && (
|
||||
<>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
@@ -66,9 +67,11 @@ const Node: FC<NodeProps<EndNodeType>> = ({
|
||||
</>
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', isEnv && '!max-w-[70px] text-gray-900')}>{varName}</div>
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
|
||||
<div className={cn('max-w-[50px] ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && '!max-w-[70px] text-gray-900')}>{varName}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='text-xs font-normal text-gray-700'>
|
||||
|
||||
@@ -17,6 +17,8 @@ type Props = {
|
||||
onChange: (newList: KeyValue[]) => void
|
||||
onAdd: () => void
|
||||
// onSwitchToBulkEdit: () => void
|
||||
keyNotSupportVar?: boolean
|
||||
insertVarTipToLeft?: boolean
|
||||
}
|
||||
|
||||
const KeyValueList: FC<Props> = ({
|
||||
@@ -26,6 +28,8 @@ const KeyValueList: FC<Props> = ({
|
||||
onChange,
|
||||
onAdd,
|
||||
// onSwitchToBulkEdit,
|
||||
keyNotSupportVar,
|
||||
insertVarTipToLeft,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -47,6 +51,9 @@ const KeyValueList: FC<Props> = ({
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
if (!Array.isArray(list))
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className='border border-gray-200 rounded-lg overflow-hidden'>
|
||||
<div className='flex items-center h-7 leading-7 text-xs font-medium text-gray-500 uppercase'>
|
||||
@@ -79,6 +86,8 @@ const KeyValueList: FC<Props> = ({
|
||||
onAdd={onAdd}
|
||||
readonly={readonly}
|
||||
canRemove={list.length > 1}
|
||||
keyNotSupportVar={keyNotSupportVar}
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Props = {
|
||||
onRemove?: () => void
|
||||
placeholder?: string
|
||||
readOnly?: boolean
|
||||
insertVarTipToLeft?: boolean
|
||||
}
|
||||
|
||||
const InputItem: FC<Props> = ({
|
||||
@@ -30,6 +31,7 @@ const InputItem: FC<Props> = ({
|
||||
onRemove,
|
||||
placeholder,
|
||||
readOnly,
|
||||
insertVarTipToLeft,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -64,6 +66,7 @@ const InputItem: FC<Props> = ({
|
||||
placeholder={t('workflow.nodes.http.insertVarPlaceholder')!}
|
||||
placeholderClassName='!leading-[21px]'
|
||||
promptMinHeightClassName='h-full'
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
)
|
||||
: <div
|
||||
@@ -83,6 +86,7 @@ const InputItem: FC<Props> = ({
|
||||
placeholder={t('workflow.nodes.http.insertVarPlaceholder')!}
|
||||
placeholderClassName='!leading-[21px]'
|
||||
promptMinHeightClassName='h-full'
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import produce from 'immer'
|
||||
import type { KeyValue } from '../../../types'
|
||||
import InputItem from './input-item'
|
||||
import cn from '@/utils/classnames'
|
||||
import Input from '@/app/components/base/input'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.http'
|
||||
|
||||
@@ -20,6 +21,8 @@ type Props = {
|
||||
onRemove: () => void
|
||||
isLastItem: boolean
|
||||
onAdd: () => void
|
||||
keyNotSupportVar?: boolean
|
||||
insertVarTipToLeft?: boolean
|
||||
}
|
||||
|
||||
const KeyValueItem: FC<Props> = ({
|
||||
@@ -33,6 +36,8 @@ const KeyValueItem: FC<Props> = ({
|
||||
onRemove,
|
||||
isLastItem,
|
||||
onAdd,
|
||||
keyNotSupportVar,
|
||||
insertVarTipToLeft,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -51,15 +56,26 @@ const KeyValueItem: FC<Props> = ({
|
||||
// group class name is for hover row show remove button
|
||||
<div className={cn(className, 'group flex h-min-7 border-t border-gray-200')}>
|
||||
<div className='w-1/2 border-r border-gray-200'>
|
||||
<InputItem
|
||||
instanceId={`http-key-${instanceId}`}
|
||||
nodeId={nodeId}
|
||||
value={payload.key}
|
||||
onChange={handleChange('key')}
|
||||
hasRemove={false}
|
||||
placeholder={t(`${i18nPrefix}.key`)!}
|
||||
readOnly={readonly}
|
||||
/>
|
||||
{!keyNotSupportVar
|
||||
? (
|
||||
<InputItem
|
||||
instanceId={`http-key-${instanceId}`}
|
||||
nodeId={nodeId}
|
||||
value={payload.key}
|
||||
onChange={handleChange('key')}
|
||||
hasRemove={false}
|
||||
placeholder={t(`${i18nPrefix}.key`)!}
|
||||
readOnly={readonly}
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Input
|
||||
className='rounded-none bg-white border-none system-sm-regular focus:ring-0 focus:bg-gray-100! hover:bg-gray-50'
|
||||
value={payload.key}
|
||||
onChange={handleChange('key')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className='w-1/2'>
|
||||
<InputItem
|
||||
@@ -71,6 +87,7 @@ const KeyValueItem: FC<Props> = ({
|
||||
onRemove={onRemove}
|
||||
placeholder={t(`${i18nPrefix}.value`)!}
|
||||
readOnly={readonly}
|
||||
insertVarTipToLeft={insertVarTipToLeft}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
isComparisonOperatorNeedTranslate,
|
||||
} from '../utils'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import cn from '@/utils/classnames'
|
||||
import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
|
||||
type ConditionValueProps = {
|
||||
variableSelector: string[]
|
||||
@@ -27,7 +27,8 @@ const ConditionValue = ({
|
||||
const variableName = isSystemVar(variableSelector) ? variableSelector.slice(0).join('.') : variableSelector.slice(1).join('.')
|
||||
const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator
|
||||
const notHasValue = comparisonOperatorNotRequireValue(operator)
|
||||
|
||||
const isEnvVar = isENV(variableSelector)
|
||||
const isChatVar = isConversationVar(variableSelector)
|
||||
const formatValue = useMemo(() => {
|
||||
if (notHasValue)
|
||||
return ''
|
||||
@@ -43,8 +44,10 @@ const ConditionValue = ({
|
||||
|
||||
return (
|
||||
<div className='flex items-center px-1 h-6 rounded-md bg-workflow-block-parma-bg'>
|
||||
{!isENV(variableSelector) && <Variable02 className='shrink-0 mr-1 w-3.5 h-3.5 text-text-accent' />}
|
||||
{isENV(variableSelector) && <Env className='shrink-0 mr-1 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
{!isEnvVar && !isChatVar && <Variable02 className='shrink-0 mr-1 w-3.5 h-3.5 text-text-accent' />}
|
||||
{isEnvVar && <Env className='shrink-0 mr-1 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'shrink-0 truncate text-xs font-medium text-text-accent',
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { filterVar } from '../utils'
|
||||
import AddVariable from './add-variable'
|
||||
import NodeVariableItem from './node-variable-item'
|
||||
import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.variableAssigner'
|
||||
@@ -124,6 +124,8 @@ const NodeGroupItem = ({
|
||||
!!item.variables.length && item.variables.map((variable = [], index) => {
|
||||
const isSystem = isSystemVar(variable)
|
||||
const isEnv = isENV(variable)
|
||||
const isChatVar = isConversationVar(variable)
|
||||
|
||||
const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0])
|
||||
const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.')
|
||||
|
||||
@@ -131,6 +133,7 @@ const NodeGroupItem = ({
|
||||
<NodeVariableItem
|
||||
key={index}
|
||||
isEnv={isEnv}
|
||||
isChatVar={isChatVar}
|
||||
node={node as Node}
|
||||
varName={varName}
|
||||
showBorder={showSelectedBorder || showSelectionBorder}
|
||||
|
||||
@@ -3,28 +3,33 @@ import cn from '@/utils/classnames'
|
||||
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
|
||||
import { Line3 } from '@/app/components/base/icons/src/public/common'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
|
||||
type NodeVariableItemProps = {
|
||||
isEnv: boolean
|
||||
isChatVar: boolean
|
||||
node: Node
|
||||
varName: string
|
||||
showBorder?: boolean
|
||||
className?: string
|
||||
}
|
||||
const NodeVariableItem = ({
|
||||
isEnv,
|
||||
isChatVar,
|
||||
node,
|
||||
varName,
|
||||
showBorder,
|
||||
className,
|
||||
}: NodeVariableItemProps) => {
|
||||
return (
|
||||
<div className={cn(
|
||||
'relative flex items-center mt-0.5 h-6 bg-gray-100 rounded-md px-1 text-xs font-normal text-gray-700',
|
||||
showBorder && '!bg-black/[0.02]',
|
||||
className,
|
||||
)}>
|
||||
{!isEnv && (
|
||||
{!isEnv && !isChatVar && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
@@ -37,9 +42,10 @@ const NodeVariableItem = ({
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center text-primary-600'>
|
||||
{!isEnv && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
|
||||
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
|
||||
<div className={cn('max-w-[75px] truncate ml-0.5 text-xs font-medium', isEnv && 'text-gray-900')} title={varName}>{varName}</div>
|
||||
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
|
||||
<div className={cn('max-w-[75px] truncate ml-0.5 text-xs font-medium', (isEnv || isChatVar) && 'text-gray-900')} title={varName}>{varName}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -143,6 +143,7 @@ export const useGetAvailableVars = () => {
|
||||
beforeNodes: uniqBy(availableNodes, 'id').filter(node => node.id !== nodeId),
|
||||
isChatMode,
|
||||
hideEnv,
|
||||
hideChatVar: hideEnv,
|
||||
filterVar,
|
||||
})
|
||||
.map(node => ({
|
||||
|
||||
Reference in New Issue
Block a user