feat:conversation variable support file array (#21174)
Co-authored-by: kino.lu <kino.lu@vipshop.com>
This commit is contained in:
@@ -8,4 +8,5 @@ EMPTY_VALUE_MAPPING = {
|
|||||||
SegmentType.ARRAY_STRING: [],
|
SegmentType.ARRAY_STRING: [],
|
||||||
SegmentType.ARRAY_NUMBER: [],
|
SegmentType.ARRAY_NUMBER: [],
|
||||||
SegmentType.ARRAY_OBJECT: [],
|
SegmentType.ARRAY_OBJECT: [],
|
||||||
|
SegmentType.ARRAY_FILE: [],
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from core.file import File
|
||||||
from core.variables import SegmentType
|
from core.variables import SegmentType
|
||||||
|
|
||||||
from .enums import Operation
|
from .enums import Operation
|
||||||
@@ -85,6 +86,8 @@ def is_input_value_valid(*, variable_type: SegmentType, operation: Operation, va
|
|||||||
return isinstance(value, int | float)
|
return isinstance(value, int | float)
|
||||||
case SegmentType.ARRAY_OBJECT if operation == Operation.APPEND:
|
case SegmentType.ARRAY_OBJECT if operation == Operation.APPEND:
|
||||||
return isinstance(value, dict)
|
return isinstance(value, dict)
|
||||||
|
case SegmentType.ARRAY_FILE if operation == Operation.APPEND:
|
||||||
|
return isinstance(value, File)
|
||||||
|
|
||||||
# Array & Extend / Overwrite
|
# Array & Extend / Overwrite
|
||||||
case SegmentType.ARRAY_ANY if operation in {Operation.EXTEND, Operation.OVER_WRITE}:
|
case SegmentType.ARRAY_ANY if operation in {Operation.EXTEND, Operation.OVER_WRITE}:
|
||||||
@@ -95,6 +98,8 @@ def is_input_value_valid(*, variable_type: SegmentType, operation: Operation, va
|
|||||||
return isinstance(value, list) and all(isinstance(item, int | float) for item in value)
|
return isinstance(value, list) and all(isinstance(item, int | float) for item in value)
|
||||||
case SegmentType.ARRAY_OBJECT if operation in {Operation.EXTEND, Operation.OVER_WRITE}:
|
case SegmentType.ARRAY_OBJECT if operation in {Operation.EXTEND, Operation.OVER_WRITE}:
|
||||||
return isinstance(value, list) and all(isinstance(item, dict) for item in value)
|
return isinstance(value, list) and all(isinstance(item, dict) for item in value)
|
||||||
|
case SegmentType.ARRAY_FILE if operation in {Operation.EXTEND, Operation.OVER_WRITE}:
|
||||||
|
return isinstance(value, list) and all(isinstance(item, File) for item in value)
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
return False
|
return False
|
||||||
|
@@ -101,6 +101,8 @@ def _build_variable_from_mapping(*, mapping: Mapping[str, Any], selector: Sequen
|
|||||||
result = ArrayNumberVariable.model_validate(mapping)
|
result = ArrayNumberVariable.model_validate(mapping)
|
||||||
case SegmentType.ARRAY_OBJECT if isinstance(value, list):
|
case SegmentType.ARRAY_OBJECT if isinstance(value, list):
|
||||||
result = ArrayObjectVariable.model_validate(mapping)
|
result = ArrayObjectVariable.model_validate(mapping)
|
||||||
|
case SegmentType.ARRAY_FILE if isinstance(value, list):
|
||||||
|
result = ArrayFileVariable.model_validate(mapping)
|
||||||
case _:
|
case _:
|
||||||
raise VariableError(f"not supported value type {value_type}")
|
raise VariableError(f"not supported value type {value_type}")
|
||||||
if result.size > dify_config.MAX_VARIABLE_SIZE:
|
if result.size > dify_config.MAX_VARIABLE_SIZE:
|
||||||
|
@@ -37,6 +37,7 @@ const typeList = [
|
|||||||
ChatVarType.ArrayString,
|
ChatVarType.ArrayString,
|
||||||
ChatVarType.ArrayNumber,
|
ChatVarType.ArrayNumber,
|
||||||
ChatVarType.ArrayObject,
|
ChatVarType.ArrayObject,
|
||||||
|
ChatVarType.ArrayFile,
|
||||||
]
|
]
|
||||||
|
|
||||||
const objectPlaceholder = `# example
|
const objectPlaceholder = `# example
|
||||||
@@ -127,6 +128,7 @@ const ChatVariableModal = ({
|
|||||||
case ChatVarType.ArrayString:
|
case ChatVarType.ArrayString:
|
||||||
case ChatVarType.ArrayNumber:
|
case ChatVarType.ArrayNumber:
|
||||||
case ChatVarType.ArrayObject:
|
case ChatVarType.ArrayObject:
|
||||||
|
case ChatVarType.ArrayFile:
|
||||||
return value?.filter(Boolean) || []
|
return value?.filter(Boolean) || []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,84 +296,86 @@ const ChatVariableModal = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* default value */}
|
{/* default value */}
|
||||||
<div className='mb-4'>
|
{type !== ChatVarType.ArrayFile && (
|
||||||
<div className='system-sm-semibold mb-1 flex h-6 items-center justify-between text-text-secondary'>
|
<div className='mb-4'>
|
||||||
<div>{t('workflow.chatVariable.modal.value')}</div>
|
<div className='system-sm-semibold mb-1 flex h-6 items-center justify-between text-text-secondary'>
|
||||||
{(type === ChatVarType.ArrayString || type === ChatVarType.ArrayNumber) && (
|
<div>{t('workflow.chatVariable.modal.value')}</div>
|
||||||
<Button
|
{(type === ChatVarType.ArrayString || type === ChatVarType.ArrayNumber) && (
|
||||||
variant='ghost'
|
<Button
|
||||||
size='small'
|
variant='ghost'
|
||||||
className='text-text-tertiary'
|
size='small'
|
||||||
onClick={() => handleEditorChange(!editInJSON)}
|
className='text-text-tertiary'
|
||||||
>
|
onClick={() => handleEditorChange(!editInJSON)}
|
||||||
{editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />}
|
>
|
||||||
{editInJSON ? t('workflow.chatVariable.modal.oneByOne') : t('workflow.chatVariable.modal.editInJSON')}
|
{editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />}
|
||||||
</Button>
|
{editInJSON ? t('workflow.chatVariable.modal.oneByOne') : t('workflow.chatVariable.modal.editInJSON')}
|
||||||
)}
|
</Button>
|
||||||
{type === ChatVarType.Object && (
|
)}
|
||||||
<Button
|
{type === ChatVarType.Object && (
|
||||||
variant='ghost'
|
<Button
|
||||||
size='small'
|
variant='ghost'
|
||||||
className='text-text-tertiary'
|
size='small'
|
||||||
onClick={() => handleEditorChange(!editInJSON)}
|
className='text-text-tertiary'
|
||||||
>
|
onClick={() => handleEditorChange(!editInJSON)}
|
||||||
{editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />}
|
>
|
||||||
{editInJSON ? t('workflow.chatVariable.modal.editInForm') : t('workflow.chatVariable.modal.editInJSON')}
|
{editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />}
|
||||||
</Button>
|
{editInJSON ? t('workflow.chatVariable.modal.editInForm') : t('workflow.chatVariable.modal.editInJSON')}
|
||||||
)}
|
</Button>
|
||||||
</div>
|
)}
|
||||||
<div className='flex'>
|
</div>
|
||||||
{type === ChatVarType.String && (
|
<div className='flex'>
|
||||||
// Input will remove \n\r, so use Textarea just like description area
|
{type === ChatVarType.String && (
|
||||||
<textarea
|
// Input will remove \n\r, so use Textarea just like description area
|
||||||
className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'
|
<textarea
|
||||||
value={value}
|
className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'
|
||||||
placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''}
|
value={value}
|
||||||
onChange={e => setValue(e.target.value)}
|
placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''}
|
||||||
/>
|
onChange={e => setValue(e.target.value)}
|
||||||
)}
|
|
||||||
{type === ChatVarType.Number && (
|
|
||||||
<Input
|
|
||||||
placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''}
|
|
||||||
value={value}
|
|
||||||
onChange={e => setValue(Number(e.target.value))}
|
|
||||||
type='number'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{type === ChatVarType.Object && !editInJSON && (
|
|
||||||
<ObjectValueList
|
|
||||||
list={objectValue}
|
|
||||||
onChange={setObjectValue}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{type === ChatVarType.ArrayString && !editInJSON && (
|
|
||||||
<ArrayValueList
|
|
||||||
isString
|
|
||||||
list={value || [undefined]}
|
|
||||||
onChange={setValue}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{type === ChatVarType.ArrayNumber && !editInJSON && (
|
|
||||||
<ArrayValueList
|
|
||||||
isString={false}
|
|
||||||
list={value || [undefined]}
|
|
||||||
onChange={setValue}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{editInJSON && (
|
|
||||||
<div className='w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1' style={{ height: editorMinHeight }}>
|
|
||||||
<CodeEditor
|
|
||||||
isExpand
|
|
||||||
noWrapper
|
|
||||||
language={CodeLanguage.json}
|
|
||||||
value={editorContent}
|
|
||||||
placeholder={<div className='whitespace-pre'>{placeholder}</div>}
|
|
||||||
onChange={handleEditorValueChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
)}
|
{type === ChatVarType.Number && (
|
||||||
|
<Input
|
||||||
|
placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''}
|
||||||
|
value={value}
|
||||||
|
onChange={e => setValue(Number(e.target.value))}
|
||||||
|
type='number'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{type === ChatVarType.Object && !editInJSON && (
|
||||||
|
<ObjectValueList
|
||||||
|
list={objectValue}
|
||||||
|
onChange={setObjectValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{type === ChatVarType.ArrayString && !editInJSON && (
|
||||||
|
<ArrayValueList
|
||||||
|
isString
|
||||||
|
list={value || [undefined]}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{type === ChatVarType.ArrayNumber && !editInJSON && (
|
||||||
|
<ArrayValueList
|
||||||
|
isString={false}
|
||||||
|
list={value || [undefined]}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{editInJSON && (
|
||||||
|
<div className='w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1' style={{ height: editorMinHeight }}>
|
||||||
|
<CodeEditor
|
||||||
|
isExpand
|
||||||
|
noWrapper
|
||||||
|
language={CodeLanguage.json}
|
||||||
|
value={editorContent}
|
||||||
|
placeholder={<div className='whitespace-pre'>{placeholder}</div>}
|
||||||
|
onChange={handleEditorValueChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
{/* description */}
|
{/* description */}
|
||||||
<div className=''>
|
<div className=''>
|
||||||
<div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.chatVariable.modal.description')}</div>
|
<div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.chatVariable.modal.description')}</div>
|
||||||
|
@@ -5,4 +5,5 @@ export enum ChatVarType {
|
|||||||
ArrayString = 'array[string]',
|
ArrayString = 'array[string]',
|
||||||
ArrayNumber = 'array[number]',
|
ArrayNumber = 'array[number]',
|
||||||
ArrayObject = 'array[object]',
|
ArrayObject = 'array[object]',
|
||||||
|
ArrayFile = 'array[file]',
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user