@ -1,20 +1,31 @@
'use client'
import type { FC , PropsWithChildren , ReactNode } from 'react'
import React , { useCallback , useEffect , useLayoutEffect , useRef , useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useBoolean } from 'ahooks'
import { XMarkIcon } from '@heroicons/react/20/solid'
import { RocketLaunchIcon } from '@heroicons/react/24/outline'
import {
RiArrowLeftLine ,
RiCloseLine ,
RiSearchEyeLine ,
} from '@remixicon/react'
import Link from 'next/link'
import { groupBy } from 'lodash-es'
import Image from 'next/image'
import SettingCog from '../assets/setting-gear-mod.svg'
import OrangeEffect from '../assets/option-card-effect-orange.svg'
import FamilyMod from '../assets/family-mod.svg'
import Note from '../assets/note-mod.svg'
import FileList from '../assets/file-list-3-fill.svg'
import { indexMethodIcon } from '../icons'
import PreviewItem , { PreviewType } from './preview-item'
import LanguageSelect from './language-select'
import s from './index.module.css'
import unescape from './unescape'
import escape from './escape'
import { OptionCard } from './option-card'
import LanguageSelect from './language-select'
import { DelimiterInput , MaxLengthInput , OverlapInput } from './inputs'
import cn from '@/utils/classnames'
import type { CrawlOptions , CrawlResultItem , CreateDocumentReq , CustomFile , FileIndexingEstimateResponse , FullDocumentDetail , IndexingEstimateParams , NotionInfo , PreProcessingRule , ProcessRule , Rules , createDocumentResponse } from '@/models/datasets'
import {
@ -24,7 +35,6 @@ import {
fetchDefaultProcessRule ,
} from '@/service/datasets'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
import FloatRightContainer from '@/app/components/base/float-right-container'
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
@ -32,25 +42,34 @@ import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/ec
import { type RetrievalConfig } from '@/types/app'
import { ensureRerankModelSelected , isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
import Toast from '@/app/components/base/toast'
import { formatNumber } from '@/utils/format'
import type { NotionPage } from '@/models/common'
import { DataSourceProvider } from '@/models/common'
import { DataSourceType , DocForm } from '@/models/datasets'
import NotionIcon from '@/app/components/base/notion-icon'
import Switch from '@/app/components/base/switch'
import { MessageChatSquare } from '@/app/components/base/icons/src/public/common'
import { useDatasetDetailContext } from '@/context/dataset-detail'
import I18n from '@/context/i18n'
import { IS_CE_EDITION } from '@/config'
import { RETRIEVE_METHOD } from '@/types/app'
import useBreakpoints , { MediaType } from '@/hooks/use-breakpoints'
import Tooltip from '@/app/components/base/tooltip'
import { useDefaultModel , useModelList , useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { LanguagesSupported } from '@/i18n/language'
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
import Checkbox from '@/app/components/base/checkbox'
import RadioCard from '@/app/components/base/radio-card'
import { MessageChatSquare } from '@/app/components/base/icons/src/public/common'
import { IS_CE_EDITION } from '@/config'
import Switch from '@/app/components/base/switch'
const TextLabel : FC < PropsWithChildren > = ( props ) = > {
return < label className = 'text-text-secondary text-xs font-semibold leading-none' > { props . children } < / label >
}
const FormField : FC < PropsWithChildren < { label : ReactNode } > > = ( props ) = > {
return < div className = 'space-y-2 flex-1' >
< TextLabel > { props . label } < / TextLabel >
{ props . children }
< / div >
}
type ValueOf < T > = T [ keyof T ]
type StepTwoProps = {
@ -60,6 +79,7 @@ type StepTwoProps = {
onSetting : ( ) = > void
datasetId? : string
indexingType? : ValueOf < IndexingType >
retrievalMethod? : string
dataSourceType : DataSourceType
files : CustomFile [ ]
notionPages? : NotionPage [ ]
@ -69,6 +89,7 @@ type StepTwoProps = {
websiteCrawlJobId? : string
onStepChange ? : ( delta : number ) = > void
updateIndexingTypeCache ? : ( type : string ) = > void
updateRetrievalMethodCache ? : ( method : string ) = > void
updateResultCache ? : ( res : createDocumentResponse ) = > void
onSave ? : ( ) = > void
onCancel ? : ( ) = > void
@ -76,7 +97,8 @@ type StepTwoProps = {
enum SegmentType {
AUTO = 'automatic' ,
CUSTOM = 'custom' ,
GENERAL = 'general' ,
PARENT_CHILD = 'parent_child' ,
}
enum IndexingType {
QUALIFIED = 'high_quality' ,
@ -85,6 +107,32 @@ enum IndexingType {
const DEFAULT_SEGMENT_IDENTIFIER = '\\n\\n'
type ParentChildConfig = {
chunkForContext : 'paragraph' | 'full_doc'
parent : {
delimiter : string
maxLength : number
}
child : {
delimiter : string
maxLength : number
}
rules : PreProcessingRule [ ]
}
const defaultParentChildConfig : ParentChildConfig = {
chunkForContext : 'paragraph' ,
parent : {
delimiter : '\\n\\n' ,
maxLength : 4000 ,
} ,
child : {
delimiter : '\\n\\n' ,
maxLength : 4000 ,
} ,
rules : [ ] ,
}
const StepTwo = ( {
isSetting ,
documentDetail ,
@ -104,6 +152,7 @@ const StepTwo = ({
updateResultCache ,
onSave ,
onCancel ,
updateRetrievalMethodCache ,
} : StepTwoProps ) = > {
const { t } = useTranslation ( )
const { locale } = useContext ( I18n )
@ -117,7 +166,7 @@ const StepTwo = ({
const [ scrolled , setScrolled ] = useState ( false )
const previewScrollRef = useRef < HTMLDivElement > ( null )
const [ previewScrolled , setPreviewScrolled ] = useState ( false )
const [ segmentationType , setSegmentationType ] = useState < SegmentType > ( SegmentType . AUTO )
const [ segmentationType , setSegmentationType ] = useState < SegmentType > ( SegmentType . GENERAL )
const [ segmentIdentifier , doSetSegmentIdentifier ] = useState ( DEFAULT_SEGMENT_IDENTIFIER )
const setSegmentIdentifier = useCallback ( ( value : string ) = > {
doSetSegmentIdentifier ( value ? escape ( value ) : DEFAULT_SEGMENT_IDENTIFIER )
@ -143,14 +192,17 @@ const StepTwo = ({
const [ QATipHide , setQATipHide ] = useState ( false )
const [ previewSwitched , setPreviewSwitched ] = useState ( false )
const [ showPreview , { setTrue : setShowPreview , setFalse : hidePreview } ] = useBoolean ( )
const [ customFileIndexingEstimate, setCustom FileIndexingEstimate] = useState < FileIndexingEstimateResponse | null > ( null )
const [ generalFileIndexingEstimate, setGeneral FileIndexingEstimate] = useState < FileIndexingEstimateResponse | null > ( null )
const [ automaticFileIndexingEstimate , setAutomaticFileIndexingEstimate ] = useState < FileIndexingEstimateResponse | null > ( null )
// TODO: refactor this
const fileIndexingEstimate = ( ( ) = > {
return segmentationType === SegmentType . AUTO ? automaticFileIndexingEstimate : custom FileIndexingEstimate
return segmentationType === SegmentType . AUTO ? automaticFileIndexingEstimate : general FileIndexingEstimate
} ) ( )
const [ isCreating , setIsCreating ] = useState ( false )
const [ parentChildConfig , setParentChildConfig ] = useState < ParentChildConfig > ( defaultParentChildConfig )
const scrollHandle = ( e : Event ) = > {
if ( ( e . target as HTMLDivElement ) . scrollTop > 0 )
setScrolled ( true )
@ -197,26 +249,27 @@ const StepTwo = ({
if ( defaultConfig ) {
setSegmentIdentifier ( defaultConfig . segmentation . separator )
setMax ( defaultConfig . segmentation . max_tokens )
setOverlap ( defaultConfig . segmentation . chunk_overlap )
setOverlap ( defaultConfig . segmentation . chunk_overlap ! )
setRules ( defaultConfig . pre_processing_rules )
}
setParentChildConfig ( defaultParentChildConfig )
}
const fetchFileIndexingEstimate = async ( docForm = DocForm . TEXT , language? : string ) = > {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const res = await didFetchFileIndexingEstimate ( getFileIndexingEstimateParams ( docForm , language ) ! )
if ( segmentationType === SegmentType . CUSTOM )
set Custom FileIndexingEstimate( res )
if ( segmentationType === SegmentType . GENERAL )
set General FileIndexingEstimate( res )
else
setAutomaticFileIndexingEstimate ( res )
}
const confirmChangeCustomConfig = ( ) = > {
if ( segmentationType === SegmentType . CUSTOM && max > 4000 ) {
if ( segmentationType === SegmentType . GENERAL && max > 4000 ) {
Toast . notify ( { type : 'error' , message : t ( 'datasetCreation.stepTwo.maxLengthCheck' ) } )
return
}
set Custom FileIndexingEstimate( null )
set General FileIndexingEstimate( null )
setShowPreview ( )
fetchFileIndexingEstimate ( )
setPreviewSwitched ( false )
@ -229,7 +282,7 @@ const StepTwo = ({
rules : { } as any , // api will check this. It will be removed after api refactored.
mode : segmentationType ,
}
if ( segmentationType === SegmentType . CUSTOM ) {
if ( segmentationType === SegmentType . GENERAL ) {
const ruleObj = {
pre_processing_rules : rules ,
segmentation : {
@ -339,11 +392,11 @@ const StepTwo = ({
)
const getCreationParams = ( ) = > {
let params
if ( segmentationType === SegmentType . CUSTOM && overlap > max ) {
if ( segmentationType === SegmentType . GENERAL && overlap > max ) {
Toast . notify ( { type : 'error' , message : t ( 'datasetCreation.stepTwo.overlapCheck' ) } )
return
}
if ( segmentationType === SegmentType . CUSTOM && max > 4000 ) {
if ( segmentationType === SegmentType . GENERAL && max > 4000 ) {
Toast . notify ( { type : 'error' , message : t ( 'datasetCreation.stepTwo.maxLengthCheck' ) } )
return
}
@ -461,6 +514,8 @@ const StepTwo = ({
} )
updateIndexingTypeCache && updateIndexingTypeCache ( indexType as string )
updateResultCache && updateResultCache ( res )
// eslint-disable-next-line @typescript-eslint/no-use-before-define
updateRetrievalMethodCache && updateRetrievalMethodCache ( retrievalConfig . search_method as string )
}
else {
res = await createDocument ( {
@ -496,10 +551,8 @@ const StepTwo = ({
const previewSwitch = async ( language? : string ) = > {
setPreviewSwitched ( true )
setIsLanguageSelectDisabled ( true )
if ( segmentationType === SegmentType . AUTO )
setAutomaticFileIndexingEstimate ( null )
else
setCustomFileIndexingEstimate ( null )
if ( segmentationType === SegmentType . GENERAL )
setGeneralFileIndexingEstimate ( null )
try {
await fetchFileIndexingEstimate ( DocForm . QA , language )
}
@ -564,17 +617,9 @@ const StepTwo = ({
} , [ isAPIKeySet , indexingType , datasetId ] )
useEffect ( ( ) = > {
if ( segmentationType === SegmentType . AUTO ) {
setAutomaticFileIndexingEstimate ( null )
! isMobile && setShowPreview ( )
fetchFileIndexingEstimate ( )
setPreviewSwitched ( false )
}
else {
hidePreview ( )
setCustomFileIndexingEstimate ( null )
setPreviewSwitched ( false )
}
hidePreview ( )
setGeneralFileIndexingEstimate ( null )
setPreviewSwitched ( false )
} , [ segmentationType , indexType ] )
const [ retrievalConfig , setRetrievalConfig ] = useState ( currentDataset ? . retrieval_model_dict || {
@ -590,132 +635,188 @@ const StepTwo = ({
} as RetrievalConfig )
return (
< div className = 'flex w-full h-full' >
< div ref = { scrollRef } className = 'relative h-full w-full overflow-y-scroll' >
< div className = { cn ( s . pageHeader , scrolled && s . fixed , isMobile && '!px-6' ) } >
< span > { t ( 'datasetCreation.steps.two' ) } < / span >
{ ( isMobile || ! showPreview ) && (
< Button
className = 'border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
onClick = { setShowPreview }
>
< Tooltip >
< div className = "flex flex-row items-center" >
< RocketLaunchIcon className = "h-4 w-4 mr-1.5 stroke-[1.8px]" / >
< span className = "text-[13px]" > { t ( 'datasetCreation.stepTwo.previewTitleButton' ) } < / span >
< / div >
< / Tooltip >
< / Button >
) }
< / div >
< div className = 'flex w-full max-h-full h-full overflow-y-auto' >
< div className = 'relative h-full w-full overflow-y-scroll' >
< div className = { cn ( s . form , isMobile && '!px-4' ) } >
< div className = { s . label } > { t ( 'datasetCreation.stepTwo.segmentation' ) } < / div >
< div className = 'max-w-[640px]' >
< div
className = { cn (
s . radioItem ,
s . segmentationItem ,
segmentationType === SegmentType . AUTO && s . active ,
) }
onClick = { ( ) = > setSegmentationType ( SegmentType . AUTO ) }
>
< span className = { cn ( s . typeIcon , s . auto ) } / >
< span className = { cn ( s . radio ) } / >
< div className = { s . typeHeader } >
< div className = { s . title } > { t ( 'datasetCreation.stepTwo.auto' ) } < / div >
< div className = { s . tip } > { t ( 'datasetCreation.stepTwo.autoDescription' ) } < / div >
< / div >
< / div >
< div
className = { cn (
s . radioItem ,
s . segmentationItem ,
segmentationType === SegmentType . CUSTOM && s . active ,
segmentationType === SegmentType . CUSTOM && s . custom ,
) }
onClick = { ( ) = > setSegmentationType ( SegmentType . CUSTOM ) }
>
< span className = { cn ( s . typeIcon , s . customize ) } / >
< span className = { cn ( s . radio ) } / >
< div className = { s . typeHeader } >
< div className = { s . title } > { t ( 'datasetCreation.stepTwo.custom' ) } < / div >
< div className = { s . tip } > { t ( 'datasetCreation.stepTwo.customDescription' ) } < / div >
< / div >
{ segmentationType === SegmentType . CUSTOM && (
< div className = { s . typeFormBody } >
< div className = { s . formRow } >
< div className = 'w-full' >
< div className = { s . label } >
{ t ( 'datasetCreation.stepTwo.separator' ) }
< Tooltip
popupContent = {
< div className = 'max-w-[200px]' >
{ t ( 'datasetCreation.stepTwo.separatorTip' ) }
< / div >
}
/ >
< div className = 'space-y-4' >
< OptionCard
title = { t ( 'datasetCreation.stepTwo.general' ) }
icon = { < Image src = { SettingCog } alt = { t ( 'datasetCreation.stepTwo.general' ) } / > }
activeHeaderClassName = 'bg-gradient-to-r from-[#EFF0F9] to-[#F9FAFB]'
description = { t ( 'datasetCreation.stepTwo.generalTip' ) }
isActive = { SegmentType . GENERAL === segmentationType }
onClick = { ( ) = > setSegmentationType ( SegmentType . GENERAL ) }
actions = {
< >
< Button variant = { 'secondary-accent' } >
< RiSearchEyeLine className = 'h-4 w-4 mr-1.5' / >
{ t ( 'datasetCreation.stepTwo.previewChunk' ) }
< / Button >
< Button variant = { 'ghost' } onClick = { resetRules } >
{ t ( 'datasetCreation.stepTwo.reset' ) }
< / Button >
< / >
}
>
< div className = 'space-y-4' >
< div className = 'flex gap-3' >
< DelimiterInput
value = { segmentIdentifier }
onChange = { e = > setSegmentIdentifier ( e . target . value ) }
/ >
< MaxLengthInput
value = { max }
onChange = { setMax }
/ >
< OverlapInput
value = { overlap }
min = { 1 }
onChange = { setOverlap }
/ >
< / div >
< div className = 'space-y-2' >
< div className = 'w-full flex flex-col' >
< TextLabel > { t ( 'datasetCreation.stepTwo.rules' ) } < / TextLabel >
< div className = 'mt-4 space-y-2' >
{ rules . map ( rule = > (
< div key = { rule . id } className = { s . ruleItem } onClick = { ( ) = > {
ruleChangeHandle ( rule . id )
} } >
< Checkbox
checked = { rule . enabled }
/ >
< label className = "ml-2 text-sm font-normal cursor-pointer text-gray-800" > { getRuleName ( rule . id ) } < / label >
< / div >
) ) }
< / div >
< Input
type = "text"
className = 'h-9'
placeholder = { t ( 'datasetCreation.stepTwo.separatorPlaceholder' ) || '' } value = { segmentIdentifier }
onChange = { e = > setSegmentIdentifier ( e . target . value ) }
/ >
< / div >
< / div >
< div className = { s . formRow } >
< div className = 'w-full' >
< div className = { s . label } > { t ( 'datasetCreation.stepTwo.maxLength' ) } < / div >
< Input
type = "number"
className = 'h-9'
placeholder = { t ( 'datasetCreation.stepTwo.maxLength' ) || '' }
value = { max }
max = { 4000 }
min = { 1 }
onChange = { e = > setMax ( parseInt ( e . target . value . replace ( /^0+/ , '' ) , 10 ) ) }
/ >
< / div >
< / div >
< / OptionCard >
< OptionCard
title = { t ( 'datasetCreation.stepTwo.parentChild' ) }
icon = { < Image src = { FamilyMod } alt = { t ( 'datasetCreation.stepTwo.parentChild' ) } / > }
effectImg = { OrangeEffect . src }
activeHeaderClassName = 'bg-gradient-to-r from-[#F9F1EE] to-[#F9FAFB]'
description = { t ( 'datasetCreation.stepTwo.parentChildTip' ) }
isActive = { SegmentType . PARENT_CHILD === segmentationType }
onClick = { ( ) = > setSegmentationType ( SegmentType . PARENT_CHILD ) }
actions = {
< >
< Button variant = { 'secondary-accent' } >
< RiSearchEyeLine className = 'h-4 w-4 mr-1.5' / >
{ t ( 'datasetCreation.stepTwo.previewChunk' ) }
< / Button >
< Button variant = { 'ghost' } onClick = { resetRules } >
{ t ( 'datasetCreation.stepTwo.reset' ) }
< / Button >
< / >
}
>
< div className = 'space-y-4' >
< div className = 'space-y-2' >
< TextLabel >
{ t ( 'datasetCreation.stepTwo.parentChunkForContext' ) }
< / TextLabel >
< RadioCard
icon = { < Image src = { Note } alt = '' / > }
title = { t ( 'datasetCreation.stepTwo.paragraph' ) }
description = { t ( 'datasetCreation.stepTwo.paragraphTip' ) }
isChosen = { parentChildConfig . chunkForContext === 'paragraph' }
onChosen = { ( ) = > setParentChildConfig (
{
. . . parentChildConfig ,
chunkForContext : 'paragraph' ,
} ,
) }
chosenConfig = {
< div className = 'flex gap-2' >
< DelimiterInput
value = { parentChildConfig . parent . delimiter }
onChange = { e = > setParentChildConfig ( {
. . . parentChildConfig ,
parent : {
. . . parentChildConfig . parent ,
delimiter : e.target.value ,
} ,
} ) }
/ >
< MaxLengthInput
value = { parentChildConfig . parent . maxLength }
onChange = { value = > setParentChildConfig ( {
. . . parentChildConfig ,
parent : {
. . . parentChildConfig . parent ,
maxLength : value ,
} ,
} ) }
/ >
< / div >
}
/ >
< RadioCard
icon = { < Image src = { FileList } alt = '' / > }
title = { t ( 'datasetCreation.stepTwo.fullDoc' ) }
description = { t ( 'datasetCreation.stepTwo.fullDocTip' ) }
onChosen = { ( ) = > setParentChildConfig (
{
. . . parentChildConfig ,
chunkForContext : 'full_doc' ,
} ,
) }
isChosen = { parentChildConfig . chunkForContext === 'full_doc' }
/ >
< / div >
< div className = { s . formRow } >
< div className = 'w-full' >
< div className = { s . label } >
{ t ( 'datasetCreation.stepTwo.overlap' ) }
< Tooltip
popupContent = {
< div className = 'max-w-[200px]' >
{ t ( 'datasetCreation.stepTwo.overlapTip' ) }
< / div >
}
/ >
< / div >
< Input
type = "number"
className = 'h-9'
placeholder = { t ( 'datasetCreation.stepTwo.overlap' ) || '' }
value = { overlap }
min = { 1 }
onChange = { e = > setOverlap ( parseInt ( e . target . value . replace ( /^0+/ , '' ) , 10 ) ) }
< div className = 'space-y-4' >
< TextLabel >
{ t ( 'datasetCreation.stepTwo.childChunkForRetrieval' ) }
< / TextLabel >
< div className = 'flex gap-3 mt-2' >
< DelimiterInput
value = { parentChildConfig . child . delimiter }
onChange = { e = > setParentChildConfig ( {
. . . parentChildConfig ,
child : {
. . . parentChildConfig . child ,
delimiter : e.target.value ,
} ,
} ) }
/ >
< MaxLengthInput
value = { parentChildConfig . child . maxLength }
onChange = { value = > setParentChildConfig ( {
. . . parentChildConfig ,
child : {
. . . parentChildConfig . child ,
maxLength : value ,
} ,
} ) }
/ >
< / div >
< / div >
< div className = { s . formRow } >
< div className = 'w-full flex flex-col gap-1' >
< div className = { s . label } > { t ( 'datasetCreation.stepTwo.rules' ) } < / div >
{ rules . map ( rule = > (
< div key = { rule . id } className = { s . ruleItem } >
< input id = { rule . id } type = "checkbox" checked = { rule . enabled } onChange = { ( ) = > ruleChangeHandle ( rule . id ) } className = "w-4 h-4 rounded border-gray-300 text-blue-700 focus:ring-blue-700" / >
< label htmlFor = { rule . id } className = "ml-2 text-sm font-normal cursor-pointer text-gray-800" > { getRuleName ( rule . id ) } < / label >
< / div >
) ) }
< div className = 'space-y-2' >
< TextLabel >
{ t ( 'datasetCreation.stepTwo.rules' ) }
< / TextLabel >
< div className = 'space-y-2 mt-2' >
{ rules . map ( rule = > (
< div key = { rule . id } className = { s . ruleItem } onClick = { ( ) = > {
ruleChangeHandle ( rule . id )
} } >
< Checkbox
checked = { rule . enabled }
/ >
< label className = "ml-2 text-sm font-normal cursor-pointer text-gray-800" > { getRuleName ( rule . id ) } < / label >
< / div >
) ) }
< / div >
< / div >
< / div >
< div className = { s . formFooter } >
< Button variant = "primary" className = { cn ( s . button ) } onClick = { confirmChangeCustomConfig } > { t ( 'datasetCreation.stepTwo.preview' ) } < / Button >
< Button className = { cn ( s . button , 'ml-2' ) } onClick = { resetRules } > { t ( 'datasetCreation.stepTwo.reset' ) } < / Button >
< / div >
< / div >
) }
< / OptionCard >
< / div >
< / div >
< div className = { s . label } > { t ( 'datasetCreation.stepTwo.indexMode' ) } < / div >
@ -736,7 +837,9 @@ const StepTwo = ({
setIndexType ( IndexingType . QUALIFIED )
} }
>
< span className = { cn ( s . typeIcon , s . qualified ) } / >
< div className = 'h-8 p-1.5 bg-white rounded-lg border border-components-panel-border-subtle justify-center items-center inline-flex absolute left-5 top-[18px]' >
< Image src = { indexMethodIcon . high_quality } alt = 'Gold Icon' width = { 20 } height = { 20 } / >
< / div >
{ ! hasSetIndexType && < span className = { cn ( s . radio ) } / > }
< div className = { s . typeHeader } >
< div className = { s . title } >
@ -765,7 +868,9 @@ const StepTwo = ({
) }
onClick = { changeToEconomicalType }
>
< span className = { cn ( s . typeIcon , s . economical ) } / >
< div className = 'h-8 p-1.5 bg-white rounded-lg border border-components-panel-border-subtle justify-center items-center inline-flex absolute left-5 top-[18px]' >
< Image src = { indexMethodIcon . economical } alt = 'Economical Icon' width = { 20 } height = { 20 } / >
< / div >
{ ! hasSetIndexType && < span className = { cn ( s . radio ) } / > }
< div className = { s . typeHeader } >
< div className = { s . title } > { t ( 'datasetCreation.stepTwo.economical' ) } < / div >
@ -777,7 +882,7 @@ const StepTwo = ({
{ hasSetIndexType && indexType === IndexingType . ECONOMICAL && (
< div className = 'mt-2 text-xs text-gray-500 font-medium' >
{ t ( 'datasetCreation.stepTwo.indexSettingTip' ) }
< Link className = 'text- [#155EEF] ' href = { ` /datasets/ ${ datasetId } /settings ` } > { t ( 'datasetCreation.stepTwo.datasetSettingLink' ) } < / Link >
< Link className = 'text- text-accent ' href = { ` /datasets/ ${ datasetId } /settings ` } > { t ( 'datasetCreation.stepTwo.datasetSettingLink' ) } < / Link >
< / div >
) }
{ IS_CE_EDITION && indexType === IndexingType . QUALIFIED && (
@ -787,19 +892,17 @@ const StepTwo = ({
< MessageChatSquare className = 'w-4 h-4' / >
< / div >
< div className = 'grow mx-3' >
< div className = 'mb- [2px] text-md font-medium text-gray-900'> { t ( 'datasetCreation.stepTwo.QATitle' ) } < / div >
< div className = 'mb- 0.5 text-md font-medium text-gray-900'> { t ( 'datasetCreation.stepTwo.QATitle' ) } < / div >
< div className = 'inline-flex items-center text-[13px] leading-[18px] text-gray-500' >
< span className = 'pr-1' > { t ( 'datasetCreation.stepTwo.QALanguage' ) } < / span >
< LanguageSelect currentLanguage = { docLanguage } onSelect = { handleSelect } disabled = { isLanguageSelectDisabled } / >
< / div >
< / div >
< div className = 'shrink-0' >
< Switch
defaultValue = { docForm === DocForm . QA }
onChange = { handleSwitch }
size = 'md'
/ >
< / div >
< Switch
defaultValue = { docForm === DocForm . QA }
onChange = { handleSwitch }
size = 'md'
/ >
< / div >
{ docForm === DocForm . QA && ! QATipHide && (
< div className = 'flex justify-between items-center px-5 py-2 bg-orange-50 border-t border-amber-100 rounded-b-xl text-[13px] leading-[18px] text-medium text-amber-500' >
@ -824,7 +927,7 @@ const StepTwo = ({
{ ! ! datasetId && (
< div className = 'mt-2 text-xs text-gray-500 font-medium' >
{ t ( 'datasetCreation.stepTwo.indexSettingTip' ) }
< Link className = 'text- [#155EEF] ' href = { ` /datasets/ ${ datasetId } /settings ` } > { t ( 'datasetCreation.stepTwo.datasetSettingLink' ) } < / Link >
< Link className = 'text- text-accent ' href = { ` /datasets/ ${ datasetId } /settings ` } > { t ( 'datasetCreation.stepTwo.datasetSettingLink' ) } < / Link >
< / div >
) }
< / div >
@ -836,7 +939,7 @@ const StepTwo = ({
< div className = { s . label } >
< div className = 'shrink-0 mr-4' > { t ( 'datasetSettings.form.retrievalSetting.title' ) } < / div >
< div className = 'leading-[18px] text-xs font-normal text-gray-500' >
< a target = '_blank' rel = 'noopener noreferrer' href = 'https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className = 'text- [#155eef] '> { t ( 'datasetSettings.form.retrievalSetting.learnMore' ) } < / a >
< a target = '_blank' rel = 'noopener noreferrer' href = 'https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className = 'text- text-accent '> { t ( 'datasetSettings.form.retrievalSetting.learnMore' ) } < / a >
{ t ( 'datasetSettings.form.retrievalSetting.longDescription' ) }
< / div >
< / div >
@ -866,83 +969,14 @@ const StepTwo = ({
< / div >
< / div >
< div className = { s . source } >
< div className = { s . sourceContent } >
{ dataSourceType === DataSourceType . FILE && (
< >
< div className = 'mb-2 text-xs font-medium text-gray-500' > { t ( 'datasetCreation.stepTwo.fileSource' ) } < / div >
< div className = 'flex items-center text-sm leading-6 font-medium text-gray-800' >
< span className = { cn ( s . fileIcon , files . length && s [ files [ 0 ] . extension || '' ] ) } / >
{ getFileName ( files [ 0 ] . name || '' ) }
{ files . length > 1 && (
< span className = { s . sourceCount } >
< span > { t ( 'datasetCreation.stepTwo.other' ) } < / span >
< span > { files . length - 1 } < / span >
< span > { t ( 'datasetCreation.stepTwo.fileUnit' ) } < / span >
< / span >
) }
< / div >
< / >
) }
{ dataSourceType === DataSourceType . NOTION && (
< >
< div className = 'mb-2 text-xs font-medium text-gray-500' > { t ( 'datasetCreation.stepTwo.notionSource' ) } < / div >
< div className = 'flex items-center text-sm leading-6 font-medium text-gray-800' >
< NotionIcon
className = 'shrink-0 mr-1'
type = 'page'
src = { notionPages [ 0 ] ? . page_icon }
/ >
{ notionPages [ 0 ] ? . page_name }
{ notionPages . length > 1 && (
< span className = { s . sourceCount } >
< span > { t ( 'datasetCreation.stepTwo.other' ) } < / span >
< span > { notionPages . length - 1 } < / span >
< span > { t ( 'datasetCreation.stepTwo.notionUnit' ) } < / span >
< / span >
) }
< / div >
< / >
) }
{ dataSourceType === DataSourceType . WEB && (
< >
< div className = 'mb-2 text-xs font-medium text-gray-500' > { t ( 'datasetCreation.stepTwo.websiteSource' ) } < / div >
< div className = 'flex items-center text-sm leading-6 font-medium text-gray-800' >
< Globe01 className = 'shrink-0 mr-1' / >
< span className = 'grow w-0 truncate' > { websitePages [ 0 ] . source_url } < / span >
{ websitePages . length > 1 && (
< span className = { s . sourceCount } >
< span > { t ( 'datasetCreation.stepTwo.other' ) } < / span >
< span > { websitePages . length - 1 } < / span >
< span > { t ( 'datasetCreation.stepTwo.webpageUnit' ) } < / span >
< / span >
) }
< / div >
< / >
) }
< / div >
< div className = { s . divider } / >
< div className = { s . segmentCount } >
< div className = 'mb-2 text-xs font-medium text-gray-500' > { t ( 'datasetCreation.stepTwo.estimateSegment' ) } < / div >
< div className = 'flex items-center text-sm leading-6 font-medium text-gray-800' >
{
fileIndexingEstimate
? (
< div className = 'text-xs font-medium text-gray-800' > { formatNumber ( fileIndexingEstimate . total_segments ) } < / div >
)
: (
< div className = { s . calculating } > { t ( 'datasetCreation.stepTwo.calculating' ) } < / div >
)
}
< / div >
< / div >
< / div >
{ ! isSetting
? (
< div className = 'flex items-center mt-8 py-2' >
< Button onClick = { ( ) = > onStepChange && onStepChange ( - 1 ) } > { t ( 'datasetCreation.stepTwo.previousStep' ) } < / Button >
< div className = { s . divider } / >
< Button loading = { isCreating } variant = 'primary' onClick = { createHandle } > { t ( 'datasetCreation.stepTwo.nextStep' ) } < / Button >
< Button onClick = { ( ) = > onStepChange && onStepChange ( - 1 ) } >
< RiArrowLeftLine className = 'w-4 h-4 mr-1' / >
{ t ( 'datasetCreation.stepTwo.previousStep' ) }
< / Button >
< Button className = 'ml-auto' loading = { isCreating } variant = 'primary' onClick = { createHandle } > { t ( 'datasetCreation.stepTwo.nextStep' ) } < / Button >
< / div >
)
: (