FEAT: NEW WORKFLOW ENGINE (#3160)

Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Yeuoly <admin@srmxy.cn>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
Co-authored-by: jyong <jyong@dify.ai>
Co-authored-by: nite-knite <nkCoding@gmail.com>
Co-authored-by: jyong <718720800@qq.com>
This commit is contained in:
takatost
2024-04-08 18:51:46 +08:00
committed by GitHub
parent 2fb9850af5
commit 7753ba2d37
1161 changed files with 103836 additions and 10327 deletions

View File

@@ -0,0 +1,41 @@
'use client'
import { useBoolean } from 'ahooks'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import AddButton from '@/app/components/base/button/add-button'
import SelectDataset from '@/app/components/app/configuration/dataset-config/select-dataset'
import type { DataSet } from '@/models/datasets'
type Props = {
selectedIds: string[]
onChange: (dataSets: DataSet[]) => void
}
const AddDataset: FC<Props> = ({
selectedIds,
onChange,
}) => {
const [isShowModal, {
setTrue: showModal,
setFalse: hideModal,
}] = useBoolean(false)
const handleSelect = useCallback((datasets: DataSet[]) => {
onChange(datasets)
hideModal()
}, [onChange, hideModal])
return (
<div>
<AddButton onClick={showModal} />
{isShowModal && (
<SelectDataset
isShow={isShowModal}
onClose={hideModal}
selectedIds={selectedIds}
onSelect={handleSelect}
/>
)}
</div>
)
}
export default React.memo(AddDataset)

View File

@@ -0,0 +1,85 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import { useBoolean } from 'ahooks'
import type { DataSet } from '@/models/datasets'
import { DataSourceType } from '@/models/datasets'
import { Settings01, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
import FileIcon from '@/app/components/base/file-icon'
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
import SettingsModal from '@/app/components/app/configuration/dataset-config/settings-modal'
import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
type Props = {
payload: DataSet
onRemove: () => void
onChange: (dataSet: DataSet) => void
readonly?: boolean
}
const DatasetItem: FC<Props> = ({
payload,
onRemove,
onChange,
readonly,
}) => {
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const [isShowSettingsModal, {
setTrue: showSettingsModal,
setFalse: hideSettingsModal,
}] = useBoolean(false)
const handleSave = useCallback((newDataset: DataSet) => {
onChange(newDataset)
hideSettingsModal()
}, [hideSettingsModal, onChange])
return (
<div className='flex items-center h-10 justify-between rounded-xl px-2 bg-white border border-gray-200 cursor-pointer group/dataset-item'>
<div className='w-0 grow flex items-center space-x-1.5'>
{
payload.data_source_type === DataSourceType.NOTION
? (
<div className='shrink-0 flex items-center justify-center w-6 h-6 rounded-md border-[0.5px] border-[#EAECF5]'>
<FileIcon type='notion' className='w-4 h-4' />
</div>
)
: <div className='shrink-0 flex items-center justify-center w-6 h-6 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]'>
<Folder className='w-4 h-4 text-[#444CE7]' />
</div>
}
<div className='w-0 grow text-[13px] font-normal text-gray-800 truncate'>{payload.name}</div>
</div>
{!readonly && (
<div className='hidden group-hover/dataset-item:flex shrink-0 ml-2 items-center space-x-1'>
<div
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
onClick={showSettingsModal}
>
<Settings01 className='w-4 h-4 text-gray-500' />
</div>
<div
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
onClick={onRemove}
>
<Trash03 className='w-4 h-4 text-gray-500' />
</div>
</div>
)}
{isShowSettingsModal && (
<Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
<SettingsModal
currentDataset={payload}
onCancel={hideSettingsModal}
onSave={handleSave}
/>
</Drawer>
)}
</div>
)
}
export default React.memo(DatasetItem)

View File

@@ -0,0 +1,62 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import produce from 'immer'
import { useTranslation } from 'react-i18next'
import Item from './dataset-item'
import type { DataSet } from '@/models/datasets'
type Props = {
list: DataSet[]
onChange: (list: DataSet[]) => void
readonly?: boolean
}
const DatasetList: FC<Props> = ({
list,
onChange,
readonly,
}) => {
const { t } = useTranslation()
const handleRemove = useCallback((index: number) => {
return () => {
const newList = produce(list, (draft) => {
draft.splice(index, 1)
})
onChange(newList)
}
}, [list, onChange])
const handleChange = useCallback((index: number) => {
return (value: DataSet) => {
const newList = produce(list, (draft) => {
draft[index] = value
})
onChange(newList)
}
}, [list, onChange])
return (
<div className='space-y-1'>
{list.length
? list.map((item, index) => {
return (
<Item
key={index}
payload={item}
onRemove={handleRemove(index)}
onChange={handleChange(index)}
readonly={readonly}
/>
)
})
: (
<div className='p-3 text-xs text-center text-gray-500 rounded-lg cursor-default select-none bg-gray-50'>
{t('appDebug.datasetConfig.knowledgeTip')}
</div>
)
}
</div>
)
}
export default React.memo(DatasetList)

View File

@@ -0,0 +1,134 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types'
import type { ModelConfig } from '../../../types'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import ConfigRetrievalContent from '@/app/components/app/configuration/dataset-config/params-config/config-content'
import { RETRIEVE_TYPE } from '@/types/app'
import { DATASET_DEFAULT } from '@/config'
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type {
DatasetConfigs,
} from '@/models/debug'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
type Props = {
payload: {
retrieval_mode: RETRIEVE_TYPE
multiple_retrieval_config?: MultipleRetrievalConfig
single_retrieval_config?: SingleRetrievalConfig
}
onRetrievalModeChange: (mode: RETRIEVE_TYPE) => void
onMultipleRetrievalConfigChange: (config: MultipleRetrievalConfig) => void
singleRetrievalModelConfig?: ModelConfig
onSingleRetrievalModelChange?: (config: ModelConfig) => void
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
readonly?: boolean
}
const RetrievalConfig: FC<Props> = ({
payload,
onRetrievalModeChange,
onMultipleRetrievalConfigChange,
singleRetrievalModelConfig,
onSingleRetrievalModelChange,
onSingleRetrievalModelParamsChange,
readonly,
}) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const {
defaultModel: rerankDefaultModel,
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
const { multiple_retrieval_config } = payload
const handleChange = useCallback((configs: DatasetConfigs, isRetrievalModeChange?: boolean) => {
if (isRetrievalModeChange) {
onRetrievalModeChange(configs.retrieval_model)
return
}
onMultipleRetrievalConfigChange({
top_k: configs.top_k,
score_threshold: configs.score_threshold_enabled ? (configs.score_threshold || DATASET_DEFAULT.score_threshold) : null,
reranking_model: payload.retrieval_mode === RETRIEVE_TYPE.oneWay
? undefined
: (!configs.reranking_model?.reranking_provider_name
? {
provider: rerankDefaultModel?.provider?.provider || '',
model: rerankDefaultModel?.model || '',
}
: {
provider: configs.reranking_model?.reranking_provider_name,
model: configs.reranking_model?.reranking_model_name,
}),
})
}, [onMultipleRetrievalConfigChange, payload.retrieval_mode, rerankDefaultModel?.provider?.provider, rerankDefaultModel?.model, onRetrievalModeChange])
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
offset={{
// mainAxis: 12,
crossAxis: -2,
}}
>
<PortalToFollowElemTrigger
onClick={() => {
if (readonly)
return
setOpen(v => !v)
}}
>
<div className={cn(!readonly && 'cursor-pointer', open && 'bg-gray-100', 'flex items-center h-6 px-2 rounded-md hover:bg-gray-100 group select-none')}>
<div className={cn(open ? 'text-gray-700' : 'text-gray-500', 'leading-[18px] text-xs font-medium group-hover:bg-gray-100')}>{payload.retrieval_mode === RETRIEVE_TYPE.oneWay ? t('appDebug.datasetConfig.retrieveOneWay.title') : t('appDebug.datasetConfig.retrieveMultiWay.title')}</div>
{!readonly && <ChevronDown className='w-3 h-3 ml-1' />}
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 1001 }}>
<div className='w-[404px] pt-3 pb-4 px-4 shadow-xl rounded-2xl border border-gray-200 bg-white'>
<ConfigRetrievalContent
datasetConfigs={
{
retrieval_model: payload.retrieval_mode,
reranking_model: !multiple_retrieval_config?.reranking_model?.provider
? {
reranking_provider_name: rerankDefaultModel?.provider?.provider || '',
reranking_model_name: rerankDefaultModel?.model || '',
}
: {
reranking_provider_name: multiple_retrieval_config?.reranking_model?.provider || '',
reranking_model_name: multiple_retrieval_config?.reranking_model?.model || '',
},
top_k: multiple_retrieval_config?.top_k || DATASET_DEFAULT.top_k,
score_threshold_enabled: !(multiple_retrieval_config?.score_threshold === undefined || multiple_retrieval_config?.score_threshold === null),
score_threshold: multiple_retrieval_config?.score_threshold,
datasets: {
datasets: [],
},
}
}
onChange={handleChange}
isInWorkflow
singleRetrievalModelConfig={singleRetrievalModelConfig}
onSingleRetrievalModelChange={onSingleRetrievalModelChange}
onSingleRetrievalModelParamsChange={onSingleRetrievalModelParamsChange}
/>
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default React.memo(RetrievalConfig)