Merge remote-tracking branch 'origin/feat/rag-2' into feat/rag-2

This commit is contained in:
jyong 2025-08-28 15:39:36 +08:00
commit 334bc8f1a2
32 changed files with 422 additions and 190 deletions

View File

@ -0,0 +1,8 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0855 2.50411C10.5056 2.54689 10.8333 2.90198 10.8333 3.33337C10.8333 3.79361 10.4602 4.16671 10 4.16671H6.00017C5.51971 4.16671 5.20926 4.16776 4.97315 4.18705C4.74683 4.20555 4.66278 4.23686 4.62159 4.25785C4.46494 4.33771 4.33768 4.46498 4.25782 4.62162C4.23683 4.66282 4.20551 4.74687 4.18702 4.97319C4.16772 5.2093 4.16667 5.51975 4.16667 6.0002V13.9999C4.16667 14.4803 4.16772 14.7908 4.18702 15.0269C4.20551 15.2532 4.23684 15.3373 4.25782 15.3785C4.33771 15.5351 4.465 15.6624 4.62159 15.7422C4.66278 15.7632 4.74684 15.7945 4.97315 15.813C5.20926 15.8323 5.51971 15.8334 6.00017 15.8334H13.9998C14.4803 15.8334 14.7907 15.8323 15.0269 15.813C15.2532 15.7945 15.3372 15.7632 15.3784 15.7422C15.5351 15.6624 15.6624 15.5351 15.7422 15.3785C15.7632 15.3373 15.7945 15.2532 15.813 15.0269C15.8323 14.7908 15.8333 14.4803 15.8333 13.9999V10C15.8333 9.53981 16.2064 9.16671 16.6667 9.16671C17.1269 9.16671 17.5 9.53981 17.5 10V13.9999C17.5 14.4528 17.5009 14.8431 17.4748 15.1628C17.4478 15.4922 17.388 15.82 17.2274 16.1353C16.9878 16.6055 16.6054 16.9878 16.1353 17.2274C15.82 17.3881 15.4922 17.4479 15.1628 17.4748C14.843 17.5009 14.4528 17.5 13.9998 17.5H6.00017C5.54721 17.5 5.15697 17.5009 4.83724 17.4748C4.50786 17.4479 4.18 17.3881 3.86475 17.2274C3.39449 16.9878 3.01223 16.6054 2.77263 16.1353C2.61199 15.82 2.55216 15.4922 2.52523 15.1628C2.49911 14.8431 2.5 14.4528 2.5 13.9999V6.0002C2.5 5.54725 2.49911 5.15701 2.52523 4.83728C2.55216 4.5079 2.612 4.18003 2.77263 3.86479C3.01227 3.3946 3.39456 3.0123 3.86475 2.77266C4.18 2.61203 4.50786 2.55219 4.83724 2.52527C5.15697 2.49915 5.54721 2.50004 6.00017 2.50004H10L10.0855 2.50411Z" fill="#155AEF"/>
<path d="M10.8333 12.5C11.2936 12.5 11.6667 12.8731 11.6667 13.3334C11.6667 13.7936 11.2936 14.1667 10.8333 14.1667H6.66667C6.20643 14.1667 5.83334 13.7936 5.83334 13.3334C5.83334 12.8731 6.20643 12.5 6.66667 12.5H10.8333Z" fill="#155AEF"/>
<path d="M8.33334 9.16671C8.79357 9.16671 9.16667 9.5398 9.16667 10C9.16667 10.4603 8.79357 10.8334 8.33334 10.8334H6.66667C6.20643 10.8334 5.83334 10.4603 5.83334 10C5.83334 9.5398 6.20643 9.16671 6.66667 9.16671H8.33334Z" fill="#155AEF"/>
<path d="M13.3333 9.16671C13.7936 9.16671 14.1667 9.5398 14.1667 10C14.1667 10.4603 13.7936 10.8334 13.3333 10.8334H10.8333C10.3731 10.8334 10 10.4603 10 10C10 9.5398 10.3731 9.16671 10.8333 9.16671H13.3333Z" fill="#155AEF"/>
<path d="M10 5.83337C10.4602 5.83337 10.8333 6.20647 10.8333 6.66671C10.8333 7.12694 10.4602 7.50004 10 7.50004H6.66667C6.20643 7.50004 5.83334 7.12694 5.83334 6.66671C5.83334 6.20647 6.20643 5.83338 6.66667 5.83337H10Z" fill="#155AEF"/>
<path d="M15.8333 0.833374C16.2936 0.833374 16.6667 1.20647 16.6667 1.66671V3.33337H18.3333C18.7936 3.33337 19.1667 3.70647 19.1667 4.16671C19.1667 4.62694 18.7936 5.00004 18.3333 5.00004H16.6667V6.66671C16.6667 7.12694 16.2936 7.50004 15.8333 7.50004C15.3731 7.50004 15 7.12694 15 6.66671V5.00004H13.3333C12.8731 5.00004 12.5 4.62694 12.5 4.16671C12.5 3.70647 12.8731 3.33338 13.3333 3.33337H15V1.66671C15 1.20647 15.3731 0.833376 15.8333 0.833374Z" fill="#155AEF"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,71 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "20",
"height": "20",
"viewBox": "0 0 20 20",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10.0855 2.50411C10.5056 2.54689 10.8333 2.90198 10.8333 3.33337C10.8333 3.79361 10.4602 4.16671 10 4.16671H6.00017C5.51971 4.16671 5.20926 4.16776 4.97315 4.18705C4.74683 4.20555 4.66278 4.23686 4.62159 4.25785C4.46494 4.33771 4.33768 4.46498 4.25782 4.62162C4.23683 4.66282 4.20551 4.74687 4.18702 4.97319C4.16772 5.2093 4.16667 5.51975 4.16667 6.0002V13.9999C4.16667 14.4803 4.16772 14.7908 4.18702 15.0269C4.20551 15.2532 4.23684 15.3373 4.25782 15.3785C4.33771 15.5351 4.465 15.6624 4.62159 15.7422C4.66278 15.7632 4.74684 15.7945 4.97315 15.813C5.20926 15.8323 5.51971 15.8334 6.00017 15.8334H13.9998C14.4803 15.8334 14.7907 15.8323 15.0269 15.813C15.2532 15.7945 15.3372 15.7632 15.3784 15.7422C15.5351 15.6624 15.6624 15.5351 15.7422 15.3785C15.7632 15.3373 15.7945 15.2532 15.813 15.0269C15.8323 14.7908 15.8333 14.4803 15.8333 13.9999V10C15.8333 9.53981 16.2064 9.16671 16.6667 9.16671C17.1269 9.16671 17.5 9.53981 17.5 10V13.9999C17.5 14.4528 17.5009 14.8431 17.4748 15.1628C17.4478 15.4922 17.388 15.82 17.2274 16.1353C16.9878 16.6055 16.6054 16.9878 16.1353 17.2274C15.82 17.3881 15.4922 17.4479 15.1628 17.4748C14.843 17.5009 14.4528 17.5 13.9998 17.5H6.00017C5.54721 17.5 5.15697 17.5009 4.83724 17.4748C4.50786 17.4479 4.18 17.3881 3.86475 17.2274C3.39449 16.9878 3.01223 16.6054 2.77263 16.1353C2.61199 15.82 2.55216 15.4922 2.52523 15.1628C2.49911 14.8431 2.5 14.4528 2.5 13.9999V6.0002C2.5 5.54725 2.49911 5.15701 2.52523 4.83728C2.55216 4.5079 2.612 4.18003 2.77263 3.86479C3.01227 3.3946 3.39456 3.0123 3.86475 2.77266C4.18 2.61203 4.50786 2.55219 4.83724 2.52527C5.15697 2.49915 5.54721 2.50004 6.00017 2.50004H10L10.0855 2.50411Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10.8333 12.5C11.2936 12.5 11.6667 12.8731 11.6667 13.3334C11.6667 13.7936 11.2936 14.1667 10.8333 14.1667H6.66667C6.20643 14.1667 5.83334 13.7936 5.83334 13.3334C5.83334 12.8731 6.20643 12.5 6.66667 12.5H10.8333Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M8.33334 9.16671C8.79357 9.16671 9.16667 9.5398 9.16667 10C9.16667 10.4603 8.79357 10.8334 8.33334 10.8334H6.66667C6.20643 10.8334 5.83334 10.4603 5.83334 10C5.83334 9.5398 6.20643 9.16671 6.66667 9.16671H8.33334Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M13.3333 9.16671C13.7936 9.16671 14.1667 9.5398 14.1667 10C14.1667 10.4603 13.7936 10.8334 13.3333 10.8334H10.8333C10.3731 10.8334 10 10.4603 10 10C10 9.5398 10.3731 9.16671 10.8333 9.16671H13.3333Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10 5.83337C10.4602 5.83337 10.8333 6.20647 10.8333 6.66671C10.8333 7.12694 10.4602 7.50004 10 7.50004H6.66667C6.20643 7.50004 5.83334 7.12694 5.83334 6.66671C5.83334 6.20647 6.20643 5.83338 6.66667 5.83337H10Z",
"fill": "currentColor"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M15.8333 0.833374C16.2936 0.833374 16.6667 1.20647 16.6667 1.66671V3.33337H18.3333C18.7936 3.33337 19.1667 3.70647 19.1667 4.16671C19.1667 4.62694 18.7936 5.00004 18.3333 5.00004H16.6667V6.66671C16.6667 7.12694 16.2936 7.50004 15.8333 7.50004C15.3731 7.50004 15 7.12694 15 6.66671V5.00004H13.3333C12.8731 5.00004 12.5 4.62694 12.5 4.16671C12.5 3.70647 12.8731 3.33338 13.3333 3.33337H15V1.66671C15 1.20647 15.3731 0.833376 15.8333 0.833374Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "AddChunks"
}

View File

@ -0,0 +1,20 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './AddChunks.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconData } from '@/app/components/base/icons/IconBase'
const Icon = (
{
ref,
...props
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>;
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
Icon.displayName = 'AddChunks'
export default Icon

View File

@ -1,3 +1,4 @@
export { default as AddChunks } from './AddChunks'
export { default as ArrowShape } from './ArrowShape'
export { default as Chunk } from './Chunk'
export { default as Collapse } from './Collapse'

View File

@ -32,7 +32,6 @@ const DataSourceOptions = ({
useEffect(() => {
if (options.length > 0 && !datasourceNodeId)
handelSelect(options[0].value)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (

View File

@ -32,7 +32,7 @@ const LocalFile = ({
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { locale } = useContext(I18n)
const fileList = useDataSourceStoreWithSelector(state => state.localFileList)
const localFileList = useDataSourceStoreWithSelector(state => state.localFileList)
const dataSourceStore = useDataSourceStore()
const [dragging, setDragging] = useState(false)
@ -41,7 +41,7 @@ const LocalFile = ({
const fileUploader = useRef<HTMLInputElement>(null)
const fileListRef = useRef<FileItem[]>([])
const hideUpload = notSupportBatchUpload && fileList.length > 0
const hideUpload = notSupportBatchUpload && localFileList.length > 0
const { data: fileUploadConfigResponse } = useFileUploadConfig()
const supportTypesShowNames = useMemo(() => {
@ -179,7 +179,7 @@ const LocalFile = ({
if (!files.length)
return false
if (files.length + fileList.length > FILES_NUMBER_LIMIT && !IS_CE_EDITION) {
if (files.length + localFileList.length > FILES_NUMBER_LIMIT && !IS_CE_EDITION) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.filesNumber', { filesNumber: FILES_NUMBER_LIMIT }) })
return false
}
@ -193,7 +193,7 @@ const LocalFile = ({
updateFileList(newFiles)
fileListRef.current = newFiles
uploadMultipleFiles(preparedFiles)
}, [updateFileList, uploadMultipleFiles, notify, t, fileList])
}, [updateFileList, uploadMultipleFiles, notify, t, localFileList])
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
@ -297,9 +297,9 @@ const LocalFile = ({
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
</div>
)}
{fileList.length > 0 && (
{localFileList.length > 0 && (
<div className='mt-1 flex flex-col gap-y-1'>
{fileList.map((fileItem, index) => {
{localFileList.map((fileItem, index) => {
const isUploading = fileItem.progress >= 0 && fileItem.progress < 100
const isError = fileItem.progress === -2
return (

View File

@ -70,7 +70,6 @@ const OnlineDocuments = ({
const getOnlineDocuments = useCallback(async () => {
const { currentCredentialId } = dataSourceStore.getState()
if (!currentCredentialId) return
ssePost(
datasourceNodeRunURL,
{
@ -96,6 +95,7 @@ const OnlineDocuments = ({
}, [dataSourceStore, datasourceNodeRunURL])
useEffect(() => {
if (!currentCredentialId) return
getOnlineDocuments()
}, [currentCredentialId])

View File

@ -3,12 +3,12 @@ import cn from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
type DriveProps = {
prefix: string[]
breadcrumbs: string[]
handleBackToRoot: () => void
}
const Drive = ({
prefix,
breadcrumbs,
handleBackToRoot,
}: DriveProps) => {
const { t } = useTranslation()
@ -19,15 +19,15 @@ const Drive = ({
type='button'
className={cn(
'max-w-full shrink truncate rounded-md px-[5px] py-1',
prefix.length > 0 && 'system-sm-regular text-text-tertiary hover:bg-state-base-hover',
prefix.length === 0 && 'system-sm-medium text-text-secondary',
breadcrumbs.length > 0 && 'system-sm-regular text-text-tertiary hover:bg-state-base-hover',
breadcrumbs.length === 0 && 'system-sm-medium text-text-secondary',
)}
onClick={handleBackToRoot}
disabled={prefix.length === 0}
disabled={breadcrumbs.length === 0}
>
{t('datasetPipeline.onlineDrive.breadcrumbs.allFiles')}
</button>
{prefix.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
{breadcrumbs.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
</>
)
}

View File

@ -17,6 +17,7 @@ const Menu = ({
{breadcrumbs.map((breadcrumb, index) => {
return (
<Item
key={`${breadcrumb}-${index}`}
name={breadcrumb}
index={startIndex + index}
onBreadcrumbClick={onBreadcrumbClick}

View File

@ -7,7 +7,7 @@ import Dropdown from './dropdown'
import Drive from './drive'
type BreadcrumbsProps = {
prefix: string[]
breadcrumbs: string[]
keywords: string
bucket: string
searchResultsLength: number
@ -15,7 +15,7 @@ type BreadcrumbsProps = {
}
const Breadcrumbs = ({
prefix,
breadcrumbs,
keywords,
bucket,
searchResultsLength,
@ -25,52 +25,57 @@ const Breadcrumbs = ({
const dataSourceStore = useDataSourceStore()
const hasBucket = useDataSourceStoreWithSelector(s => s.hasBucket)
const showSearchResult = !!keywords && searchResultsLength > 0
const showBucketListTitle = prefix.length === 0 && hasBucket && bucket === ''
const showBucketListTitle = breadcrumbs.length === 0 && hasBucket && bucket === ''
const displayBreadcrumbNum = useMemo(() => {
const num = isInPipeline ? 2 : 3
return bucket ? num - 1 : num
}, [isInPipeline, bucket])
const breadcrumbs = useMemo(() => {
const prefixToDisplay = prefix.slice(0, displayBreadcrumbNum - 1)
const collapsedBreadcrumbs = prefix.slice(displayBreadcrumbNum - 1, prefix.length - 1)
const breadcrumbsConfig = useMemo(() => {
const prefixToDisplay = breadcrumbs.slice(0, displayBreadcrumbNum - 1)
const collapsedBreadcrumbs = breadcrumbs.slice(displayBreadcrumbNum - 1, breadcrumbs.length - 1)
return {
original: prefix,
needCollapsed: prefix.length > displayBreadcrumbNum,
original: breadcrumbs,
needCollapsed: breadcrumbs.length > displayBreadcrumbNum,
prefixBreadcrumbs: prefixToDisplay,
collapsedBreadcrumbs,
lastBreadcrumb: prefix[prefix.length - 1],
lastBreadcrumb: breadcrumbs[breadcrumbs.length - 1],
}
}, [displayBreadcrumbNum, prefix])
}, [displayBreadcrumbNum, breadcrumbs])
const handleBackToBucketList = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix, setBucket } = dataSourceStore.getState()
setFileList([])
const { setOnlineDriveFileList, setSelectedFileIds, setBreadcrumbs, setPrefix, setBucket } = dataSourceStore.getState()
setOnlineDriveFileList([])
setSelectedFileIds([])
setBucket('')
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleClickBucketName = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
setFileList([])
const { setOnlineDriveFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
setOnlineDriveFileList([])
setSelectedFileIds([])
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleBackToRoot = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
setFileList([])
const { setOnlineDriveFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
setOnlineDriveFileList([])
setSelectedFileIds([])
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleClickBreadcrumb = useCallback((index: number) => {
const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
const { breadcrumbs, prefix, setOnlineDriveFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
const newBreadcrumbs = breadcrumbs.slice(0, index + 1)
const newPrefix = prefix.slice(0, index + 1)
setFileList([])
setOnlineDriveFileList([])
setSelectedFileIds([])
setBreadcrumbs(newBreadcrumbs)
setPrefix(newPrefix)
}, [dataSourceStore])
@ -80,7 +85,7 @@ const Breadcrumbs = ({
<div className='system-sm-medium text-test-secondary px-[5px]'>
{t('datasetPipeline.onlineDrive.breadcrumbs.searchResult', {
searchResultsLength,
folderName: prefix.length > 0 ? prefix[prefix.length - 1] : bucket,
folderName: breadcrumbs.length > 0 ? breadcrumbs[breadcrumbs.length - 1] : bucket,
})}
</div>
)}
@ -96,21 +101,21 @@ const Breadcrumbs = ({
bucketName={bucket}
handleBackToBucketList={handleBackToBucketList}
handleClickBucketName={handleClickBucketName}
isActive={prefix.length === 0}
disabled={prefix.length === 0}
showSeparator={prefix.length > 0}
isActive={breadcrumbs.length === 0}
disabled={breadcrumbs.length === 0}
showSeparator={breadcrumbs.length > 0}
/>
)}
{!hasBucket && (
<Drive
prefix={prefix}
breadcrumbs={breadcrumbs}
handleBackToRoot={handleBackToRoot}
/>
)}
{!breadcrumbs.needCollapsed && (
{!breadcrumbsConfig.needCollapsed && (
<>
{breadcrumbs.original.map((breadcrumb, index) => {
const isLast = index === breadcrumbs.original.length - 1
{breadcrumbsConfig.original.map((breadcrumb, index) => {
const isLast = index === breadcrumbsConfig.original.length - 1
return (
<BreadcrumbItem
key={`${breadcrumb}-${index}`}
@ -125,9 +130,9 @@ const Breadcrumbs = ({
})}
</>
)}
{breadcrumbs.needCollapsed && (
{breadcrumbsConfig.needCollapsed && (
<>
{breadcrumbs.prefixBreadcrumbs.map((breadcrumb, index) => {
{breadcrumbsConfig.prefixBreadcrumbs.map((breadcrumb, index) => {
return (
<BreadcrumbItem
key={`${breadcrumb}-${index}`}
@ -138,14 +143,14 @@ const Breadcrumbs = ({
)
})}
<Dropdown
startIndex={breadcrumbs.prefixBreadcrumbs.length}
breadcrumbs={breadcrumbs.collapsedBreadcrumbs}
startIndex={breadcrumbsConfig.prefixBreadcrumbs.length}
breadcrumbs={breadcrumbsConfig.collapsedBreadcrumbs}
onBreadcrumbClick={handleClickBreadcrumb}
/>
<BreadcrumbItem
index={prefix.length - 1}
index={breadcrumbs.length - 1}
handleClick={handleClickBreadcrumb}
name={breadcrumbs.lastBreadcrumb}
name={breadcrumbsConfig.lastBreadcrumb}
isActive={true}
disabled={true}
showSeparator={false}

View File

@ -4,7 +4,7 @@ import Input from '@/app/components/base/input'
import { useTranslation } from 'react-i18next'
type HeaderProps = {
prefix: string[]
breadcrumbs: string[]
inputValue: string
keywords: string
bucket: string
@ -15,7 +15,7 @@ type HeaderProps = {
}
const Header = ({
prefix,
breadcrumbs,
inputValue,
keywords,
bucket,
@ -29,7 +29,7 @@ const Header = ({
return (
<div className='flex items-center gap-x-2 bg-components-panel-bg p-1 pl-3'>
<Breadcrumbs
prefix={prefix}
breadcrumbs={breadcrumbs}
keywords={keywords}
bucket={bucket}
searchResultsLength={searchResultsLength}

View File

@ -7,7 +7,7 @@ import { useDebounceFn } from 'ahooks'
type FileListProps = {
fileList: OnlineDriveFile[]
selectedFileIds: string[]
prefix: string[]
breadcrumbs: string[]
keywords: string
bucket: string
isInPipeline: boolean
@ -22,7 +22,7 @@ type FileListProps = {
const FileList = ({
fileList,
selectedFileIds,
prefix,
breadcrumbs,
keywords,
bucket,
resetKeywords,
@ -56,7 +56,7 @@ const FileList = ({
return (
<div className='flex h-[400px] flex-col overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg shadow-xs shadow-shadow-shadow-3'>
<Header
prefix={prefix}
breadcrumbs={breadcrumbs}
inputValue={inputValue}
keywords={keywords}
bucket={bucket}

View File

@ -44,7 +44,7 @@ const List = ({
observerRef.current.observe(anchorRef.current)
}
return () => observerRef.current?.disconnect()
}, [anchorRef])
}, [anchorRef, isLoading])
const isAllLoading = isLoading && fileList.length === 0 && keywords.length === 0
const isPartialLoading = isLoading && fileList.length > 0

View File

@ -1,6 +1,6 @@
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
import Header from '../base/header'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import FileList from './file-list'
import type { OnlineDriveFile } from '@/models/pipeline'
import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline'
@ -33,23 +33,26 @@ const OnlineDrive = ({
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
const {
nextPageParameters,
breadcrumbs,
prefix,
keywords,
bucket,
selectedFileIds,
fileList,
onlineDriveFileList,
currentCredentialId,
} = useDataSourceStoreWithSelector(useShallow(state => ({
nextPageParameters: state.nextPageParameters,
breadcrumbs: state.breadcrumbs,
prefix: state.prefix,
keywords: state.keywords,
bucket: state.bucket,
selectedFileIds: state.selectedFileIds,
fileList: state.fileList,
onlineDriveFileList: state.onlineDriveFileList,
currentCredentialId: state.currentCredentialId,
})))
const dataSourceStore = useDataSourceStore()
const [isLoading, setIsLoading] = useState(false)
const isLoadingRef = useRef(false)
const { data: dataSourceAuth } = useGetDataSourceAuth({
pluginId: nodeData.plugin_id,
@ -61,18 +64,19 @@ const OnlineDrive = ({
: `/rag/pipelines/${pipelineId}/workflows/draft/datasource/nodes/${nodeId}/run`
const getOnlineDriveFiles = useCallback(async () => {
const { nextPageParameters, prefix, bucket, fileList, currentCredentialId } = dataSourceStore.getState()
const prefixString = prefix.length > 0 ? `${prefix.join('/')}/` : ''
if (isLoadingRef.current) return
const { nextPageParameters, prefix, bucket, onlineDriveFileList, currentCredentialId } = dataSourceStore.getState()
setIsLoading(true)
isLoadingRef.current = true
ssePost(
datasourceNodeRunURL,
{
body: {
inputs: {
prefix: prefixString,
prefix: prefix[prefix.length - 1],
bucket,
next_page_parameters: nextPageParameters,
max_keys: 30, // Adjust as needed
max_keys: 30,
},
datasource_type: DatasourceType.onlineDrive,
credential_id: currentCredentialId,
@ -80,18 +84,19 @@ const OnlineDrive = ({
},
{
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
const { setFileList, isTruncated, currentNextPageParametersRef, setHasBucket } = dataSourceStore.getState()
const { setOnlineDriveFileList, isTruncated, currentNextPageParametersRef, setHasBucket } = dataSourceStore.getState()
const {
fileList: newFileList,
isTruncated: newIsTruncated,
nextPageParameters: newNextPageParameters,
hasBucket: newHasBucket,
} = convertOnlineDriveData(documentsData.data, prefix, bucket)
setFileList([...fileList, ...newFileList])
} = convertOnlineDriveData(documentsData.data, breadcrumbs, bucket)
setOnlineDriveFileList([...onlineDriveFileList, ...newFileList])
isTruncated.current = newIsTruncated
currentNextPageParametersRef.current = newNextPageParameters
setHasBucket(newHasBucket)
setIsLoading(false)
isLoadingRef.current = false
},
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
Toast.notify({
@ -99,15 +104,17 @@ const OnlineDrive = ({
message: error.error,
})
setIsLoading(false)
isLoadingRef.current = false
},
},
)
}, [datasourceNodeRunURL, dataSourceStore])
useEffect(() => {
if (!currentCredentialId) return
if (isInitialMount) {
// Only fetch files on initial mount if fileList is empty
if (fileList.length === 0)
if (onlineDriveFileList.length === 0)
getOnlineDriveFiles()
setIsInitialMount(false)
}
@ -116,11 +123,11 @@ const OnlineDrive = ({
}
}, [nextPageParameters, prefix, bucket, currentCredentialId])
const onlineDriveFileList = useMemo(() => {
const filteredOnlineDriveFileList = useMemo(() => {
if (keywords)
return fileList.filter(file => file.name.toLowerCase().includes(keywords.toLowerCase()))
return fileList
}, [fileList, keywords])
return onlineDriveFileList.filter(file => file.name.toLowerCase().includes(keywords.toLowerCase()))
return onlineDriveFileList
}, [onlineDriveFileList, keywords])
const updateKeywords = useCallback((keywords: string) => {
const { setKeywords } = dataSourceStore.getState()
@ -150,17 +157,21 @@ const OnlineDrive = ({
}, [dataSourceStore, isInPipeline])
const handleOpenFolder = useCallback((file: OnlineDriveFile) => {
const { prefix, setPrefix, setBucket, setFileList, setSelectedFileIds } = dataSourceStore.getState()
const { breadcrumbs, prefix, setBreadcrumbs, setPrefix, setBucket, setOnlineDriveFileList, setSelectedFileIds } = dataSourceStore.getState()
if (file.type === OnlineDriveFileType.file) return
setFileList([])
setOnlineDriveFileList([])
if (file.type === OnlineDriveFileType.bucket) {
setBucket(file.name)
}
else {
setSelectedFileIds([])
const newPrefix = produce(prefix, (draft) => {
const newBreadcrumbs = produce(breadcrumbs, (draft) => {
draft.push(file.name)
})
const newPrefix = produce(prefix, (draft) => {
draft.push(file.id)
})
setBreadcrumbs(newBreadcrumbs)
setPrefix(newPrefix)
}
}, [dataSourceStore, getOnlineDriveFiles])
@ -183,14 +194,14 @@ const OnlineDrive = ({
credentials={dataSourceAuth?.result || []}
/>
<FileList
fileList={onlineDriveFileList}
fileList={filteredOnlineDriveFileList}
selectedFileIds={selectedFileIds}
prefix={prefix}
breadcrumbs={breadcrumbs}
keywords={keywords}
bucket={bucket}
resetKeywords={resetKeywords}
updateKeywords={updateKeywords}
searchResultsLength={onlineDriveFileList.length}
searchResultsLength={filteredOnlineDriveFileList.length}
handleSelectFile={handleSelectFile}
handleOpenFolder={handleOpenFolder}
isInPipeline={isInPipeline}

View File

@ -2,14 +2,16 @@ import type { StateCreator } from 'zustand'
import type { OnlineDriveFile } from '@/models/pipeline'
export type OnlineDriveSliceShape = {
breadcrumbs: string[]
setBreadcrumbs: (breadcrumbs: string[]) => void
prefix: string[]
setPrefix: (prefix: string[]) => void
keywords: string
setKeywords: (keywords: string) => void
selectedFileIds: string[]
setSelectedFileIds: (selectedFileIds: string[]) => void
fileList: OnlineDriveFile[]
setFileList: (fileList: OnlineDriveFile[]) => void
onlineDriveFileList: OnlineDriveFile[]
setOnlineDriveFileList: (onlineDriveFileList: OnlineDriveFile[]) => void
bucket: string
setBucket: (bucket: string) => void
nextPageParameters: Record<string, any>
@ -23,6 +25,10 @@ export type OnlineDriveSliceShape = {
export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set, get) => {
return ({
breadcrumbs: [],
setBreadcrumbs: (breadcrumbs: string[]) => set(() => ({
breadcrumbs,
})),
prefix: [],
setPrefix: (prefix: string[]) => set(() => ({
prefix,
@ -37,12 +43,12 @@ export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set,
selectedFileIds,
}))
const id = selectedFileIds[0]
const { fileList, previewOnlineDriveFileRef } = get()
previewOnlineDriveFileRef.current = fileList.find(file => file.id === id)
const { onlineDriveFileList, previewOnlineDriveFileRef } = get()
previewOnlineDriveFileRef.current = onlineDriveFileList.find(file => file.id === id)
},
fileList: [],
setFileList: (fileList: OnlineDriveFile[]) => set(() => ({
fileList,
onlineDriveFileList: [],
setOnlineDriveFileList: (onlineDriveFileList: OnlineDriveFile[]) => set(() => ({
onlineDriveFileList,
})),
bucket: '',
setBucket: (bucket: string) => set(() => ({

View File

@ -65,7 +65,7 @@ export const useDatasourceOptions = (pipelineNodes: Node<DataSourceNodeType>[])
export const useLocalFile = () => {
const {
localFileList: fileList,
localFileList,
currentLocalFile,
} = useDataSourceStoreWithSelector(useShallow(state => ({
localFileList: state.localFileList,
@ -73,7 +73,7 @@ export const useLocalFile = () => {
})))
const dataSourceStore = useDataSourceStore()
const allFileLoaded = useMemo(() => (fileList.length > 0 && fileList.every(file => file.file.id)), [fileList])
const allFileLoaded = useMemo(() => (localFileList.length > 0 && localFileList.every(file => file.file.id)), [localFileList])
const hidePreviewLocalFile = useCallback(() => {
const { setCurrentLocalFile } = dataSourceStore.getState()
@ -81,7 +81,7 @@ export const useLocalFile = () => {
}, [dataSourceStore])
return {
fileList,
localFileList,
allFileLoaded,
currentLocalFile,
hidePreviewLocalFile,
@ -187,27 +187,27 @@ export const useWebsiteCrawl = () => {
export const useOnlineDrive = () => {
const {
fileList,
onlineDriveFileList,
selectedFileIds,
} = useDataSourceStoreWithSelector(useShallow(state => ({
fileList: state.fileList,
onlineDriveFileList: state.onlineDriveFileList,
selectedFileIds: state.selectedFileIds,
})))
const dataSourceStore = useDataSourceStore()
const selectedOnlineDriveFileList = useMemo(() => {
return selectedFileIds.map(key => fileList.find(item => item.id === key)!)
}, [fileList, selectedFileIds])
return selectedFileIds.map(key => onlineDriveFileList.find(item => item.id === key)!)
}, [onlineDriveFileList, selectedFileIds])
const clearOnlineDriveData = useCallback(() => {
const {
setFileList,
setOnlineDriveFileList,
setBucket,
setPrefix,
setKeywords,
setSelectedFileIds,
} = dataSourceStore.getState()
setFileList([])
setOnlineDriveFileList([])
setBucket('')
setPrefix([])
setKeywords('')
@ -215,7 +215,7 @@ export const useOnlineDrive = () => {
}, [dataSourceStore])
return {
fileList,
onlineDriveFileList,
selectedFileIds,
selectedOnlineDriveFileList,
clearOnlineDriveData,

View File

@ -61,7 +61,7 @@ const CreateFormPipeline = () => {
handleBackStep,
} = useAddDocumentsSteps()
const {
fileList,
localFileList,
allFileLoaded,
currentLocalFile,
hidePreviewLocalFile,
@ -81,7 +81,7 @@ const CreateFormPipeline = () => {
clearWebsiteCrawlData,
} = useWebsiteCrawl()
const {
fileList: onlineDriveFileList,
onlineDriveFileList,
selectedFileIds,
selectedOnlineDriveFileList,
clearOnlineDriveData,
@ -107,7 +107,7 @@ const CreateFormPipeline = () => {
const nextBtnDisabled = useMemo(() => {
if (!datasource) return true
if (datasourceType === DatasourceType.localFile)
return isShowVectorSpaceFull || !fileList.length || !allFileLoaded
return isShowVectorSpaceFull || !localFileList.length || !allFileLoaded
if (datasourceType === DatasourceType.onlineDocument)
return isShowVectorSpaceFull || !onlineDocuments.length
if (datasourceType === DatasourceType.websiteCrawl)
@ -115,7 +115,7 @@ const CreateFormPipeline = () => {
if (datasourceType === DatasourceType.onlineDrive)
return isShowVectorSpaceFull || !selectedFileIds.length
return false
}, [datasource, datasourceType, isShowVectorSpaceFull, fileList.length, allFileLoaded, onlineDocuments.length, websitePages.length, selectedFileIds.length])
}, [datasource, datasourceType, isShowVectorSpaceFull, localFileList.length, allFileLoaded, onlineDocuments.length, websitePages.length, selectedFileIds.length])
const fileUploadConfig = useMemo(() => fileUploadConfigResponse ?? {
file_size_limit: 15,
@ -285,7 +285,7 @@ const CreateFormPipeline = () => {
const {
bucket,
selectedFileIds,
fileList: onlineDriveFileList,
onlineDriveFileList,
} = dataSourceStore.getState()
selectedFileIds.forEach((id) => {
const file = onlineDriveFileList.find(file => file.id === id)
@ -354,7 +354,7 @@ const CreateFormPipeline = () => {
const handleSelectAll = useCallback(() => {
const {
onlineDocuments,
fileList: onlineDriveFileList,
onlineDriveFileList,
selectedFileIds,
setOnlineDocuments,
setSelectedFileIds,
@ -535,7 +535,7 @@ const CreateFormPipeline = () => {
<div className='flex h-full flex-col pl-2 pt-2'>
<ChunkPreview
dataSourceType={datasourceType as DatasourceType}
localFiles={fileList.map(file => file.file)}
localFiles={localFileList.map(file => file.file)}
onlineDocuments={onlineDocuments}
websitePages={websitePages}
onlineDriveFiles={selectedOnlineDriveFileList}

View File

@ -22,13 +22,13 @@ const Line = React.memo(({
className,
}: LineProps) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="2" height="241" viewBox="0 0 2 241" fill="none" className={className}>
<path d="M1 0.5L1 240.5" stroke="url(#paint0_linear_1989_74474)"/>
<svg xmlns='http://www.w3.org/2000/svg' width='2' height='241' viewBox='0 0 2 241' fill='none' className={className}>
<path d='M1 0.5L1 240.5' stroke='url(#paint0_linear_1989_74474)' />
<defs>
<linearGradient id="paint0_linear_1989_74474" x1="-7.99584" y1="240.5" x2="-7.88094" y2="0.50004" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.01"/>
<stop offset="0.503965" stopColor="#101828" stopOpacity="0.08"/>
<stop offset="1" stopColor="white" stopOpacity="0.01"/>
<linearGradient id='paint0_linear_1989_74474' x1='-7.99584' y1='240.5' x2='-7.88094' y2='0.50004' gradientUnits='userSpaceOnUse'>
<stop stopColor='white' stopOpacity='0.01' />
<stop offset='0.503965' stopColor='#101828' stopOpacity='0.08' />
<stop offset='1' stopColor='white' stopOpacity='0.01' />
</linearGradient>
</defs>
</svg>
@ -47,8 +47,8 @@ const Empty: FC<IEmptyProps> = ({
<div className='flex flex-col items-center'>
<div className='relative z-10 flex h-14 w-14 items-center justify-center rounded-xl border border-divider-subtle bg-components-card-bg shadow-lg shadow-shadow-shadow-5'>
<RiFileList2Line className='h-6 w-6 text-text-secondary' />
<Line className='absolute -right-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute -left-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute -right-px top-1/2 -translate-y-1/2' />
<Line className='absolute -left-px top-1/2 -translate-y-1/2' />
<Line className='absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 rotate-90' />
<Line className='absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-90' />
</div>

View File

@ -110,13 +110,13 @@ export const useOnlineDrive = () => {
const clearOnlineDriveData = useCallback(() => {
const {
setFileList,
setOnlineDriveFileList,
setBucket,
setPrefix,
setKeywords,
setSelectedFileIds,
} = dataSourceStore.getState()
setFileList([])
setOnlineDriveFileList([])
setBucket('')
setPrefix([])
setKeywords('')

View File

@ -104,8 +104,8 @@ const Preparation = () => {
})
}
if (datasourceType === DatasourceType.onlineDrive) {
const { bucket, fileList, selectedFileIds } = dataSourceStore.getState()
const file = fileList.find(file => file.id === selectedFileIds[0])
const { bucket, onlineDriveFileList, selectedFileIds } = dataSourceStore.getState()
const file = onlineDriveFileList.find(file => file.id === selectedFileIds[0])
datasourceInfoList.push({
bucket,
id: file?.id,

View File

@ -287,10 +287,9 @@ export const usePipelineRun = () => {
)
const handleStopRun = useCallback((taskId: string) => {
const { pipelineId, setShowDebugAndPreviewPanel } = workflowStore.getState()
const { pipelineId } = workflowStore.getState()
stopWorkflowRun(`/rag/pipeline/${pipelineId}/workflow-runs/tasks/${taskId}/stop`)
setShowDebugAndPreviewPanel(false)
}, [workflowStore])
const handleRestoreFromPublishedWorkflow = useCallback((publishedWorkflow: VersionHistory) => {

View File

@ -10,6 +10,7 @@ import type {
} from '@/app/components/workflow/types'
import { useIsChatMode } from './use-workflow'
import { useStoreApi } from 'reactflow'
import { useStore } from '@/app/components/workflow/store'
import type { Type } from '../nodes/llm/types'
import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type'
@ -18,6 +19,11 @@ export const useWorkflowVariables = () => {
const workflowStore = useWorkflowStore()
const { getMatchedSchemaType } = useMatchSchemaType()
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const mcpTools = useStore(s => s.mcpTools)
const dataSourceList = useStore(s => s.dataSourceList)
const getNodeAvailableVars = useCallback(({
parentNode,
beforeNodes,
@ -37,11 +43,6 @@ export const useWorkflowVariables = () => {
conversationVariables,
environmentVariables,
ragPipelineVariables,
buildInTools,
customTools,
workflowTools,
mcpTools,
dataSourceList,
} = workflowStore.getState()
return toNodeAvailableVars({
parentNode,
@ -61,7 +62,7 @@ export const useWorkflowVariables = () => {
},
getMatchedSchemaType,
})
}, [t, workflowStore, getMatchedSchemaType])
}, [t, workflowStore, getMatchedSchemaType, buildInTools])
const getCurrentVariableType = useCallback(({
parentNode,
@ -71,6 +72,7 @@ export const useWorkflowVariables = () => {
availableNodes,
isChatMode,
isConstant,
preferSchemaType,
}: {
valueSelector: ValueSelector
parentNode?: Node | null
@ -79,6 +81,7 @@ export const useWorkflowVariables = () => {
availableNodes: any[]
isChatMode: boolean
isConstant?: boolean
preferSchemaType?: boolean
}) => {
const {
conversationVariables,
@ -109,8 +112,9 @@ export const useWorkflowVariables = () => {
dataSourceList: dataSourceList ?? [],
},
getMatchedSchemaType,
preferSchemaType,
})
}, [workflowStore])
}, [workflowStore, getVarType, getMatchedSchemaType])
return {
getNodeAvailableVars,

View File

@ -27,6 +27,7 @@ export const AddVariablePopup = ({
hideSearch
vars={availableVars}
onChange={onSelect}
isSupportFileVar
/>
</div>
</div>

View File

@ -825,6 +825,7 @@ export const getVarType = ({
ragVariables = [],
allPluginInfoList,
getMatchedSchemaType,
preferSchemaType,
}: {
valueSelector: ValueSelector
parentNode?: Node | null
@ -838,6 +839,7 @@ export const getVarType = ({
ragVariables?: RAGPipelineVariable[]
allPluginInfoList: Record<string, ToolWithProvider[]>
getMatchedSchemaType: (obj: any) => string
preferSchemaType?: boolean
}): VarType => {
if (isConstant)
return VarType.string
@ -934,7 +936,7 @@ export const getVarType = ({
const isStructuredOutputVar = !!targetVar.children?.schema?.properties
if (isStructuredOutputVar) {
if (valueSelector.length === 2) { // root
return targetVar.alias || VarType.object
return (preferSchemaType && targetVar.schemaType) ? targetVar.schemaType : VarType.object
}
let currProperties = targetVar.children.schema;
(valueSelector as ValueSelector).slice(2).forEach((key, i) => {
@ -955,7 +957,7 @@ export const getVarType = ({
curr = curr?.find((v: any) => v.variable === key)
if (isLast) {
type = curr?.type
type = (preferSchemaType && curr?.schemaType) ? curr?.schemaType : curr?.type
}
else {
if (curr?.type === VarType.object || curr?.type === VarType.file)

View File

@ -288,6 +288,7 @@ const VarReferencePicker: FC<Props> = ({
availableNodes,
isChatMode,
isConstant: !!isConstant,
preferSchemaType,
})
const { isEnv, isChatVar, isRagVar, isValidVar, isException } = useMemo(() => {

View File

@ -110,8 +110,8 @@ const useBeforeRunForm = ({
}
}
if (datasourceType === DatasourceType.onlineDrive) {
const { bucket, fileList, selectedFileIds } = dataSourceStore.getState()
const file = fileList.find(file => file.id === selectedFileIds[0])
const { bucket, onlineDriveFileList, selectedFileIds } = dataSourceStore.getState()
const file = onlineDriveFileList.find(file => file.id === selectedFileIds[0])
datasourceInfo = {
bucket,
id: file?.id,

View File

@ -7,6 +7,7 @@ import OptionCard from '../option-card'
import Selector from './selector'
import { useChunkStructure } from './hooks'
import Button from '@/app/components/base/button'
import Instruction from './instruction'
type ChunkStructureProps = {
chunkStructure?: ChunkStructureEnum
@ -51,20 +52,23 @@ const ChunkStructure = ({
}
{
!chunkStructure && (
<Selector
options={options}
onChange={onChunkStructureChange}
readonly={readonly}
trigger={(
<Button
className='w-full'
variant='secondary-accent'
>
<RiAddLine className='mr-1 h-4 w-4' />
{t('workflow.nodes.knowledgeBase.chooseChunkStructure')}
</Button>
)}
/>
<>
<Selector
options={options}
onChange={onChunkStructureChange}
readonly={readonly}
trigger={(
<Button
className='w-full'
variant='secondary-accent'
>
<RiAddLine className='mr-1 h-4 w-4' />
{t('workflow.nodes.knowledgeBase.chooseChunkStructure')}
</Button>
)}
/>
<Instruction className='mt-2' />
</>
)
}
</Field>

View File

@ -0,0 +1,47 @@
import React from 'react'
import { AddChunks } from '@/app/components/base/icons/src/vender/knowledge'
import Line from './line'
import cn from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
import { useDocLink } from '@/context/i18n'
type InstructionProps = {
className?: string
}
const Instruction = ({
className,
}: InstructionProps) => {
const { t } = useTranslation()
const docLink = useDocLink()
return (
<div className={cn('flex flex-col gap-y-2 overflow-hidden rounded-[10px] bg-workflow-process-bg p-4', className)}>
<div className='relative flex size-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-[5px]'>
<AddChunks className='size-5 text-text-accent' />
<Line className='absolute -left-px bottom-[-76px]' type='vertical' />
<Line className='absolute -right-px bottom-[-76px]' type='vertical' />
<Line className='absolute -top-px right-[-184px]' type='horizontal' />
<Line className='absolute -bottom-px right-[-184px]' type='horizontal' />
</div>
<div className='flex flex-col gap-y-1'>
<div className='system-sm-medium text-text-secondary'>
{t('workflow.nodes.knowledgeBase.chunkStructureTip.title')}
</div>
<div className='system-xs-regular'>
<p className='text-text-tertiary'>{t('workflow.nodes.knowledgeBase.chunkStructureTip.message')}</p>
<a
href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents/chunking-and-cleaning-text')}
target='_blank'
rel='noopener noreferrer'
className='text-text-accent'
>
{t('workflow.nodes.knowledgeBase.chunkStructureTip.learnMore')}
</a>
</div>
</div>
</div>
)
}
export default React.memo(Instruction)

View File

@ -0,0 +1,41 @@
import React from 'react'
type LineProps = {
type?: 'vertical' | 'horizontal'
className?: string
}
const Line = ({
type = 'vertical',
className,
}: LineProps) => {
if (type === 'vertical') {
return (
<svg xmlns='http://www.w3.org/2000/svg' width='2' height='132' viewBox='0 0 2 132' fill='none' className={className}>
<path d='M1 0L1 132' stroke='url(#paint0_linear_10882_18766)' />
<defs>
<linearGradient id='paint0_linear_10882_18766' x1='-7.99584' y1='132' x2='-7.96108' y2='6.4974e-07' gradientUnits='userSpaceOnUse'>
<stop stopColor='var(--color-background-gradient-mask-transparent)' />
<stop offset='0.877606' stopColor='var(--color-divider-subtle)' />
<stop offset='1' stopColor='var(--color-background-gradient-mask-transparent)' />
</linearGradient>
</defs>
</svg>
)
}
return (
<svg xmlns='http://www.w3.org/2000/svg' width='240' height='2' viewBox='0 0 240 2' fill='none' className={className}>
<path d='M0 1H240' stroke='url(#paint0_linear_10882_18763)' />
<defs>
<linearGradient id='paint0_linear_10882_18763' x1='240' y1='9.99584' x2='3.95539e-05' y2='9.88094' gradientUnits='userSpaceOnUse'>
<stop stopColor='var(--color-background-gradient-mask-transparent)' />
<stop offset='0.9031' stopColor='var(--color-divider-subtle)' />
<stop offset='1' stopColor='var(--color-background-gradient-mask-transparent)' />
</linearGradient>
</defs>
</svg>
)
}
export default React.memo(Line)

View File

@ -47,7 +47,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
} = useConfig(id)
const filterVar = useCallback((variable: Var) => {
if(!data.chunk_structure) return false
if (!data.chunk_structure) return false
switch (data.chunk_structure) {
case ChunkStructureEnum.general:
return variable.schemaType === 'general_structure'
@ -55,37 +55,16 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
return variable.schemaType === 'parent_child_structure'
case ChunkStructureEnum.question_answer:
return variable.schemaType === 'qa_structure'
default:
return false
}
return false
}, [data.chunk_structure])
return (
<div>
<BoxGroupField
boxGroupProps={{
boxProps: { withBorderBottom: true },
}}
fieldProps={{
fieldTitleProps: {
title: t('workflow.nodes.common.inputVars'),
},
}}
>
<VarReferencePicker
nodeId={id}
isShowNodeName
value={data.index_chunk_variable_selector}
onChange={handleInputVariableChange}
readonly={nodesReadOnly}
filterVar={filterVar}
isFilterFileVar
isSupportFileVar={false}
preferSchemaType
/>
</BoxGroupField>
<Group
className='py-3'
withBorderBottom
withBorderBottom={!!data.chunk_structure}
>
<ChunkStructure
chunkStructure={data.chunk_structure}
@ -93,11 +72,33 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
readonly={nodesReadOnly}
/>
</Group>
<BoxGroup>
<div className='space-y-3'>
{
data.chunk_structure && (
<>
{
data.chunk_structure && (
<>
<BoxGroupField
boxGroupProps={{
boxProps: { withBorderBottom: true },
}}
fieldProps={{
fieldTitleProps: {
title: t('workflow.nodes.common.inputVars'),
},
}}
>
<VarReferencePicker
nodeId={id}
isShowNodeName
value={data.index_chunk_variable_selector}
onChange={handleInputVariableChange}
readonly={nodesReadOnly}
filterVar={filterVar}
isFilterFileVar
isSupportFileVar={false}
preferSchemaType
/>
</BoxGroupField>
<BoxGroup>
<div className='space-y-3'>
<IndexMethod
chunkStructure={data.chunk_structure}
indexMethod={data.indexing_technique}
@ -119,29 +120,29 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
<div className='pt-1'>
<Split className='h-[1px]' />
</div>
</>
)
}
<RetrievalSetting
indexMethod={data.indexing_technique}
searchMethod={data.retrieval_model.search_method}
onRetrievalSearchMethodChange={handleRetrievalSearchMethodChange}
hybridSearchMode={data.retrieval_model.hybridSearchMode}
onHybridSearchModeChange={handleHybridSearchModeChange}
weightedScore={data.retrieval_model.weights}
onWeightedScoreChange={handleWeighedScoreChange}
rerankingModel={data.retrieval_model.reranking_model}
onRerankingModelChange={handleRerankingModelChange}
topK={data.retrieval_model.top_k}
onTopKChange={handleTopKChange}
scoreThreshold={data.retrieval_model.score_threshold}
onScoreThresholdChange={handleScoreThresholdChange}
isScoreThresholdEnabled={data.retrieval_model.score_threshold_enabled}
onScoreThresholdEnabledChange={handleScoreThresholdEnabledChange}
readonly={nodesReadOnly}
/>
</div>
</BoxGroup>
<RetrievalSetting
indexMethod={data.indexing_technique}
searchMethod={data.retrieval_model.search_method}
onRetrievalSearchMethodChange={handleRetrievalSearchMethodChange}
hybridSearchMode={data.retrieval_model.hybridSearchMode}
onHybridSearchModeChange={handleHybridSearchModeChange}
weightedScore={data.retrieval_model.weights}
onWeightedScoreChange={handleWeighedScoreChange}
rerankingModel={data.retrieval_model.reranking_model}
onRerankingModelChange={handleRerankingModelChange}
topK={data.retrieval_model.top_k}
onTopKChange={handleTopKChange}
scoreThreshold={data.retrieval_model.score_threshold}
onScoreThresholdChange={handleScoreThresholdChange}
isScoreThresholdEnabled={data.retrieval_model.score_threshold_enabled}
onScoreThresholdEnabledChange={handleScoreThresholdEnabledChange}
readonly={nodesReadOnly}
/>
</div>
</BoxGroup>
</>
)
}
</div>
)
}

View File

@ -934,6 +934,11 @@ const translation = {
knowledgeBase: {
chunkStructure: 'Chunk Structure',
chooseChunkStructure: 'Choose a chunk structure',
chunkStructureTip: {
title: 'Please choose a chunk structure',
message: 'After configuring chunk structure, this node will automatically load the remaining configurations.',
learnMore: 'Learn more',
},
changeChunkStructure: 'Change Chunk Structure',
aboutRetrieval: 'about retrieval method.',
chunkIsRequired: 'Chunk structure is required',

View File

@ -933,6 +933,11 @@ const translation = {
knowledgeBase: {
chunkStructure: '分段结构',
chooseChunkStructure: '选择分段结构',
chunkStructureTip: {
title: '请选择分段结构',
message: '配置完成分段结构后,将自动加载剩余配置。',
learnMore: '了解更多',
},
changeChunkStructure: '更改分段结构',
aboutRetrieval: '关于知识检索。',
chunkIsRequired: '分段结构是必需的',