mirror of https://github.com/langgenius/dify.git
feat: var picker support choose type
This commit is contained in:
parent
2af2e2be67
commit
8bd74d5abf
|
|
@ -249,7 +249,7 @@ export const LLM_OUTPUT_STRUCT: Var[] = [
|
|||
variable: 'text',
|
||||
type: VarType.string,
|
||||
},
|
||||
|
||||
USAGE,
|
||||
]
|
||||
|
||||
export const KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT: Var[] = [
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ type Item = {
|
|||
}
|
||||
type Props = {
|
||||
trigger?: JSX.Element
|
||||
DropDownIcon?: any
|
||||
noLeft?: boolean
|
||||
options: Item[]
|
||||
value: string
|
||||
onChange: (value: any) => void
|
||||
|
|
@ -24,6 +26,8 @@ type Props = {
|
|||
|
||||
const TypeSelector: FC<Props> = ({
|
||||
trigger,
|
||||
DropDownIcon = ChevronSelectorVertical,
|
||||
noLeft,
|
||||
options: list,
|
||||
value,
|
||||
onChange,
|
||||
|
|
@ -41,7 +45,7 @@ const TypeSelector: FC<Props> = ({
|
|||
setHide()
|
||||
}, ref)
|
||||
return (
|
||||
<div className={cn(!trigger && 'left-[-8px]', 'relative')} ref={ref}>
|
||||
<div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative')} ref={ref}>
|
||||
{trigger
|
||||
? (
|
||||
<div
|
||||
|
|
@ -55,7 +59,7 @@ const TypeSelector: FC<Props> = ({
|
|||
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-sm font-semibold', uppercase && 'uppercase')}>{item?.label}</div>
|
||||
<ChevronSelectorVertical className='w-3 h-3 ' />
|
||||
<DropDownIcon className='w-3 h-3 ' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ import React, { useCallback } from 'react'
|
|||
import produce from 'immer'
|
||||
import RemoveButton from '../remove-button'
|
||||
import VarReferencePicker from './var-reference-picker'
|
||||
import type { ValueSelector, Variable } from '@/app/components/workflow/types'
|
||||
import { type ValueSelector, type Variable } from '@/app/components/workflow/types'
|
||||
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
|
||||
type Props = {
|
||||
nodeId: string
|
||||
readonly: boolean
|
||||
list: Variable[]
|
||||
onChange: (list: Variable[]) => void
|
||||
isSupportConstantValue?: boolean
|
||||
}
|
||||
|
||||
const VarList: FC<Props> = ({
|
||||
|
|
@ -18,6 +20,7 @@ const VarList: FC<Props> = ({
|
|||
readonly,
|
||||
list,
|
||||
onChange,
|
||||
isSupportConstantValue,
|
||||
}) => {
|
||||
const handleVarNameChange = useCallback((index: number) => {
|
||||
return (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
@ -29,11 +32,21 @@ const VarList: FC<Props> = ({
|
|||
}, [list, onChange])
|
||||
|
||||
const handleVarReferenceChange = useCallback((index: number) => {
|
||||
return (value: ValueSelector) => {
|
||||
return (value: ValueSelector | string, varKindType: VarKindType) => {
|
||||
const newList = produce(list, (draft) => {
|
||||
draft[index].value_selector = value
|
||||
if (!draft[index].variable)
|
||||
draft[index].variable = value[value.length - 1]
|
||||
if (!isSupportConstantValue || varKindType === VarKindType.selector) {
|
||||
draft[index].value_selector = value as ValueSelector
|
||||
if (isSupportConstantValue)
|
||||
draft[index].variable_type = VarKindType.selector
|
||||
|
||||
if (!draft[index].variable)
|
||||
draft[index].variable = value[value.length - 1]
|
||||
}
|
||||
else {
|
||||
draft[index].variable_type = VarKindType.static
|
||||
draft[index].value_selector = value as ValueSelector
|
||||
draft[index].value = value as string
|
||||
}
|
||||
})
|
||||
onChange(newList)
|
||||
}
|
||||
|
|
@ -63,8 +76,10 @@ const VarList: FC<Props> = ({
|
|||
readonly={readonly}
|
||||
isShowNodeName
|
||||
className='grow'
|
||||
value={item.value_selector}
|
||||
value={item.variable_type === VarKindType.static ? (item.value || '') : (item.value_selector || [])}
|
||||
isSupportConstantValue={isSupportConstantValue}
|
||||
onChange={handleVarReferenceChange(index)}
|
||||
defaultVarKindType={item.variable_type}
|
||||
/>
|
||||
<RemoveButton
|
||||
className='!p-2 !bg-gray-100 hover:!bg-gray-200'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import { isArray } from 'lodash-es'
|
||||
import VarReferencePopup from './var-reference-popup'
|
||||
|
|
@ -19,14 +19,20 @@ import {
|
|||
useIsChatMode,
|
||||
useWorkflow,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
width?: number
|
||||
nodeId: string
|
||||
isShowNodeName: boolean
|
||||
readonly: boolean
|
||||
value: ValueSelector
|
||||
onChange: (value: ValueSelector) => void
|
||||
value: ValueSelector | string
|
||||
onChange: (value: ValueSelector | string, varKindType: VarKindType) => void
|
||||
isSupportConstantValue?: boolean
|
||||
defaultVarKindType?: VarKindType
|
||||
}
|
||||
|
||||
export const getNodeInfoById = (nodes: any, id: string) => {
|
||||
|
|
@ -38,31 +44,38 @@ export const getNodeInfoById = (nodes: any, id: string) => {
|
|||
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
nodeId,
|
||||
width,
|
||||
readonly,
|
||||
className,
|
||||
isShowNodeName,
|
||||
value,
|
||||
onChange,
|
||||
isSupportConstantValue,
|
||||
defaultVarKindType = VarKindType.static,
|
||||
}) => {
|
||||
const isChatMode = useIsChatMode()
|
||||
|
||||
const [varKindType, setVarKindType] = useState<VarKindType>(defaultVarKindType)
|
||||
const isConstant = isSupportConstantValue && varKindType === VarKindType.static
|
||||
const { getTreeLeafNodes, getBeforeNodesInSameBranch } = useWorkflow()
|
||||
const availableNodes = getBeforeNodesInSameBranch(nodeId)
|
||||
const outputVars = toNodeOutputVars(availableNodes, isChatMode)
|
||||
const [open, setOpen] = useState(false)
|
||||
const hasValue = value.length > 0
|
||||
const hasValue = !isConstant && value.length > 0
|
||||
const outputVarNodeId = hasValue ? value[0] : ''
|
||||
const outputVarNode = hasValue ? getNodeInfoById(availableNodes, outputVarNodeId)?.data : null
|
||||
const varName = hasValue ? value[value.length - 1] : ''
|
||||
|
||||
const getVarType = () => {
|
||||
if (isConstant)
|
||||
return 'undefined'
|
||||
|
||||
const targetVar = outputVars.find(v => v.nodeId === outputVarNodeId)
|
||||
if (!targetVar)
|
||||
return 'undefined'
|
||||
|
||||
let type: VarType = VarType.string
|
||||
let curr: any = targetVar.vars
|
||||
value.slice(1).forEach((key, i) => {
|
||||
let curr: any = targetVar.vars;
|
||||
(value as ValueSelector).slice(1).forEach((key, i) => {
|
||||
const isLast = i === value.length - 2
|
||||
curr = curr.find((v: any) => v.variable === key)
|
||||
if (isLast) {
|
||||
|
|
@ -76,6 +89,38 @@ const VarReferencePicker: FC<Props> = ({
|
|||
return type
|
||||
}
|
||||
|
||||
const varKindTypes = [
|
||||
{
|
||||
label: 'Variable',
|
||||
value: VarKindType.selector,
|
||||
},
|
||||
{
|
||||
label: 'Constant',
|
||||
value: VarKindType.static,
|
||||
},
|
||||
]
|
||||
|
||||
const handleVarKindTypeChange = useCallback((value: VarKindType) => {
|
||||
setVarKindType(value)
|
||||
if (value === VarKindType.static)
|
||||
onChange('', value)
|
||||
else
|
||||
onChange([], value)
|
||||
}, [varKindType])
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [isFocus, setIsFocus] = useState(false)
|
||||
const [controlFocus, setControlFocus] = useState(0)
|
||||
useEffect(() => {
|
||||
if (controlFocus && inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
setIsFocus(true)
|
||||
}
|
||||
}, [controlFocus])
|
||||
|
||||
const handleStaticChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(e.target.value as string, varKindType)
|
||||
}, [onChange, varKindType])
|
||||
|
||||
return (
|
||||
<div className={cn(className, !readonly && 'cursor-pointer')}>
|
||||
<PortalToFollowElem
|
||||
|
|
@ -83,47 +128,81 @@ const VarReferencePicker: FC<Props> = ({
|
|||
onOpenChange={setOpen}
|
||||
placement='bottom-start'
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='!flex'>
|
||||
<div className={cn('w-full h-8 p-1 rounded-lg bg-gray-100')}>
|
||||
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
|
||||
{hasValue && (
|
||||
<>
|
||||
{isShowNodeName && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
className='!text-gray-900'
|
||||
type={outputVarNode?.type}
|
||||
/>
|
||||
<PortalToFollowElemTrigger onClick={() => !isConstant && setOpen(!open)} className='!flex'>
|
||||
<div className={cn((open || isFocus) && 'border border-gray-300', 'flex items-center w-full h-8 p-1 rounded-lg bg-gray-100')}>
|
||||
{isSupportConstantValue
|
||||
? <div onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setOpen(false)
|
||||
}} className='mr-1 flex items-center space-x-1'>
|
||||
<TypeSelector
|
||||
noLeft
|
||||
triggerClassName='!text-xs'
|
||||
DropDownIcon={ChevronDown}
|
||||
value={varKindType}
|
||||
options={varKindTypes}
|
||||
onChange={handleVarKindTypeChange}
|
||||
/>
|
||||
<div className='h-4 w-px bg-black/5'></div>
|
||||
</div>
|
||||
: <div className='ml-1.5 mr-1'>
|
||||
<Variable02 className='w-3.5 h-3.5 text-gray-400' />
|
||||
</div>}
|
||||
{isConstant
|
||||
? (
|
||||
<input
|
||||
type='text'
|
||||
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'
|
||||
value={isConstant ? value : ''}
|
||||
onChange={handleStaticChange}
|
||||
onFocus={() => setIsFocus(true)}
|
||||
onBlur={() => setIsFocus(false)}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className={cn('inline-flex h-full items-center px-1.5 rounded-[5px]', hasValue && 'bg-white')}>
|
||||
{hasValue && (
|
||||
<>
|
||||
{isShowNodeName && (
|
||||
<div className='flex items-center'>
|
||||
<div className='p-[1px]'>
|
||||
<VarBlockIcon
|
||||
className='!text-gray-900'
|
||||
type={outputVarNode?.type}
|
||||
/>
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700'>{outputVarNode?.title}</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'>{varName}</div>
|
||||
</div>
|
||||
<div className='mx-0.5 text-xs font-medium text-gray-700'>{outputVarNode?.title}</div>
|
||||
<Line3 className='mr-0.5'></Line3>
|
||||
</div>
|
||||
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize'>{getVarType()}</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'>{varName}</div>
|
||||
</div>
|
||||
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize'>{getVarType()}</div>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent style={{
|
||||
zIndex: 100,
|
||||
minWidth: 227,
|
||||
}}>
|
||||
<VarReferencePopup
|
||||
vars={outputVars}
|
||||
onChange={(value) => {
|
||||
onChange(value)
|
||||
setOpen(false)
|
||||
}}
|
||||
/>
|
||||
{!isConstant && (
|
||||
<VarReferencePopup
|
||||
vars={outputVars}
|
||||
onChange={(value) => {
|
||||
onChange(value, varKindType)
|
||||
setOpen(false)
|
||||
}}
|
||||
itemWidth={width}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
export default React.memo(VarReferencePicker)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ type ObjectChildrenProps = {
|
|||
data: Var[]
|
||||
objPath: string[]
|
||||
onChange: (value: ValueSelector) => void
|
||||
itemWidth?: number
|
||||
}
|
||||
|
||||
type ItemProps = {
|
||||
|
|
@ -21,6 +22,7 @@ type ItemProps = {
|
|||
objPath: string[]
|
||||
itemData: Var
|
||||
onChange: (value: ValueSelector) => void
|
||||
itemWidth?: number
|
||||
}
|
||||
|
||||
const Item: FC<ItemProps> = ({
|
||||
|
|
@ -29,6 +31,7 @@ const Item: FC<ItemProps> = ({
|
|||
objPath,
|
||||
itemData,
|
||||
onChange,
|
||||
itemWidth,
|
||||
}) => {
|
||||
const isObj = itemData.type === VarType.object && itemData.children && itemData.children.length > 0
|
||||
const itemRef = useRef(null)
|
||||
|
|
@ -40,7 +43,11 @@ const Item: FC<ItemProps> = ({
|
|||
return (
|
||||
<div
|
||||
ref={itemRef}
|
||||
className={cn(isObj ? 'hover:bg-primary-50 pr-1' : 'hover:bg-gray-50 pr-[18px]', 'relative flex items-center h-6 w-[252px] pl-3 rounded-md cursor-pointer')}
|
||||
className={cn(
|
||||
isObj ? 'hover:bg-primary-50 pr-1' : 'hover:bg-gray-50 pr-[18px]',
|
||||
'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'>
|
||||
|
|
@ -59,6 +66,7 @@ const Item: FC<ItemProps> = ({
|
|||
objPath={[...objPath, itemData.variable]}
|
||||
data={itemData.children as Var[]}
|
||||
onChange={onChange}
|
||||
itemWidth={itemWidth}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -71,11 +79,15 @@ const ObjectChildren: FC<ObjectChildrenProps> = ({
|
|||
objPath,
|
||||
data,
|
||||
onChange,
|
||||
itemWidth,
|
||||
}) => {
|
||||
const currObjPath = objPath
|
||||
|
||||
return (
|
||||
<div className='absolute right-[248px] top-[-2px] bg-white rounded-lg border border-gray-200 shadow-lg space-y-1'>
|
||||
<div className='absolute top-[-2px] bg-white rounded-lg border border-gray-200 shadow-lg space-y-1' style={{
|
||||
right: itemWidth ? itemWidth - 4 : 215,
|
||||
minWidth: 252,
|
||||
}}>
|
||||
<div className='flex items-center h-[22px] px-3 text-xs font-normal text-gray-700'><span className='text-gray-500'>{title}.</span>{currObjPath.join('.')}</div>
|
||||
{
|
||||
data?.map((v, i) => (
|
||||
|
|
@ -96,14 +108,17 @@ const ObjectChildren: FC<ObjectChildrenProps> = ({
|
|||
type Props = {
|
||||
vars: NodeOutPutVar[]
|
||||
onChange: (value: ValueSelector) => void
|
||||
itemWidth?: number
|
||||
}
|
||||
const VarReferencePopup: FC<Props> = ({
|
||||
|
||||
vars,
|
||||
onChange,
|
||||
itemWidth,
|
||||
}) => {
|
||||
return (
|
||||
<div className='p-1 bg-white rounded-lg border border-gray-200 shadow-lg space-y-1'>
|
||||
<div className='p-1 bg-white rounded-lg border border-gray-200 shadow-lg space-y-1' style={{
|
||||
width: itemWidth || 228,
|
||||
}}>
|
||||
{vars.map((item, i) => (
|
||||
<div key={i}>
|
||||
<div className='flex items-center h-[22px] px-3 text-xs font-medium text-gray-500 uppercase'>{item.title}</div>
|
||||
|
|
@ -115,6 +130,7 @@ const VarReferencePopup: FC<Props> = ({
|
|||
objPath={[]}
|
||||
itemData={v}
|
||||
onChange={onChange}
|
||||
itemWidth={itemWidth}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import type { FC } from 'react'
|
|||
import React, { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import type { ToolVarInput } from '../types'
|
||||
import { VarType } from '../types'
|
||||
import { VarType as VarKindType } from '../types'
|
||||
import { type ValueSelector } from '@/app/components/workflow/types'
|
||||
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
|
@ -15,6 +16,7 @@ type Props = {
|
|||
schema: CredentialFormSchema[]
|
||||
value: ToolVarInput[]
|
||||
onChange: (value: ToolVarInput[]) => void
|
||||
isSupportConstantValue?: boolean
|
||||
}
|
||||
|
||||
const InputVarList: FC<Props> = ({
|
||||
|
|
@ -23,6 +25,7 @@ const InputVarList: FC<Props> = ({
|
|||
schema,
|
||||
value,
|
||||
onChange,
|
||||
isSupportConstantValue,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
|
||||
|
|
@ -35,17 +38,26 @@ const InputVarList: FC<Props> = ({
|
|||
})()
|
||||
|
||||
const handleChange = useCallback((variable: string) => {
|
||||
return (varValue: any) => {
|
||||
return (varValue: ValueSelector | string, varKindType: VarKindType) => {
|
||||
const newValue = produce(value, (draft: ToolVarInput[]) => {
|
||||
const target = draft.find(item => item.variable === variable)
|
||||
if (target) {
|
||||
target.value_selector = varValue // TODO: support constant value
|
||||
if (!isSupportConstantValue || varKindType === VarKindType.selector) {
|
||||
if (isSupportConstantValue)
|
||||
target.variable_type = VarKindType.selector
|
||||
|
||||
target.value_selector = varValue as ValueSelector
|
||||
}
|
||||
else {
|
||||
target.variable_type = VarKindType.static
|
||||
target.value = varValue as string
|
||||
}
|
||||
}
|
||||
else {
|
||||
draft.push({
|
||||
variable,
|
||||
variable_type: VarType.selector, // TODO: support constant value
|
||||
value_selector: varValue,
|
||||
variable_type: VarKindType.static,
|
||||
value: '',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -74,9 +86,12 @@ const InputVarList: FC<Props> = ({
|
|||
<VarReferencePicker
|
||||
readonly={readOnly}
|
||||
isShowNodeName
|
||||
width={372}
|
||||
nodeId={nodeId}
|
||||
value={varInput?.value_selector || []} // TODO: support constant value
|
||||
value={varInput?.variable_type === VarKindType.static ? (varInput?.value || '') : (varInput?.value_selector || [])}
|
||||
onChange={handleChange(variable)}
|
||||
isSupportConstantValue={isSupportConstantValue}
|
||||
defaultVarKindType={varInput?.variable_type}
|
||||
/>
|
||||
{tooltip && <div className='leading-[18px] text-xs font-normal text-gray-600'>{tooltip[language] || tooltip.en_US}</div>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
|
|||
schema={toolInputVarSchema as any}
|
||||
value={inputs.tool_parameters}
|
||||
onChange={setInputVar}
|
||||
isSupportConstantValue
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type {
|
|||
Node as ReactFlowNode,
|
||||
} from 'reactflow'
|
||||
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
|
||||
export enum BlockEnum {
|
||||
Start = 'start',
|
||||
|
|
@ -57,6 +58,8 @@ export type ValueSelector = string[] // [nodeId, key | obj key path]
|
|||
export type Variable = {
|
||||
variable: string
|
||||
value_selector: ValueSelector
|
||||
variable_type?: VarKindType
|
||||
value?: string
|
||||
}
|
||||
|
||||
export type VariableWithValue = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue