Selaa lähdekoodia

feat: add retriever rank fe (#1557)

Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
Joel 1 vuosi sitten
vanhempi
commit
888e8c6dac
80 muutettua tiedostoa jossa 2670 lisäystä ja 463 poistoa
  1. 2 2
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
  2. 1 1
      web/app/components/app/configuration/dataset-config/card-item/index.tsx
  3. 9 3
      web/app/components/app/configuration/dataset-config/card-item/style.module.css
  4. 0 1
      web/app/components/app/configuration/dataset-config/index.tsx
  5. 172 143
      web/app/components/app/configuration/dataset-config/params-config/index.tsx
  6. 209 64
      web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
  7. 12 6
      web/app/components/app/configuration/index.tsx
  8. 19 0
      web/app/components/base/icons/assets/public/common/multi-path-retrieval.svg
  9. 18 0
      web/app/components/base/icons/assets/public/common/n-to-1-retrieval.svg
  10. 1 0
      web/app/components/base/icons/assets/public/llm/cohere-text.svg
  11. 16 0
      web/app/components/base/icons/assets/public/llm/cohere.svg
  12. 6 0
      web/app/components/base/icons/assets/vender/solid/arrows/high-priority.svg
  13. 11 0
      web/app/components/base/icons/assets/vender/solid/development/pattern-recognition.svg
  14. 6 0
      web/app/components/base/icons/assets/vender/solid/development/semantic.svg
  15. 153 0
      web/app/components/base/icons/src/public/common/MultiPathRetrieval.json
  16. 16 0
      web/app/components/base/icons/src/public/common/MultiPathRetrieval.tsx
  17. 146 0
      web/app/components/base/icons/src/public/common/NTo1Retrieval.json
  18. 16 0
      web/app/components/base/icons/src/public/common/NTo1Retrieval.tsx
  19. 2 0
      web/app/components/base/icons/src/public/common/index.ts
  20. 112 0
      web/app/components/base/icons/src/public/llm/Cohere.json
  21. 16 0
      web/app/components/base/icons/src/public/llm/Cohere.tsx
  22. 17 0
      web/app/components/base/icons/src/public/llm/CohereText.json
  23. 16 0
      web/app/components/base/icons/src/public/llm/CohereText.tsx
  24. 2 0
      web/app/components/base/icons/src/public/llm/index.ts
  25. 53 0
      web/app/components/base/icons/src/vender/solid/arrows/HighPriority.json
  26. 16 0
      web/app/components/base/icons/src/vender/solid/arrows/HighPriority.tsx
  27. 1 0
      web/app/components/base/icons/src/vender/solid/arrows/index.ts
  28. 98 0
      web/app/components/base/icons/src/vender/solid/development/PatternRecognition.json
  29. 16 0
      web/app/components/base/icons/src/vender/solid/development/PatternRecognition.tsx
  30. 53 0
      web/app/components/base/icons/src/vender/solid/development/Semantic.json
  31. 16 0
      web/app/components/base/icons/src/vender/solid/development/Semantic.tsx
  32. 2 0
      web/app/components/base/icons/src/vender/solid/development/index.ts
  33. 1 1
      web/app/components/base/param-item/score-threshold-item.tsx
  34. 1 1
      web/app/components/base/param-item/top-k-item.tsx
  35. 55 0
      web/app/components/base/radio-card/index.tsx
  36. 40 0
      web/app/components/base/radio-card/simple/index.tsx
  37. 25 0
      web/app/components/base/radio-card/simple/style.module.css
  38. 25 0
      web/app/components/base/radio-card/style.module.css
  39. 1 0
      web/app/components/base/slider/index.tsx
  40. 50 0
      web/app/components/datasets/common/check-rerank-model.ts
  41. 40 0
      web/app/components/datasets/common/economical-retrieval-method-config/index.tsx
  42. 91 0
      web/app/components/datasets/common/retrieval-method-config/index.tsx
  43. 64 0
      web/app/components/datasets/common/retrieval-method-info/index.tsx
  44. 131 0
      web/app/components/datasets/common/retrieval-param-config/index.tsx
  45. 100 6
      web/app/components/datasets/create/step-two/index.tsx
  46. 10 4
      web/app/components/datasets/documents/detail/completed/SegmentCard.tsx
  47. 24 0
      web/app/components/datasets/hit-testing/index.tsx
  48. 95 0
      web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx
  49. 3 3
      web/app/components/datasets/hit-testing/style.module.css
  50. 105 79
      web/app/components/datasets/hit-testing/textarea.tsx
  51. 55 1
      web/app/components/datasets/settings/form/index.tsx
  52. 3 0
      web/app/components/datasets/settings/permissions-radio/index.tsx
  53. 53 0
      web/app/components/header/account-setting/model-page/configs/cohere.tsx
  54. 2 0
      web/app/components/header/account-setting/model-page/configs/index.ts
  55. 2 0
      web/app/components/header/account-setting/model-page/declarations.ts
  56. 7 104
      web/app/components/header/account-setting/model-page/index.tsx
  57. 28 4
      web/app/components/header/account-setting/model-page/model-selector/index.tsx
  58. 205 0
      web/app/components/header/account-setting/model-page/system-model/index.tsx
  59. 5 0
      web/config/index.ts
  60. 8 5
      web/context/debug-configuration.ts
  61. 37 2
      web/context/provider-context.tsx
  62. 11 0
      web/i18n/lang/app-debug.en.ts
  63. 11 0
      web/i18n/lang/app-debug.zh.ts
  64. 11 3
      web/i18n/lang/common.en.ts
  65. 11 3
      web/i18n/lang/common.zh.ts
  66. 1 0
      web/i18n/lang/dataset-creation.en.ts
  67. 1 0
      web/i18n/lang/dataset-creation.zh.ts
  68. 3 3
      web/i18n/lang/dataset-documents.en.ts
  69. 3 3
      web/i18n/lang/dataset-documents.zh.ts
  70. 9 9
      web/i18n/lang/dataset-hit-testing.en.ts
  71. 7 7
      web/i18n/lang/dataset-hit-testing.zh.ts
  72. 6 0
      web/i18n/lang/dataset-settings.en.ts
  73. 6 0
      web/i18n/lang/dataset-settings.zh.ts
  74. 21 0
      web/i18n/lang/dataset.en.ts
  75. 21 0
      web/i18n/lang/dataset.zh.ts
  76. 4 0
      web/models/datasets.ts
  77. 9 2
      web/models/debug.ts
  78. 8 0
      web/service/common.ts
  79. 4 3
      web/service/datasets.ts
  80. 24 0
      web/types/app.ts

+ 2 - 2
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx

@@ -153,7 +153,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
     return <Loading />
 
   return (
-    <div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
+    <div className='flex'>
       {!hideSideBar && <AppSideBar
         title={datasetRes?.name || '--'}
         icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
@@ -168,7 +168,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
         dataset: datasetRes,
         mutateDatasetRes: () => mutateDatasetRes(),
       }}>
-        <div className="bg-white grow">{children}</div>
+        <div className="bg-white grow" style={{ minHeight: 'calc(100vh - 56px)' }}>{children}</div>
       </DatasetDetailContext.Provider>
     </div>
   )

+ 1 - 1
web/app/components/app/configuration/dataset-config/card-item/index.tsx

@@ -16,7 +16,7 @@ export type ICardItemProps = {
   onRemove: (id: string) => void
   readonly?: boolean
 }
-
+// used in universal-chat
 const CardItem: FC<ICardItemProps> = ({
   className,
   config,

+ 9 - 3
web/app/components/app/configuration/dataset-config/card-item/style.module.css

@@ -1,16 +1,22 @@
 .card {
   box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
-  width: calc(50% - 4px);
+  width: 100%;
 }
 
 .card:hover {
   box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
 }
 
-.deleteBtn {
+.btnWrap {
+  padding-left: 64px;
   visibility: hidden;
+  background: linear-gradient(270deg, #FFF 49.99%, rgba(255, 255, 255, 0.00) 98.1%);
 }
 
-.card:hover .deleteBtn {
+.card:hover .btnWrap {
   visibility: visible;
+}
+
+.settingBtn:hover {
+  background-color: rgba(0, 0, 0, 0.05);
 }

+ 0 - 1
web/app/components/app/configuration/dataset-config/index.tsx

@@ -105,7 +105,6 @@ const DatasetConfig: FC = () => {
           onChange={handleSelectContextVar}
         />
       )}
-
     </FeaturePanel>
   )
 }

+ 172 - 143
web/app/components/app/configuration/dataset-config/params-config/index.tsx

@@ -4,96 +4,23 @@ import { memo, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
 import cn from 'classnames'
-import { HelpCircle, Settings04 } from '@/app/components/base/icons/src/vender/line/general'
-import {
-  PortalToFollowElem,
-  PortalToFollowElemContent,
-  PortalToFollowElemTrigger,
-} from '@/app/components/base/portal-to-follow-elem'
-import Tooltip from '@/app/components/base/tooltip-plus'
-import Slider from '@/app/components/base/slider'
-import Switch from '@/app/components/base/switch'
+import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
 import ConfigContext from '@/context/debug-configuration'
-
-// TODO
-const PARAMS_KEY = [
-  'top_k',
-  'score_threshold',
-]
-const PARAMS = {
-  top_k: {
-    default: 2,
-    step: 1,
-    min: 1,
-    max: 10,
-  },
-  score_threshold: {
-    default: 0.7,
-    step: 0.01,
-    min: 0,
-    max: 1,
-  },
-} as any
-
-export type IParamItemProps = {
-  id: string
-  name: string
-  tip: string
-  value: number
-  enable: boolean
-  step?: number
-  min?: number
-  max: number
-  onChange: (key: string, value: number) => void
-  onSwitchChange: (key: string, enable: boolean) => void
-}
-
-const ParamItem: FC<IParamItemProps> = ({ id, name, tip, step = 0.1, min = 0, max, value, enable, onChange, onSwitchChange }) => {
-  return (
-    <div>
-      <div className="flex items-center justify-between">
-        <div className="flex items-center">
-          {id === 'score_threshold' && (
-            <Switch
-              size='md'
-              defaultValue={enable}
-              onChange={async (val) => {
-                onSwitchChange(id, val)
-              }}
-            />
-          )}
-          <span className="mx-1 text-gray-800 text-[13px] leading-[18px] font-medium">{name}</span>
-          <Tooltip popupContent={<div className="w-[200px]">{tip}</div>}>
-            <HelpCircle className='w-[14px] h-[14px] text-gray-400' />
-          </Tooltip>
-        </div>
-        <div className="flex items-center"></div>
-      </div>
-      <div className="mt-2 flex items-center justify-between">
-        <div className="flex items-center h-7">
-          <div className="w-[148px]">
-            <Slider
-              disabled={!enable}
-              value={max < 5 ? value * 100 : value}
-              min={min < 1 ? min * 100 : min}
-              max={max < 5 ? max * 100 : max}
-              onChange={value => onChange(id, value / (max < 5 ? 100 : 1))}
-            />
-          </div>
-        </div>
-        <div className="flex items-center">
-          <input disabled={!enable} type="number" min={min} max={max} step={step} className="block w-[48px] h-7 text-xs leading-[18px] rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900  placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600 disabled:opacity-60" value={value} onChange={(e) => {
-            const value = parseFloat(e.target.value)
-            if (value < min || value > max)
-              return
-
-            onChange(id, value)
-          }} />
-        </div>
-      </div>
-    </div>
-  )
-}
+import TopKItem from '@/app/components/base/param-item/top-k-item'
+import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
+import Modal from '@/app/components/base/modal'
+import Button from '@/app/components/base/button'
+import RadioCard from '@/app/components/base/radio-card/simple'
+import { RETRIEVE_TYPE } from '@/types/app'
+import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
+import { useProviderContext } from '@/context/provider-context'
+import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
+import Toast from '@/app/components/base/toast'
+import { DATASET_DEFAULT } from '@/config'
+import {
+  MultiPathRetrieval,
+  NTo1Retrieval,
+} from '@/app/components/base/icons/src/public/common'
 
 const ParamsConfig: FC = () => {
   const { t } = useTranslation()
@@ -102,24 +29,47 @@ const ParamsConfig: FC = () => {
     datasetConfigs,
     setDatasetConfigs,
   } = useContext(ConfigContext)
+  const [tempDataSetConfigs, setTempDataSetConfigs] = useState(datasetConfigs)
+
+  const type = tempDataSetConfigs.retrieval_model
+  const setType = (value: RETRIEVE_TYPE) => {
+    setTempDataSetConfigs({
+      ...tempDataSetConfigs,
+      retrieval_model: value,
+    })
+  }
+
+  const {
+    rerankDefaultModel,
+    isRerankDefaultModelVaild,
+  } = useProviderContext()
+
+  const rerankModel = (() => {
+    if (tempDataSetConfigs.reranking_model) {
+      return {
+        provider_name: tempDataSetConfigs.reranking_model.reranking_provider_name,
+        model_name: tempDataSetConfigs.reranking_model.reranking_model_name,
+      }
+    }
+    else if (rerankDefaultModel) {
+      return {
+        provider_name: rerankDefaultModel.model_provider.provider_name,
+        model_name: rerankDefaultModel.model_name,
+      }
+    }
+  })()
 
   const handleParamChange = (key: string, value: number) => {
-    let notOutRangeValue = parseFloat(value.toFixed(2))
-    notOutRangeValue = Math.max(PARAMS[key].min, notOutRangeValue)
-    notOutRangeValue = Math.min(PARAMS[key].max, notOutRangeValue)
     if (key === 'top_k') {
-      setDatasetConfigs({
-        ...datasetConfigs,
-        top_k: notOutRangeValue,
+      setTempDataSetConfigs({
+        ...tempDataSetConfigs,
+        top_k: value,
       })
     }
     else if (key === 'score_threshold') {
-      setDatasetConfigs({
-        ...datasetConfigs,
-        [key]: {
-          enable: datasetConfigs.score_threshold.enable,
-          value: notOutRangeValue,
-        },
+      setTempDataSetConfigs({
+        ...tempDataSetConfigs,
+        score_threshold: value,
       })
     }
   }
@@ -128,54 +78,133 @@ const ParamsConfig: FC = () => {
     if (key === 'top_k')
       return
 
-    setDatasetConfigs({
-      ...datasetConfigs,
-      [key]: {
-        enable,
-        value: (datasetConfigs as any)[key].value,
-      },
+    setTempDataSetConfigs({
+      ...tempDataSetConfigs,
+      score_threshold_enabled: enable,
     })
   }
+  const isValid = () => {
+    let errMsg = ''
+    if (tempDataSetConfigs.retrieval_model === RETRIEVE_TYPE.multiWay) {
+      if (!tempDataSetConfigs.reranking_model?.reranking_model_name && (!rerankDefaultModel && isRerankDefaultModelVaild))
+        errMsg = t('appDebug.datasetConfig.rerankModelRequired')
+    }
+    if (errMsg) {
+      Toast.notify({
+        type: 'error',
+        message: errMsg,
+      })
+    }
+    return !errMsg
+  }
+  const handleSave = () => {
+    if (!isValid())
+      return
+
+    const config = { ...tempDataSetConfigs }
+    if (config.retrieval_model === RETRIEVE_TYPE.multiWay && !config.reranking_model) {
+      config.reranking_model = {
+        reranking_provider_name: rerankDefaultModel?.model_provider.provider_name,
+        reranking_model_name: rerankDefaultModel?.model_name,
+      } as any
+    }
+    setDatasetConfigs(config)
+    setOpen(false)
+  }
 
   return (
-    <PortalToFollowElem
-      open={open}
-      onOpenChange={setOpen}
-      placement='bottom-end'
-      offset={{
-        mainAxis: 4,
-      }}
-    >
-      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
-        <div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
-          <Settings04 className="w-[14px] h-[14px]" />
-          <div className='text-xs font-medium'>
-            {t('appDebug.datasetConfig.params')}
-          </div>
+    <div>
+      <div
+        className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}
+        onClick={() => {
+          setTempDataSetConfigs({
+            ...datasetConfigs,
+            top_k: datasetConfigs.top_k || DATASET_DEFAULT.top_k,
+            score_threshold: datasetConfigs.score_threshold || DATASET_DEFAULT.score_threshold,
+          })
+          setOpen(true)
+        }}
+      >
+        <Settings04 className="w-[14px] h-[14px]" />
+        <div className='text-xs font-medium'>
+          {t('appDebug.datasetConfig.params')}
         </div>
-      </PortalToFollowElemTrigger>
-      <PortalToFollowElemContent style={{ zIndex: 50 }}>
-        <div className='w-[240px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
-          {PARAMS_KEY.map((key: string) => {
-            const currentValue = key === 'top_k' ? datasetConfigs[key] : (datasetConfigs as any)[key]?.value
-            const currentEnableState = key === 'top_k' ? true : (datasetConfigs as any)[key]?.enable
-            return (
-              <ParamItem
-                key={key}
-                id={key}
-                name={t(`appDebug.datasetConfig.${key}`)}
-                tip={t(`appDebug.datasetConfig.${key}Tip`)}
-                {...PARAMS[key]}
-                value={currentValue}
-                enable={currentEnableState}
-                onChange={handleParamChange}
-                onSwitchChange={handleSwitch}
+      </div>
+      {
+        open && (
+          <Modal
+            isShow={open}
+            onClose={() => {
+              setOpen(false)
+            }}
+            className='min-w-[528px]'
+            wrapperClassName='z-50'
+            title={t('appDebug.datasetConfig.settingTitle')}
+          >
+            <div className='mt-2 space-y-3'>
+              <RadioCard
+                icon={<NTo1Retrieval className='shrink-0 mr-3 w-9 h-9 rounded-lg' />}
+                title={t('appDebug.datasetConfig.retrieveOneWay.title')}
+                description={t('appDebug.datasetConfig.retrieveOneWay.description')}
+                isChosen={type === RETRIEVE_TYPE.oneWay}
+                onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }}
               />
-            )
-          })}
-        </div>
-      </PortalToFollowElemContent>
-    </PortalToFollowElem>
+              <RadioCard
+                icon={<MultiPathRetrieval className='shrink-0 mr-3 w-9 h-9 rounded-lg' />}
+                title={t('appDebug.datasetConfig.retrieveMultiWay.title')}
+                description={t('appDebug.datasetConfig.retrieveMultiWay.description')}
+                isChosen={type === RETRIEVE_TYPE.multiWay}
+                onChosen={() => { setType(RETRIEVE_TYPE.multiWay) }}
+              />
+            </div>
+            {type === RETRIEVE_TYPE.multiWay && (
+              <>
+                <div className='mt-6'>
+                  <div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.rerankModel.key')}</div>
+                  <div>
+                    <ModelSelector
+                      popClassName='!max-w-[100%] !w-full'
+                      value={rerankModel && { providerName: rerankModel.provider_name, modelName: rerankModel.model_name } as any}
+                      modelType={ModelType.reranking}
+                      onChange={(v) => {
+                        setTempDataSetConfigs({
+                          ...tempDataSetConfigs,
+                          reranking_model: {
+                            reranking_provider_name: v.model_provider.provider_name,
+                            reranking_model_name: v.model_name,
+                          },
+                        })
+                      }}
+                    />
+                  </div>
+                </div>
+                <div className='mt-4 space-y-4'>
+                  <TopKItem
+                    value={tempDataSetConfigs.top_k}
+                    onChange={handleParamChange}
+                    enable={true}
+                  />
+                  <ScoreThresholdItem
+                    value={tempDataSetConfigs.score_threshold}
+                    onChange={handleParamChange}
+                    enable={tempDataSetConfigs.score_threshold_enabled}
+                    hasSwitch={true}
+                    onSwitchChange={handleSwitch}
+                  />
+                </div>
+              </>
+            )}
+            <div className='mt-6 flex justify-end'>
+              <Button className='mr-2 flex-shrink-0' onClick={() => {
+                setOpen(false)
+              }}>{t('common.operation.cancel')}</Button>
+              <Button type='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
+            </div>
+          </Modal>
+        )
+      }
+
+    </div>
   )
 }
 export default memo(ParamsConfig)

+ 209 - 64
web/app/components/app/configuration/dataset-config/settings-modal/index.tsx

@@ -1,8 +1,11 @@
 import type { FC } from 'react'
-import { useState } from 'react'
+import { useRef, useState } from 'react'
+import { useClickAway } from 'ahooks'
 import { useTranslation } from 'react-i18next'
+import { isEqual } from 'lodash-es'
+import cn from 'classnames'
+import { BookOpenIcon } from '@heroicons/react/24/outline'
 import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio'
-import Modal from '@/app/components/base/modal'
 import Button from '@/app/components/base/button'
 import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
 import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
@@ -11,12 +14,29 @@ import type { DataSet } from '@/models/datasets'
 import { useToastContext } from '@/app/components/base/toast'
 import { updateDatasetSetting } from '@/service/datasets'
 import { useModalContext } from '@/context/modal-context'
+import { XClose } from '@/app/components/base/icons/src/vender/line/general'
+import type { RetrievalConfig } from '@/types/app'
+import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
+import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
+import { useProviderContext } from '@/context/provider-context'
+import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
+import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
+import PermissionsRadio from '@/app/components/datasets/settings/permissions-radio'
 
 type SettingsModalProps = {
   currentDataset: DataSet
   onCancel: () => void
   onSave: (newDataset: DataSet) => void
 }
+
+const rowClass = `
+  flex justify-between py-4
+`
+
+const labelClass = `
+  flex w-[168px] shrink-0
+`
+
 const SettingsModal: FC<SettingsModalProps> = ({
   currentDataset,
   onCancel,
@@ -24,13 +44,28 @@ const SettingsModal: FC<SettingsModalProps> = ({
 }) => {
   const { t } = useTranslation()
   const { notify } = useToastContext()
+  const ref = useRef(null)
+  useClickAway(() => {
+    if (ref)
+      onCancel()
+  }, ref)
+
   const { setShowAccountSettingModal } = useModalContext()
   const [loading, setLoading] = useState(false)
   const [localeCurrentDataset, setLocaleCurrentDataset] = useState({ ...currentDataset })
+  const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique)
+  const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig)
+
+  const {
+    rerankDefaultModel,
+    isRerankDefaultModelVaild,
+  } = useProviderContext()
 
   const handleValueChange = (type: string, value: string) => {
     setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value })
   }
+  const [isHideChangedTip, setIsHideChangedTip] = useState(false)
+  const isRetrievalChanged = !isEqual(retrievalConfig, localeCurrentDataset?.retrieval_model_dict) || indexMethod !== localeCurrentDataset?.indexing_technique
 
   const handleSave = async () => {
     if (loading)
@@ -39,19 +74,41 @@ const SettingsModal: FC<SettingsModalProps> = ({
       notify({ type: 'error', message: t('datasetSettings.form.nameError') })
       return
     }
+    if (
+      !isReRankModelSelected({
+        rerankDefaultModel,
+        isRerankDefaultModelVaild,
+        retrievalConfig,
+        indexMethod,
+      })
+    ) {
+      notify({ type: 'error', message: t('appDebug.datasetConfig.rerankModelRequired') })
+      return
+    }
+    const postRetrievalConfig = ensureRerankModelSelected({
+      rerankDefaultModel: rerankDefaultModel!,
+      retrievalConfig,
+      indexMethod,
+    })
     try {
       setLoading(true)
-      const { id, name, description, indexing_technique } = localeCurrentDataset
+      const { id, name, description, permission } = localeCurrentDataset
       await updateDatasetSetting({
         datasetId: id,
         body: {
           name,
           description,
-          indexing_technique,
+          permission,
+          indexing_technique: indexMethod,
+          retrieval_model: postRetrievalConfig,
         },
       })
       notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
-      onSave(localeCurrentDataset)
+      onSave({
+        ...localeCurrentDataset,
+        indexing_technique: indexMethod,
+        retrieval_model_dict: postRetrievalConfig,
+      })
     }
     catch (e) {
       notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
@@ -62,74 +119,162 @@ const SettingsModal: FC<SettingsModalProps> = ({
   }
 
   return (
-    <Modal
-      isShow
-      onClose={() => {}}
-      className='!p-8 !pb-6 !max-w-none !w-[640px]'
+    <div
+      className='fixed top-16 right-2 flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10'
+      style={{
+        zIndex: 11,
+        width: 700,
+        height: 'calc(100vh - 72px)',
+      }}
+      ref={ref}
     >
-      <div className='mb-2 text-xl font-semibold text-gray-900'>
-        {t('datasetSettings.title')}
-      </div>
-      <div className='py-2'>
-        <div className='leading-9 text-sm font-medium text-gray-900'>
-          {t('datasetSettings.form.name')}
+      <div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
+        <div className='flex flex-col text-base font-semibold text-gray-900'>
+          <div className='leading-6'>{t('datasetSettings.title')}</div>
         </div>
-        <input
-          value={localeCurrentDataset.name}
-          onChange={e => handleValueChange('name', e.target.value)}
-          className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none'
-          placeholder={t('datasetSettings.form.namePlaceholder') || ''}
-        />
-      </div>
-      <div className='py-2'>
-        <div className='flex justify-between items-center mb-1 h-5 text-sm font-medium text-gray-900'>
-          {t('datasetSettings.form.desc')}
-        </div>
-        <div className='mb-2 text-xs text-gray-500'>
-          {t('datasetSettings.form.descInfo')}<a href='/' className='text-primary-600'>{t('common.operation.learnMore')}</a>
+        <div className='flex items-center'>
+          <div
+            onClick={onCancel}
+            className='flex justify-center items-center w-6 h-6 cursor-pointer'
+          >
+            <XClose className='w-4 h-4 text-gray-500' />
+          </div>
         </div>
-        <textarea
-          value={localeCurrentDataset.description || ''}
-          onChange={e => handleValueChange('description', e.target.value)}
-          className='block px-3 py-2 w-full h-[88px] rounded-lg bg-gray-100 text-sm outline-none appearance-none resize-none'
-          placeholder={t('datasetSettings.form.descPlaceholder') || ''}
-        />
       </div>
-      <div className='py-2'>
-        <div className='leading-9 text-sm font-medium text-gray-900'>
-          {t('datasetSettings.form.indexMethod')}
-        </div>
-        <div>
-          <IndexMethodRadio
-            disable={!localeCurrentDataset?.embedding_available}
-            value={localeCurrentDataset.indexing_technique}
-            onChange={v => handleValueChange('indexing_technique', v!)}
-            itemClassName='!w-[282px]'
+      {/* Body */}
+      <div className='p-6 pt-5 border-b overflow-y-auto pb-[68px]' style={{
+        borderBottom: 'rgba(0, 0, 0, 0.05)',
+      }}>
+        <div className={cn(rowClass, 'items-center')}>
+          <div className={labelClass}>
+            {t('datasetSettings.form.name')}
+          </div>
+          <input
+            value={localeCurrentDataset.name}
+            onChange={e => handleValueChange('name', e.target.value)}
+            className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none'
+            placeholder={t('datasetSettings.form.namePlaceholder') || ''}
           />
         </div>
-      </div>
-      <div className='py-2'>
-        <div className='leading-9 text-sm font-medium text-gray-900'>
-          {t('datasetSettings.form.embeddingModel')}
+        <div className={cn(rowClass)}>
+          <div className={labelClass}>
+            {t('datasetSettings.form.desc')}
+          </div>
+          <div className='grow'>
+            <textarea
+              value={localeCurrentDataset.description || ''}
+              onChange={e => handleValueChange('description', e.target.value)}
+              className='block px-3 py-2 w-full h-[88px] rounded-lg bg-gray-100 text-sm outline-none appearance-none resize-none'
+              placeholder={t('datasetSettings.form.descPlaceholder') || ''}
+            />
+            <a className='mt-2 flex items-center h-[18px] px-3 text-xs text-gray-500' href="https://docs.dify.ai/advanced/datasets#how-to-write-a-good-dataset-description" target='_blank'>
+              <BookOpenIcon className='w-3 h-[18px] mr-1' />
+              {t('datasetSettings.form.descWrite')}
+            </a>
+          </div>
         </div>
-        <div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
-          <ModelSelector
-            readonly
-            value={{
-              providerName: localeCurrentDataset.embedding_model_provider as ProviderEnum,
-              modelName: localeCurrentDataset.embedding_model,
-            }}
-            modelType={ModelType.embeddings}
-            onChange={() => {}}
-          />
+        <div className={rowClass}>
+          <div className={labelClass}>
+            <div>{t('datasetSettings.form.permissions')}</div>
+          </div>
+          <div className='w-[480px]'>
+            <PermissionsRadio
+              disable={!localeCurrentDataset?.embedding_available}
+              value={localeCurrentDataset.permission}
+              onChange={v => handleValueChange('permission', v!)}
+              itemClassName='!w-[227px]'
+            />
+          </div>
         </div>
-        <div className='mt-2 w-full text-xs leading-6 text-gray-500'>
-          {t('datasetSettings.form.embeddingModelTip')}
-          <span className='text-[#155eef] cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
+        <div className="w-full h-0 border-b-[0.5px] border-b-gray-200 my-2"></div>
+        <div className={cn(rowClass)}>
+          <div className={labelClass}>
+            {t('datasetSettings.form.indexMethod')}
+          </div>
+          <div className='grow'>
+            <IndexMethodRadio
+              disable={!localeCurrentDataset?.embedding_available}
+              value={indexMethod}
+              onChange={v => setIndexMethod(v!)}
+              itemClassName='!w-[227px]'
+            />
+          </div>
+        </div>
+        {indexMethod === 'high_quality' && (
+          <div className={cn(rowClass)}>
+            <div className={labelClass}>
+              {t('datasetSettings.form.embeddingModel')}
+            </div>
+            <div className='grow'>
+              <div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
+                <ModelSelector
+                  readonly
+                  value={{
+                    providerName: localeCurrentDataset.embedding_model_provider as ProviderEnum,
+                    modelName: localeCurrentDataset.embedding_model,
+                  }}
+                  modelType={ModelType.embeddings}
+                  onChange={() => {}}
+                />
+              </div>
+              <div className='mt-2 w-full text-xs leading-6 text-gray-500'>
+                {t('datasetSettings.form.embeddingModelTip')}
+                <span className='text-[#155eef] cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
+              </div>
+            </div>
+          </div>
+        )}
+
+        {/* Retrieval Method Config */}
+        <div className={rowClass}>
+          <div className={labelClass}>
+            <div>
+              <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+              <div className='leading-[18px] text-xs font-normal text-gray-500'>
+                <a target='_blank' href='https://docs.dify.ai/v/zh-hans/advanced/retrieval-augment' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
+                {t('datasetSettings.form.retrievalSetting.description')}
+              </div>
+            </div>
+          </div>
+          <div className='w-[480px]'>
+            {indexMethod === 'high_quality'
+              ? (
+                <RetrievalMethodConfig
+                  value={retrievalConfig}
+                  onChange={setRetrievalConfig}
+                />
+              )
+              : (
+                <EconomicalRetrievalMethodConfig
+                  value={retrievalConfig}
+                  onChange={setRetrievalConfig}
+                />
+              )}
+          </div>
         </div>
       </div>
-      <div></div>
-      <div className='flex items-center justify-end mt-6'>
+      {isRetrievalChanged && !isHideChangedTip && (
+        <div className='absolute z-10 left-[30px] right-[30px] bottom-[76px] flex h-10 items-center px-3 rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] shadow-lg justify-between'>
+          <div className='flex items-center'>
+            <AlertTriangle className='mr-1 w-3 h-3 text-[#F79009]' />
+            <div className='leading-[18px] text-xs font-medium text-gray-700'>{t('appDebug.datasetConfig.retrieveChangeTip')}</div>
+          </div>
+          <div className='p-1 cursor-pointer' onClick={(e) => {
+            setIsHideChangedTip(true)
+            e.stopPropagation()
+            e.nativeEvent.stopImmediatePropagation()
+          }}>
+            <XClose className='w-4 h-4 text-gray-500 ' />
+          </div>
+        </div>
+      )}
+
+      <div
+        className='absolute z-10 bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
+        style={{
+          borderColor: 'rgba(0, 0, 0, 0.05)',
+        }}
+      >
         <Button
           onClick={onCancel}
           className='mr-2 text-sm font-medium'
@@ -145,7 +290,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
           {t('common.operation.save')}
         </Button>
       </div>
-    </Modal>
+    </div>
   )
 }
 

+ 12 - 6
web/app/components/app/configuration/index.tsx

@@ -37,7 +37,7 @@ import { fetchAppDetail, updateAppModelConfig } from '@/service/apps'
 import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables } from '@/utils/model-config'
 import { fetchDatasets } from '@/service/datasets'
 import { useProviderContext } from '@/context/provider-context'
-import { AppType, ModelModeType, Resolution, TransferMethod } from '@/types/app'
+import { AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
 import { FlipBackward } from '@/app/components/base/icons/src/vender/line/arrows'
 import { PromptMode } from '@/models/debug'
 import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
@@ -127,11 +127,14 @@ const Configuration: FC = () => {
   })
 
   const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
-    top_k: 2,
-    score_threshold: {
-      enable: false,
-      value: 0.7,
+    retrieval_model: RETRIEVE_TYPE.oneWay,
+    reranking_model: {
+      reranking_provider_name: '',
+      reranking_model_name: '',
     },
+    top_k: 2,
+    score_threshold_enabled: false,
+    score_threshold: 0.7,
   })
 
   const setModelConfig = (newModelConfig: ModelConfig) => {
@@ -391,7 +394,10 @@ const Configuration: FC = () => {
 
       syncToPublishedConfig(config)
       setPublishedConfig(config)
-      setDatasetConfigs(modelConfig.dataset_configs)
+      setDatasetConfigs({
+        retrieval_model: RETRIEVE_TYPE.oneWay,
+        ...modelConfig.dataset_configs,
+      })
       setHasFetchedDetail(true)
     })
   }, [appId])

+ 19 - 0
web/app/components/base/icons/assets/public/common/multi-path-retrieval.svg

@@ -0,0 +1,19 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_13429_43710)">
+<rect width="36" height="36" rx="8" fill="#FFF6ED"/>
+<path opacity="0.7" d="M22.25 28C22.25 29.7949 20.7949 31.25 19 31.25C17.2051 31.25 15.75 29.7949 15.75 28C15.75 26.2051 17.2051 24.75 19 24.75C20.7949 24.75 22.25 26.2051 22.25 28Z" stroke="#FB6514" stroke-width="1.5"/>
+<path d="M19 12C21.2091 12 23 10.2091 23 8C23 5.79086 21.2091 4 19 4C16.7909 4 15 5.79086 15 8C15 10.2091 16.7909 12 19 12Z" fill="#FB6514"/>
+<path d="M15 22C17.2091 22 19 20.2091 19 18C19 15.7909 17.2091 14 15 14C12.7909 14 11 15.7909 11 18C11 20.2091 12.7909 22 15 22Z" fill="#FB6514"/>
+<path d="M36 23C38.7614 23 41 20.7614 41 18C41 15.2386 38.7614 13 36 13C33.2386 13 31 15.2386 31 18C31 20.7614 33.2386 23 36 23Z" fill="#FB6514"/>
+<path d="M0 18H10" stroke="#FB6514" stroke-width="1.5"/>
+<path d="M20 18L30 18" stroke="#FB6514" stroke-width="1.5"/>
+<path d="M0.00112438 15C0.00112438 15 -5.64364 15 0.851673 15C7.34699 15 7.84654 8 14 8" stroke="#FB6514" stroke-width="1.5"/>
+<path d="M23.75 9.28125C26.5688 10.1847 27.699 13.2045 30.625 15.0312" stroke="#FB6514" stroke-width="1.5"/>
+<path opacity="0.7" d="M-0.000543833 21C-0.000543833 21 -5.57819 21 0.893635 21C7.36546 21 7.8688 28 14 28" stroke="#FB6514" stroke-width="1.5"/>
+</g>
+<defs>
+<clipPath id="clip0_13429_43710">
+<rect width="36" height="36" rx="8" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 18 - 0
web/app/components/base/icons/assets/public/common/n-to-1-retrieval.svg

@@ -0,0 +1,18 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_13429_43700)">
+<rect width="36" height="36" rx="8" fill="#EEF4FF"/>
+<path opacity="0.7" d="M23.25 28C23.25 29.7949 21.7949 31.25 20 31.25C18.2051 31.25 16.75 29.7949 16.75 28C16.75 26.2051 18.2051 24.75 20 24.75C21.7949 24.75 23.25 26.2051 23.25 28Z" stroke="#444CE7" stroke-width="1.5"/>
+<path opacity="0.7" d="M23.25 8C23.25 9.79493 21.7949 11.25 20 11.25C18.2051 11.25 16.75 9.79493 16.75 8C16.75 6.20507 18.2051 4.75 20 4.75C21.7949 4.75 23.25 6.20507 23.25 8Z" stroke="#444CE7" stroke-width="1.5"/>
+<path d="M16 22C18.2091 22 20 20.2091 20 18C20 15.7909 18.2091 14 16 14C13.7909 14 12 15.7909 12 18C12 20.2091 13.7909 22 16 22Z" fill="#444CE7"/>
+<path d="M36 23C38.7614 23 41 20.7614 41 18C41 15.2386 38.7614 13 36 13C33.2386 13 31 15.2386 31 18C31 20.7614 33.2386 23 36 23Z" fill="#444CE7"/>
+<path d="M0 18L11 18" stroke="#444CE7" stroke-width="1.5"/>
+<path d="M21 18L30 18" stroke="#444CE7" stroke-width="1.5"/>
+<path opacity="0.7" d="M-0.00160408 15C-0.00160408 15 -6.00089 15 1.12411 15C8.24911 15 8.24908 8.25 14.9991 8.25" stroke="#444CE7" stroke-width="1.5"/>
+<path opacity="0.7" d="M0.000488281 21C0.000488281 21 -5.92692 21 1.17228 21C8.27148 21 8.27423 27.75 14.9998 27.75" stroke="#444CE7" stroke-width="1.5"/>
+</g>
+<defs>
+<clipPath id="clip0_13429_43700">
+<rect width="36" height="36" rx="8" fill="white"/>
+</clipPath>
+</defs>
+</svg>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 0
web/app/components/base/icons/assets/public/llm/cohere-text.svg


+ 16 - 0
web/app/components/base/icons/assets/public/llm/cohere.svg

@@ -0,0 +1,16 @@
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Clip path group">
+<mask id="mask0_13224_9519" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="22" height="22">
+<g id="clip0_2207_90691">
+<path id="Vector" d="M21.5 0.5H0.5V21.5H21.5V0.5Z" fill="white"/>
+</g>
+</mask>
+<g mask="url(#mask0_13224_9519)">
+<g id="Group">
+<path id="Vector_2" fill-rule="evenodd" clip-rule="evenodd" d="M7.30367 13.0035C7.8689 13.0035 8.99327 12.9725 10.5474 12.3326C12.3585 11.587 15.9617 10.2334 18.561 8.84305C20.3788 7.8706 21.1757 6.58448 21.1757 4.85248C21.1757 2.44869 19.2271 0.5 16.8233 0.5H6.75176C3.299 0.5 0.5 3.299 0.5 6.75176C0.5 10.2045 3.12069 13.0035 7.30367 13.0035Z" fill="#39594D"/>
+<path id="Vector_3" fill-rule="evenodd" clip-rule="evenodd" d="M9.00732 17.3086C9.00732 15.6162 10.0262 14.0902 11.5894 13.4414L14.7612 12.1251C17.9694 10.7936 21.5006 13.1513 21.5006 16.6249C21.5006 19.316 19.3185 21.4974 16.6273 21.4967L13.1933 21.4958C10.8813 21.4952 9.00732 19.6207 9.00732 17.3086Z" fill="#D18EE2"/>
+<path id="Vector_4" d="M4.10396 13.8277C2.11358 13.8277 0.5 15.4411 0.5 17.4315V17.8984C0.5 19.8887 2.11352 21.5022 4.1039 21.5022C6.09428 21.5022 7.70785 19.8887 7.70785 17.8984V17.4315C7.70785 15.4411 6.09434 13.8277 4.10396 13.8277Z" fill="#FF7759"/>
+</g>
+</g>
+</g>
+</svg>

+ 6 - 0
web/app/components/base/icons/assets/vender/solid/arrows/high-priority.svg

@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.01488 2.54553C8.91549 2.45869 8.79321 2.40229 8.66264 2.38306C8.53206 2.36384 8.39872 2.38261 8.27852 2.43712C8.15833 2.49164 8.05636 2.5796 7.98481 2.6905C7.91325 2.8014 7.87513 2.93055 7.875 3.06253V6.50003C6.05164 6.50003 4.30295 7.22436 3.01364 8.51367C1.72433 9.80299 1 11.5517 1 13.375C1 15.1984 1.72433 16.9471 3.01364 18.2364C4.30295 19.5257 6.05164 20.25 7.875 20.25H12C12.3647 20.25 12.7144 20.1052 12.9723 19.8473C13.2301 19.5894 13.375 19.2397 13.375 18.875C13.375 18.5104 13.2301 18.1606 12.9723 17.9028C12.7144 17.6449 12.3647 17.5 12 17.5H7.875C6.78098 17.5 5.73177 17.0654 4.95818 16.2919C4.1846 15.5183 3.75 14.4691 3.75 13.375C3.75 12.281 4.1846 11.2318 4.95818 10.4582C5.73177 9.68463 6.78098 9.25003 7.875 9.25003V12.6875C7.87513 12.8195 7.91325 12.9487 7.98481 13.0596C8.05636 13.1705 8.15833 13.2584 8.27852 13.3129C8.39872 13.3675 8.53206 13.3862 8.66264 13.367C8.79321 13.3478 8.91549 13.2914 9.01488 13.2045L14.5149 8.39203C14.5885 8.32751 14.6475 8.24801 14.6879 8.15885C14.7283 8.06969 14.7492 7.97292 14.7492 7.87503C14.7492 7.77714 14.7283 7.68038 14.6879 7.59122C14.6475 7.50206 14.5885 7.42256 14.5149 7.35803L9.01488 2.54553Z" fill="#212121"/>
+<path d="M21.625 17.5H17.5C17.1353 17.5 16.7856 17.6449 16.5277 17.9028C16.2699 18.1606 16.125 18.5104 16.125 18.875C16.125 19.2397 16.2699 19.5894 16.5277 19.8473C16.7856 20.1052 17.1353 20.25 17.5 20.25H21.625C21.9897 20.25 22.3394 20.1052 22.5973 19.8473C22.8551 19.5894 23 19.2397 23 18.875C23 18.5104 22.8551 18.1606 22.5973 17.9028C22.3394 17.6449 21.9897 17.5 21.625 17.5Z" fill="#212121"/>
+<path d="M21.625 12H17.5C17.1353 12 16.7856 12.1449 16.5277 12.4028C16.2699 12.6606 16.125 13.0104 16.125 13.375C16.125 13.7397 16.2699 14.0894 16.5277 14.3473C16.7856 14.6052 17.1353 14.75 17.5 14.75H21.625C21.9897 14.75 22.3394 14.6052 22.5973 14.3473C22.8551 14.0894 23 13.7397 23 13.375C23 13.0104 22.8551 12.6606 22.5973 12.4028C22.3394 12.1449 21.9897 12 21.625 12Z" fill="#212121"/>
+<path d="M17.5 9.25003H21.625C21.9897 9.25003 22.3394 9.10517 22.5973 8.8473C22.8551 8.58944 23 8.23971 23 7.87503C23 7.51036 22.8551 7.16062 22.5973 6.90276C22.3394 6.6449 21.9897 6.50003 21.625 6.50003H17.5C17.1353 6.50003 16.7856 6.6449 16.5277 6.90276C16.2699 7.16062 16.125 7.51036 16.125 7.87503C16.125 8.23971 16.2699 8.58944 16.5277 8.8473C16.7856 9.10517 17.1353 9.25003 17.5 9.25003Z" fill="#212121"/>
+</svg>

+ 11 - 0
web/app/components/base/icons/assets/vender/solid/development/pattern-recognition.svg

@@ -0,0 +1,11 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.72727 22C4.18787 22 3.66058 21.84 3.21208 21.5404C2.76359 21.2407 2.41402 20.8148 2.2076 20.3164C2.00118 19.8181 1.94717 19.2697 2.05241 18.7407C2.15764 18.2116 2.41739 17.7257 2.7988 17.3443C3.18022 16.9628 3.66617 16.7031 4.19521 16.5979C4.72425 16.4926 5.27261 16.5466 5.77096 16.7531C6.2693 16.9595 6.69524 17.309 6.99492 17.7575C7.2946 18.206 7.45455 18.7333 7.45455 19.2727C7.45455 19.996 7.16721 20.6897 6.65575 21.2012C6.14429 21.7127 5.45059 22 4.72727 22Z" fill="#212121"/>
+<path d="M12 9.27273C11.4606 9.27273 10.9333 9.43268 10.4848 9.73236C10.0363 10.032 9.68675 10.458 9.48033 10.9563C9.27391 11.4547 9.2199 12.003 9.32513 12.5321C9.43036 13.0611 9.69011 13.5471 10.0715 13.9285C10.4529 14.3099 10.9389 14.5696 11.4679 14.6749C11.997 14.7801 12.5453 14.7261 13.0437 14.5197C13.542 14.3133 13.968 13.9637 14.2676 13.5152C14.5673 13.0667 14.7273 12.5394 14.7273 12C14.7273 11.2767 14.4399 10.583 13.9285 10.0715C13.417 9.56006 12.7233 9.27273 12 9.27273Z" fill="#212121"/>
+<path d="M4.72727 2C4.18787 2 3.66058 2.15995 3.21208 2.45963C2.76358 2.7593 2.41402 3.18525 2.2076 3.68359C2.00118 4.18193 1.94717 4.7303 2.05241 5.25934C2.15764 5.78838 2.41738 6.27433 2.7988 6.65575C3.18022 7.03716 3.66617 7.29691 4.19521 7.40214C4.72425 7.50737 5.27261 7.45336 5.77096 7.24694C6.2693 7.04052 6.69524 6.69096 6.99492 6.24246C7.29459 5.79397 7.45455 5.26668 7.45455 4.72727C7.45455 4.00395 7.16721 3.31026 6.65575 2.7988C6.14428 2.28734 5.45059 2 4.72727 2Z" fill="#212121"/>
+<path d="M19.2727 2C18.7333 2 18.206 2.15995 17.7575 2.45963C17.309 2.75931 16.9595 3.18525 16.7531 3.68359C16.5466 4.18194 16.4926 4.7303 16.5979 5.25934C16.7031 5.78838 16.9628 6.27433 17.3443 6.65575C17.7257 7.03716 18.2116 7.29691 18.7407 7.40214C19.2697 7.50737 19.8181 7.45337 20.3164 7.24694C20.8148 7.04052 21.2407 6.69096 21.5404 6.24247C21.84 5.79397 22 5.26668 22 4.72727C22 4.00396 21.7127 3.31026 21.2012 2.7988C20.6897 2.28734 19.996 2 19.2727 2Z" fill="#212121"/>
+<path d="M19.2727 16.5455C18.7333 16.5455 18.206 16.7054 17.7575 17.0051C17.309 17.3048 16.9595 17.7307 16.7531 18.229C16.5466 18.7274 16.4926 19.2758 16.5979 19.8048C16.7031 20.3338 16.9628 20.8198 17.3443 21.2012C17.7257 21.5826 18.2116 21.8424 18.7407 21.9476C19.2697 22.0528 19.8181 21.9988 20.3164 21.7924C20.8148 21.586 21.2407 21.2364 21.5404 20.7879C21.84 20.3394 22 19.8121 22 19.2727C22 18.5494 21.7127 17.8557 21.2012 17.3443C20.6897 16.8328 19.996 16.5455 19.2727 16.5455Z" fill="#212121"/>
+<path d="M7.45455 9.27273H2V14.7273H7.45455V9.27273Z" fill="#212121"/>
+<path d="M22 9.27273H16.5455V14.7273H22V9.27273Z" fill="#212121"/>
+<path d="M14.7273 2H9.27273V7.45455H14.7273V2Z" fill="#212121"/>
+<path d="M14.7273 16.5455H9.27273V22H14.7273V16.5455Z" fill="#212121"/>
+</svg>

+ 6 - 0
web/app/components/base/icons/assets/vender/solid/development/semantic.svg

@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.5833 12.945C16.4856 13.3276 16.2038 14.272 15.7382 15.7784H17.4432C17.0038 14.3674 16.7569 13.5692 16.7025 13.3841C16.6493 13.1998 16.609 13.0532 16.5833 12.945Z" fill="#212121"/>
+<path d="M21.1667 9.33333H12C11.5138 9.33333 11.0475 9.52649 10.7036 9.87031C10.3598 10.2141 10.1667 10.6804 10.1667 11.1667V19.4167C10.1667 19.9029 10.3598 20.3692 10.7036 20.713C11.0475 21.0568 11.5138 21.25 12 21.25H17.5L21.1667 24V21.25C21.6529 21.25 22.1192 21.0568 22.463 20.713C22.8068 20.3692 23 19.9029 23 19.4167V11.1667C23 10.6804 22.8068 10.2141 22.463 9.87031C22.1192 9.52649 21.6529 9.33333 21.1667 9.33333ZM18.2507 18.5L17.775 16.9417H15.3917L14.9159 18.5H13.4208L15.7308 11.9293H17.4267L19.7458 18.5H18.2507Z" fill="#212121"/>
+<path d="M12 2H2.83333C2.3471 2 1.88079 2.19315 1.53697 2.53697C1.19315 2.88079 1 3.3471 1 3.83333V12.0833C1 12.5696 1.19315 13.0359 1.53697 13.3797C1.88079 13.7235 2.3471 13.9167 2.83333 13.9167V16.6667L6.5 13.9167H9.25V11.1667C9.25381 11.0459 9.26606 10.9255 9.28667 10.8064C8.64229 10.5527 8.0315 10.2208 7.468 9.81825C6.5802 10.4316 5.59355 10.8877 4.55117 11.1667C4.394 10.6965 4.15573 10.2575 3.84717 9.86958C4.76378 9.70375 5.64426 9.37861 6.44867 8.90892C6.07755 8.50417 5.75993 8.05346 5.50358 7.56783C5.29175 7.16889 5.12217 6.74892 4.99758 6.31475C4.56583 6.31475 4.3165 6.32942 3.94983 6.35875V5.03233C4.30266 5.0703 4.65741 5.08744 5.01225 5.08367H6.63292V4.64367C6.63379 4.48979 6.61904 4.33623 6.58892 4.18533H8.05833C8.02877 4.33229 8.01403 4.48185 8.01433 4.63175V5.07908H9.756C10.1108 5.08303 10.4656 5.06589 10.8184 5.02775V6.35875C10.4958 6.32942 10.2098 6.31475 9.778 6.31475C9.67623 6.80565 9.51074 7.28115 9.28575 7.72917C9.06864 8.16083 8.79489 8.56159 8.47175 8.92083C8.89523 9.17057 9.34617 9.37051 9.81558 9.51667C10.0695 9.17655 10.399 8.90012 10.7781 8.70922C11.1573 8.51831 11.5755 8.41816 12 8.41667H13.8333V3.83333C13.8333 3.3471 13.6402 2.88079 13.2964 2.53697C12.9525 2.19315 12.4862 2 12 2Z" fill="#212121"/>
+<path d="M7.43133 8.0885C7.87722 7.58102 8.19195 6.97201 8.348 6.31475H6.40833C6.59708 6.98164 6.94861 7.59116 7.43133 8.0885Z" fill="#212121"/>
+</svg>

+ 153 - 0
web/app/components/base/icons/src/public/common/MultiPathRetrieval.json

@@ -0,0 +1,153 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "36",
+			"height": "36",
+			"viewBox": "0 0 36 36",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"clip-path": "url(#clip0_13429_43710)"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "rect",
+						"attributes": {
+							"width": "36",
+							"height": "36",
+							"rx": "8",
+							"fill": "#FFF6ED"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M22.25 28C22.25 29.7949 20.7949 31.25 19 31.25C17.2051 31.25 15.75 29.7949 15.75 28C15.75 26.2051 17.2051 24.75 19 24.75C20.7949 24.75 22.25 26.2051 22.25 28Z",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M19 12C21.2091 12 23 10.2091 23 8C23 5.79086 21.2091 4 19 4C16.7909 4 15 5.79086 15 8C15 10.2091 16.7909 12 19 12Z",
+							"fill": "#FB6514"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M15 22C17.2091 22 19 20.2091 19 18C19 15.7909 17.2091 14 15 14C12.7909 14 11 15.7909 11 18C11 20.2091 12.7909 22 15 22Z",
+							"fill": "#FB6514"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M36 23C38.7614 23 41 20.7614 41 18C41 15.2386 38.7614 13 36 13C33.2386 13 31 15.2386 31 18C31 20.7614 33.2386 23 36 23Z",
+							"fill": "#FB6514"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M0 18H10",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M20 18L30 18",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M0.00112438 15C0.00112438 15 -5.64364 15 0.851673 15C7.34699 15 7.84654 8 14 8",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M23.75 9.28125C26.5688 10.1847 27.699 13.2045 30.625 15.0312",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M-0.000543833 21C-0.000543833 21 -5.57819 21 0.893635 21C7.36546 21 7.8688 28 14 28",
+							"stroke": "#FB6514",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					}
+				]
+			},
+			{
+				"type": "element",
+				"name": "defs",
+				"attributes": {},
+				"children": [
+					{
+						"type": "element",
+						"name": "clipPath",
+						"attributes": {
+							"id": "clip0_13429_43710"
+						},
+						"children": [
+							{
+								"type": "element",
+								"name": "rect",
+								"attributes": {
+									"width": "36",
+									"height": "36",
+									"rx": "8",
+									"fill": "white"
+								},
+								"children": []
+							}
+						]
+					}
+				]
+			}
+		]
+	},
+	"name": "MultiPathRetrieval"
+}

+ 16 - 0
web/app/components/base/icons/src/public/common/MultiPathRetrieval.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './MultiPathRetrieval.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'MultiPathRetrieval'
+
+export default Icon

+ 146 - 0
web/app/components/base/icons/src/public/common/NTo1Retrieval.json

@@ -0,0 +1,146 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "36",
+			"height": "36",
+			"viewBox": "0 0 36 36",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"clip-path": "url(#clip0_13429_43700)"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "rect",
+						"attributes": {
+							"width": "36",
+							"height": "36",
+							"rx": "8",
+							"fill": "#EEF4FF"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M23.25 28C23.25 29.7949 21.7949 31.25 20 31.25C18.2051 31.25 16.75 29.7949 16.75 28C16.75 26.2051 18.2051 24.75 20 24.75C21.7949 24.75 23.25 26.2051 23.25 28Z",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M23.25 8C23.25 9.79493 21.7949 11.25 20 11.25C18.2051 11.25 16.75 9.79493 16.75 8C16.75 6.20507 18.2051 4.75 20 4.75C21.7949 4.75 23.25 6.20507 23.25 8Z",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M16 22C18.2091 22 20 20.2091 20 18C20 15.7909 18.2091 14 16 14C13.7909 14 12 15.7909 12 18C12 20.2091 13.7909 22 16 22Z",
+							"fill": "#444CE7"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M36 23C38.7614 23 41 20.7614 41 18C41 15.2386 38.7614 13 36 13C33.2386 13 31 15.2386 31 18C31 20.7614 33.2386 23 36 23Z",
+							"fill": "#444CE7"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M0 18L11 18",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"d": "M21 18L30 18",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M-0.00160408 15C-0.00160408 15 -6.00089 15 1.12411 15C8.24911 15 8.24908 8.25 14.9991 8.25",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					},
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"opacity": "0.7",
+							"d": "M0.000488281 21C0.000488281 21 -5.92692 21 1.17228 21C8.27148 21 8.27423 27.75 14.9998 27.75",
+							"stroke": "#444CE7",
+							"stroke-width": "1.5"
+						},
+						"children": []
+					}
+				]
+			},
+			{
+				"type": "element",
+				"name": "defs",
+				"attributes": {},
+				"children": [
+					{
+						"type": "element",
+						"name": "clipPath",
+						"attributes": {
+							"id": "clip0_13429_43700"
+						},
+						"children": [
+							{
+								"type": "element",
+								"name": "rect",
+								"attributes": {
+									"width": "36",
+									"height": "36",
+									"rx": "8",
+									"fill": "white"
+								},
+								"children": []
+							}
+						]
+					}
+				]
+			}
+		]
+	},
+	"name": "NTo1Retrieval"
+}

+ 16 - 0
web/app/components/base/icons/src/public/common/NTo1Retrieval.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './NTo1Retrieval.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'NTo1Retrieval'
+
+export default Icon

+ 2 - 0
web/app/components/base/icons/src/public/common/index.ts

@@ -1,4 +1,6 @@
 export { default as Dify } from './Dify'
 export { default as Github } from './Github'
 export { default as MessageChatSquare } from './MessageChatSquare'
+export { default as MultiPathRetrieval } from './MultiPathRetrieval'
+export { default as NTo1Retrieval } from './NTo1Retrieval'
 export { default as Notion } from './Notion'

+ 112 - 0
web/app/components/base/icons/src/public/llm/Cohere.json

@@ -0,0 +1,112 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "22",
+			"height": "22",
+			"viewBox": "0 0 22 22",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"id": "Clip path group"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "mask",
+						"attributes": {
+							"id": "mask0_13224_9519",
+							"style": "mask-type:luminance",
+							"maskUnits": "userSpaceOnUse",
+							"x": "0",
+							"y": "0",
+							"width": "22",
+							"height": "22"
+						},
+						"children": [
+							{
+								"type": "element",
+								"name": "g",
+								"attributes": {
+									"id": "clip0_2207_90691"
+								},
+								"children": [
+									{
+										"type": "element",
+										"name": "path",
+										"attributes": {
+											"id": "Vector",
+											"d": "M21.5 0.5H0.5V21.5H21.5V0.5Z",
+											"fill": "white"
+										},
+										"children": []
+									}
+								]
+							}
+						]
+					},
+					{
+						"type": "element",
+						"name": "g",
+						"attributes": {
+							"mask": "url(#mask0_13224_9519)"
+						},
+						"children": [
+							{
+								"type": "element",
+								"name": "g",
+								"attributes": {
+									"id": "Group"
+								},
+								"children": [
+									{
+										"type": "element",
+										"name": "path",
+										"attributes": {
+											"id": "Vector_2",
+											"fill-rule": "evenodd",
+											"clip-rule": "evenodd",
+											"d": "M7.30367 13.0035C7.8689 13.0035 8.99327 12.9725 10.5474 12.3326C12.3585 11.587 15.9617 10.2334 18.561 8.84305C20.3788 7.8706 21.1757 6.58448 21.1757 4.85248C21.1757 2.44869 19.2271 0.5 16.8233 0.5H6.75176C3.299 0.5 0.5 3.299 0.5 6.75176C0.5 10.2045 3.12069 13.0035 7.30367 13.0035Z",
+											"fill": "#39594D"
+										},
+										"children": []
+									},
+									{
+										"type": "element",
+										"name": "path",
+										"attributes": {
+											"id": "Vector_3",
+											"fill-rule": "evenodd",
+											"clip-rule": "evenodd",
+											"d": "M9.00732 17.3086C9.00732 15.6162 10.0262 14.0902 11.5894 13.4414L14.7612 12.1251C17.9694 10.7936 21.5006 13.1513 21.5006 16.6249C21.5006 19.316 19.3185 21.4974 16.6273 21.4967L13.1933 21.4958C10.8813 21.4952 9.00732 19.6207 9.00732 17.3086Z",
+											"fill": "#D18EE2"
+										},
+										"children": []
+									},
+									{
+										"type": "element",
+										"name": "path",
+										"attributes": {
+											"id": "Vector_4",
+											"d": "M4.10396 13.8277C2.11358 13.8277 0.5 15.4411 0.5 17.4315V17.8984C0.5 19.8887 2.11352 21.5022 4.1039 21.5022C6.09428 21.5022 7.70785 19.8887 7.70785 17.8984V17.4315C7.70785 15.4411 6.09434 13.8277 4.10396 13.8277Z",
+											"fill": "#FF7759"
+										},
+										"children": []
+									}
+								]
+							}
+						]
+					}
+				]
+			}
+		]
+	},
+	"name": "Cohere"
+}

+ 16 - 0
web/app/components/base/icons/src/public/llm/Cohere.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Cohere.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Cohere'
+
+export default Icon

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 17 - 0
web/app/components/base/icons/src/public/llm/CohereText.json


+ 16 - 0
web/app/components/base/icons/src/public/llm/CohereText.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './CohereText.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'CohereText'
+
+export default Icon

+ 2 - 0
web/app/components/base/icons/src/public/llm/index.ts

@@ -8,6 +8,8 @@ export { default as BaichuanText } from './BaichuanText'
 export { default as Baichuan } from './Baichuan'
 export { default as ChatglmText } from './ChatglmText'
 export { default as Chatglm } from './Chatglm'
+export { default as CohereText } from './CohereText'
+export { default as Cohere } from './Cohere'
 export { default as Gpt3 } from './Gpt3'
 export { default as Gpt4 } from './Gpt4'
 export { default as HuggingfaceTextHub } from './HuggingfaceTextHub'

+ 53 - 0
web/app/components/base/icons/src/vender/solid/arrows/HighPriority.json

@@ -0,0 +1,53 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "24",
+			"height": "24",
+			"viewBox": "0 0 24 24",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M9.01488 2.54553C8.91549 2.45869 8.79321 2.40229 8.66264 2.38306C8.53206 2.36384 8.39872 2.38261 8.27852 2.43712C8.15833 2.49164 8.05636 2.5796 7.98481 2.6905C7.91325 2.8014 7.87513 2.93055 7.875 3.06253V6.50003C6.05164 6.50003 4.30295 7.22436 3.01364 8.51367C1.72433 9.80299 1 11.5517 1 13.375C1 15.1984 1.72433 16.9471 3.01364 18.2364C4.30295 19.5257 6.05164 20.25 7.875 20.25H12C12.3647 20.25 12.7144 20.1052 12.9723 19.8473C13.2301 19.5894 13.375 19.2397 13.375 18.875C13.375 18.5104 13.2301 18.1606 12.9723 17.9028C12.7144 17.6449 12.3647 17.5 12 17.5H7.875C6.78098 17.5 5.73177 17.0654 4.95818 16.2919C4.1846 15.5183 3.75 14.4691 3.75 13.375C3.75 12.281 4.1846 11.2318 4.95818 10.4582C5.73177 9.68463 6.78098 9.25003 7.875 9.25003V12.6875C7.87513 12.8195 7.91325 12.9487 7.98481 13.0596C8.05636 13.1705 8.15833 13.2584 8.27852 13.3129C8.39872 13.3675 8.53206 13.3862 8.66264 13.367C8.79321 13.3478 8.91549 13.2914 9.01488 13.2045L14.5149 8.39203C14.5885 8.32751 14.6475 8.24801 14.6879 8.15885C14.7283 8.06969 14.7492 7.97292 14.7492 7.87503C14.7492 7.77714 14.7283 7.68038 14.6879 7.59122C14.6475 7.50206 14.5885 7.42256 14.5149 7.35803L9.01488 2.54553Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M21.625 17.5H17.5C17.1353 17.5 16.7856 17.6449 16.5277 17.9028C16.2699 18.1606 16.125 18.5104 16.125 18.875C16.125 19.2397 16.2699 19.5894 16.5277 19.8473C16.7856 20.1052 17.1353 20.25 17.5 20.25H21.625C21.9897 20.25 22.3394 20.1052 22.5973 19.8473C22.8551 19.5894 23 19.2397 23 18.875C23 18.5104 22.8551 18.1606 22.5973 17.9028C22.3394 17.6449 21.9897 17.5 21.625 17.5Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M21.625 12H17.5C17.1353 12 16.7856 12.1449 16.5277 12.4028C16.2699 12.6606 16.125 13.0104 16.125 13.375C16.125 13.7397 16.2699 14.0894 16.5277 14.3473C16.7856 14.6052 17.1353 14.75 17.5 14.75H21.625C21.9897 14.75 22.3394 14.6052 22.5973 14.3473C22.8551 14.0894 23 13.7397 23 13.375C23 13.0104 22.8551 12.6606 22.5973 12.4028C22.3394 12.1449 21.9897 12 21.625 12Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M17.5 9.25003H21.625C21.9897 9.25003 22.3394 9.10517 22.5973 8.8473C22.8551 8.58944 23 8.23971 23 7.87503C23 7.51036 22.8551 7.16062 22.5973 6.90276C22.3394 6.6449 21.9897 6.50003 21.625 6.50003H17.5C17.1353 6.50003 16.7856 6.6449 16.5277 6.90276C16.2699 7.16062 16.125 7.51036 16.125 7.87503C16.125 8.23971 16.2699 8.58944 16.5277 8.8473C16.7856 9.10517 17.1353 9.25003 17.5 9.25003Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			}
+		]
+	},
+	"name": "HighPriority"
+}

+ 16 - 0
web/app/components/base/icons/src/vender/solid/arrows/HighPriority.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './HighPriority.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'HighPriority'
+
+export default Icon

+ 1 - 0
web/app/components/base/icons/src/vender/solid/arrows/index.ts

@@ -0,0 +1 @@
+export { default as HighPriority } from './HighPriority'

+ 98 - 0
web/app/components/base/icons/src/vender/solid/development/PatternRecognition.json

@@ -0,0 +1,98 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "24",
+			"height": "24",
+			"viewBox": "0 0 24 24",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M4.72727 22C4.18787 22 3.66058 21.84 3.21208 21.5404C2.76359 21.2407 2.41402 20.8148 2.2076 20.3164C2.00118 19.8181 1.94717 19.2697 2.05241 18.7407C2.15764 18.2116 2.41739 17.7257 2.7988 17.3443C3.18022 16.9628 3.66617 16.7031 4.19521 16.5979C4.72425 16.4926 5.27261 16.5466 5.77096 16.7531C6.2693 16.9595 6.69524 17.309 6.99492 17.7575C7.2946 18.206 7.45455 18.7333 7.45455 19.2727C7.45455 19.996 7.16721 20.6897 6.65575 21.2012C6.14429 21.7127 5.45059 22 4.72727 22Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M12 9.27273C11.4606 9.27273 10.9333 9.43268 10.4848 9.73236C10.0363 10.032 9.68675 10.458 9.48033 10.9563C9.27391 11.4547 9.2199 12.003 9.32513 12.5321C9.43036 13.0611 9.69011 13.5471 10.0715 13.9285C10.4529 14.3099 10.9389 14.5696 11.4679 14.6749C11.997 14.7801 12.5453 14.7261 13.0437 14.5197C13.542 14.3133 13.968 13.9637 14.2676 13.5152C14.5673 13.0667 14.7273 12.5394 14.7273 12C14.7273 11.2767 14.4399 10.583 13.9285 10.0715C13.417 9.56006 12.7233 9.27273 12 9.27273Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M4.72727 2C4.18787 2 3.66058 2.15995 3.21208 2.45963C2.76358 2.7593 2.41402 3.18525 2.2076 3.68359C2.00118 4.18193 1.94717 4.7303 2.05241 5.25934C2.15764 5.78838 2.41738 6.27433 2.7988 6.65575C3.18022 7.03716 3.66617 7.29691 4.19521 7.40214C4.72425 7.50737 5.27261 7.45336 5.77096 7.24694C6.2693 7.04052 6.69524 6.69096 6.99492 6.24246C7.29459 5.79397 7.45455 5.26668 7.45455 4.72727C7.45455 4.00395 7.16721 3.31026 6.65575 2.7988C6.14428 2.28734 5.45059 2 4.72727 2Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M19.2727 2C18.7333 2 18.206 2.15995 17.7575 2.45963C17.309 2.75931 16.9595 3.18525 16.7531 3.68359C16.5466 4.18194 16.4926 4.7303 16.5979 5.25934C16.7031 5.78838 16.9628 6.27433 17.3443 6.65575C17.7257 7.03716 18.2116 7.29691 18.7407 7.40214C19.2697 7.50737 19.8181 7.45337 20.3164 7.24694C20.8148 7.04052 21.2407 6.69096 21.5404 6.24247C21.84 5.79397 22 5.26668 22 4.72727C22 4.00396 21.7127 3.31026 21.2012 2.7988C20.6897 2.28734 19.996 2 19.2727 2Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M19.2727 16.5455C18.7333 16.5455 18.206 16.7054 17.7575 17.0051C17.309 17.3048 16.9595 17.7307 16.7531 18.229C16.5466 18.7274 16.4926 19.2758 16.5979 19.8048C16.7031 20.3338 16.9628 20.8198 17.3443 21.2012C17.7257 21.5826 18.2116 21.8424 18.7407 21.9476C19.2697 22.0528 19.8181 21.9988 20.3164 21.7924C20.8148 21.586 21.2407 21.2364 21.5404 20.7879C21.84 20.3394 22 19.8121 22 19.2727C22 18.5494 21.7127 17.8557 21.2012 17.3443C20.6897 16.8328 19.996 16.5455 19.2727 16.5455Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M7.45455 9.27273H2V14.7273H7.45455V9.27273Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M22 9.27273H16.5455V14.7273H22V9.27273Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M14.7273 2H9.27273V7.45455H14.7273V2Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M14.7273 16.5455H9.27273V22H14.7273V16.5455Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			}
+		]
+	},
+	"name": "PatternRecognition"
+}

+ 16 - 0
web/app/components/base/icons/src/vender/solid/development/PatternRecognition.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './PatternRecognition.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'PatternRecognition'
+
+export default Icon

+ 53 - 0
web/app/components/base/icons/src/vender/solid/development/Semantic.json

@@ -0,0 +1,53 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "24",
+			"height": "24",
+			"viewBox": "0 0 24 24",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M16.5833 12.945C16.4856 13.3276 16.2038 14.272 15.7382 15.7784H17.4432C17.0038 14.3674 16.7569 13.5692 16.7025 13.3841C16.6493 13.1998 16.609 13.0532 16.5833 12.945Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M21.1667 9.33333H12C11.5138 9.33333 11.0475 9.52649 10.7036 9.87031C10.3598 10.2141 10.1667 10.6804 10.1667 11.1667V19.4167C10.1667 19.9029 10.3598 20.3692 10.7036 20.713C11.0475 21.0568 11.5138 21.25 12 21.25H17.5L21.1667 24V21.25C21.6529 21.25 22.1192 21.0568 22.463 20.713C22.8068 20.3692 23 19.9029 23 19.4167V11.1667C23 10.6804 22.8068 10.2141 22.463 9.87031C22.1192 9.52649 21.6529 9.33333 21.1667 9.33333ZM18.2507 18.5L17.775 16.9417H15.3917L14.9159 18.5H13.4208L15.7308 11.9293H17.4267L19.7458 18.5H18.2507Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M12 2H2.83333C2.3471 2 1.88079 2.19315 1.53697 2.53697C1.19315 2.88079 1 3.3471 1 3.83333V12.0833C1 12.5696 1.19315 13.0359 1.53697 13.3797C1.88079 13.7235 2.3471 13.9167 2.83333 13.9167V16.6667L6.5 13.9167H9.25V11.1667C9.25381 11.0459 9.26606 10.9255 9.28667 10.8064C8.64229 10.5527 8.0315 10.2208 7.468 9.81825C6.5802 10.4316 5.59355 10.8877 4.55117 11.1667C4.394 10.6965 4.15573 10.2575 3.84717 9.86958C4.76378 9.70375 5.64426 9.37861 6.44867 8.90892C6.07755 8.50417 5.75993 8.05346 5.50358 7.56783C5.29175 7.16889 5.12217 6.74892 4.99758 6.31475C4.56583 6.31475 4.3165 6.32942 3.94983 6.35875V5.03233C4.30266 5.0703 4.65741 5.08744 5.01225 5.08367H6.63292V4.64367C6.63379 4.48979 6.61904 4.33623 6.58892 4.18533H8.05833C8.02877 4.33229 8.01403 4.48185 8.01433 4.63175V5.07908H9.756C10.1108 5.08303 10.4656 5.06589 10.8184 5.02775V6.35875C10.4958 6.32942 10.2098 6.31475 9.778 6.31475C9.67623 6.80565 9.51074 7.28115 9.28575 7.72917C9.06864 8.16083 8.79489 8.56159 8.47175 8.92083C8.89523 9.17057 9.34617 9.37051 9.81558 9.51667C10.0695 9.17655 10.399 8.90012 10.7781 8.70922C11.1573 8.51831 11.5755 8.41816 12 8.41667H13.8333V3.83333C13.8333 3.3471 13.6402 2.88079 13.2964 2.53697C12.9525 2.19315 12.4862 2 12 2Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			},
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M7.43133 8.0885C7.87722 7.58102 8.19195 6.97201 8.348 6.31475H6.40833C6.59708 6.98164 6.94861 7.59116 7.43133 8.0885Z",
+					"fill": "currentColor"
+				},
+				"children": []
+			}
+		]
+	},
+	"name": "Semantic"
+}

+ 16 - 0
web/app/components/base/icons/src/vender/solid/development/Semantic.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Semantic.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Semantic'
+
+export default Icon

+ 2 - 0
web/app/components/base/icons/src/vender/solid/development/index.ts

@@ -1,5 +1,7 @@
 export { default as Container } from './Container'
 export { default as Database02 } from './Database02'
 export { default as Database03 } from './Database03'
+export { default as PatternRecognition } from './PatternRecognition'
 export { default as PuzzlePiece01 } from './PuzzlePiece01'
+export { default as Semantic } from './Semantic'
 export { default as TerminalSquare } from './TerminalSquare'

+ 1 - 1
web/app/components/base/param-item/score-threshold-item.tsx

@@ -41,7 +41,7 @@ const ScoreThresholdItem: FC<Props> = ({
       className={className}
       id={key}
       name={t(`appDebug.datasetConfig.${key}`)}
-      tip={t(`appDebug.datasetConfig.${key}Tip`)}
+      tip={t(`appDebug.datasetConfig.${key}Tip`) as string}
       {...VALUE_LIMIT}
       value={value}
       enable={enable}

+ 1 - 1
web/app/components/base/param-item/top-k-item.tsx

@@ -37,7 +37,7 @@ const TopKItem: FC<Props> = ({
       className={className}
       id={key}
       name={t(`appDebug.datasetConfig.${key}`)}
-      tip={t(`appDebug.datasetConfig.${key}Tip`)}
+      tip={t(`appDebug.datasetConfig.${key}Tip`) as string}
       {...VALUE_LIMIT}
       value={value}
       enable={enable}

+ 55 - 0
web/app/components/base/radio-card/index.tsx

@@ -0,0 +1,55 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import cn from 'classnames'
+import s from './style.module.css'
+
+type Props = {
+  className?: string
+  icon: React.ReactNode
+  iconBgClassName?: string
+  title: React.ReactNode
+  description: string
+  noRadio?: boolean
+  isChosen?: boolean
+  onChosen?: () => void
+  chosenConfig?: React.ReactNode
+  chosenConfigWrapClassName?: string
+}
+
+const RadioCard: FC<Props> = ({
+  icon,
+  iconBgClassName = 'bg-[#F5F3FF]',
+  title,
+  description,
+  noRadio,
+  isChosen,
+  onChosen = () => {},
+  chosenConfig,
+  chosenConfigWrapClassName,
+}) => {
+  return (
+    <div className={cn(s.item, isChosen && s.active)}>
+      <div className='flex py-3 pl-3 pr-4' onClick={onChosen}>
+        <div className={cn(iconBgClassName, 'mr-3 shrink-0 flex w-8 justify-center h-8 items-center rounded-lg')}>
+          {icon}
+        </div>
+        <div className='grow'>
+          <div className='leading-5 text-sm font-medium text-gray-900'>{title}</div>
+          <div className='leading-[18px] text-xs font-normal text-[#667085]'>{description}</div>
+        </div>
+        {!noRadio && (
+          <div className='shrink-0 flex items-center h-8'>
+            <div className={s.radio}></div>
+          </div>
+        )}
+      </div>
+      {((isChosen && chosenConfig) || noRadio) && (
+        <div className={cn(chosenConfigWrapClassName, 'pt-2 px-14 pb-6 border-t border-gray-200')}>
+          {chosenConfig}
+        </div>
+      )}
+    </div>
+  )
+}
+export default React.memo(RadioCard)

+ 40 - 0
web/app/components/base/radio-card/simple/index.tsx

@@ -0,0 +1,40 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import cn from 'classnames'
+import s from './style.module.css'
+
+type Props = {
+  className?: string
+  title: string
+  description: string
+  isChosen: boolean
+  onChosen: () => void
+  chosenConfig?: React.ReactNode
+  icon?: JSX.Element
+}
+
+const RadioCard: FC<Props> = ({
+  title,
+  description,
+  isChosen,
+  onChosen,
+  icon,
+}) => {
+  return (
+    <div
+      className={cn(s.item, isChosen && s.active, 'flex')}
+      onClick={onChosen}
+    >
+      {icon}
+      <div>
+        <div className='flex justify-between items-center'>
+          <div className='leading-5 text-sm font-medium text-gray-900'>{title}</div>
+          <div className={s.radio}></div>
+        </div>
+        <div className='leading-[18px] text-xs font-normal text-gray-500'>{description}</div>
+      </div>
+    </div>
+  )
+}
+export default React.memo(RadioCard)

+ 25 - 0
web/app/components/base/radio-card/simple/style.module.css

@@ -0,0 +1,25 @@
+.item {
+  @apply relative p-4 rounded-xl border border-gray-100 cursor-pointer;
+  background-color: #fcfcfd;
+}
+
+.item.active {
+  border-width: 1.5px;
+  border-color: #528BFF;
+  box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
+}
+
+.item:hover {
+  background-color: #ffffff;
+  border-color: #B2CCFF;
+  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
+}
+
+.radio {
+  @apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
+}
+
+.item.active .radio {
+  border-width: 5px;
+  border-color: #155EEF;
+}

+ 25 - 0
web/app/components/base/radio-card/style.module.css

@@ -0,0 +1,25 @@
+.item {
+  @apply relative rounded-xl border border-gray-100 cursor-pointer;
+  background-color: #fcfcfd;
+}
+
+.item.active {
+  border-width: 1.5px;
+  border-color: #528BFF;
+  box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
+}
+
+.item:hover {
+  background-color: #ffffff;
+  border-color: #B2CCFF;
+  box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
+}
+
+.radio {
+  @apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
+}
+
+.item.active .radio {
+  border-width: 5px;
+  border-color: #155EEF;
+}

+ 1 - 0
web/app/components/base/slider/index.tsx

@@ -1,6 +1,7 @@
 import ReactSlider from 'react-slider'
 import cn from 'classnames'
 import './style.css'
+
 type ISliderProps = {
   className?: string
   value: number

+ 50 - 0
web/app/components/datasets/common/check-rerank-model.ts

@@ -0,0 +1,50 @@
+import type { BackendModel } from '../../header/account-setting/model-page/declarations'
+import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
+
+export const isReRankModelSelected = ({
+  rerankDefaultModel,
+  isRerankDefaultModelVaild,
+  retrievalConfig,
+  indexMethod,
+}: {
+  rerankDefaultModel?: BackendModel
+  isRerankDefaultModelVaild: boolean
+  retrievalConfig: RetrievalConfig
+  indexMethod?: string
+}) => {
+  const rerankModel = (retrievalConfig.reranking_model?.reranking_model_name ? retrievalConfig.reranking_model : undefined) || (isRerankDefaultModelVaild ? rerankDefaultModel : undefined)
+  if (
+    indexMethod === 'high_quality'
+    && (retrievalConfig.reranking_enable || retrievalConfig.search_method === RETRIEVE_METHOD.fullText)
+    && !rerankModel
+  )
+    return false
+
+  return true
+}
+
+export const ensureRerankModelSelected = ({
+  rerankDefaultModel,
+  indexMethod,
+  retrievalConfig,
+}: {
+  rerankDefaultModel: BackendModel
+  retrievalConfig: RetrievalConfig
+  indexMethod?: string
+}) => {
+  const rerankModel = retrievalConfig.reranking_model?.reranking_model_name ? retrievalConfig.reranking_model : undefined
+  if (
+    indexMethod === 'high_quality'
+    && (retrievalConfig.reranking_enable || retrievalConfig.search_method === RETRIEVE_METHOD.fullText)
+    && !rerankModel
+  ) {
+    return {
+      ...retrievalConfig,
+      reranking_model: {
+        reranking_provider_name: rerankDefaultModel.model_provider.provider_name,
+        reranking_model_name: rerankDefaultModel.model_name,
+      },
+    }
+  }
+  return retrievalConfig
+}

+ 40 - 0
web/app/components/datasets/common/economical-retrieval-method-config/index.tsx

@@ -0,0 +1,40 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import RetrievalParamConfig from '../retrieval-param-config'
+import { RETRIEVE_METHOD } from '@/types/app'
+import RadioCard from '@/app/components/base/radio-card'
+import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
+import type { RetrievalConfig } from '@/types/app'
+
+type Props = {
+  value: RetrievalConfig
+  onChange: (value: RetrievalConfig) => void
+}
+
+const EconomicalRetrievalMethodConfig: FC<Props> = ({
+  value,
+  onChange,
+}) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='space-y-2'>
+      <RadioCard
+        icon={<HighPriority className='w-4 h-4 text-[#7839EE]' />}
+        title={t('dataset.retrieval.invertedIndex.title')}
+        description={t('dataset.retrieval.invertedIndex.description')}
+        noRadio
+        chosenConfig={
+          <RetrievalParamConfig
+            type={RETRIEVE_METHOD.invertedIndex}
+            value={value}
+            onChange={onChange}
+          />
+        }
+      />
+    </div>
+  )
+}
+export default React.memo(EconomicalRetrievalMethodConfig)

+ 91 - 0
web/app/components/datasets/common/retrieval-method-config/index.tsx

@@ -0,0 +1,91 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import RetrievalParamConfig from '../retrieval-param-config'
+import type { RetrievalConfig } from '@/types/app'
+import { RETRIEVE_METHOD } from '@/types/app'
+import RadioCard from '@/app/components/base/radio-card'
+import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
+import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
+import { useProviderContext } from '@/context/provider-context'
+
+type Props = {
+  value: RetrievalConfig
+  onChange: (value: RetrievalConfig) => void
+}
+
+const RetrievalMethodConfig: FC<Props> = ({
+  value,
+  onChange,
+}) => {
+  const { t } = useTranslation()
+  const { supportRetrievalMethods } = useProviderContext()
+  return (
+    <div className='space-y-2'>
+      {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
+        <RadioCard
+          icon={<Semantic className='w-4 h-4 text-[#7839EE]' />}
+          title={t('dataset.retrieval.semantic_search.title')}
+          description={t('dataset.retrieval.semantic_search.description')}
+          isChosen={value.search_method === RETRIEVE_METHOD.semantic}
+          onChosen={() => onChange({
+            ...value,
+            search_method: RETRIEVE_METHOD.semantic,
+          })}
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.semantic}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
+      )}
+      {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
+        <RadioCard
+          icon={<FileSearch02 className='w-4 h-4 text-[#7839EE]' />}
+          title={t('dataset.retrieval.full_text_search.title')}
+          description={t('dataset.retrieval.full_text_search.description')}
+          isChosen={value.search_method === RETRIEVE_METHOD.fullText}
+          onChosen={() => onChange({
+            ...value,
+            search_method: RETRIEVE_METHOD.fullText,
+          })}
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.fullText}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
+      )}
+      {supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
+        <RadioCard
+          icon={<PatternRecognition className='w-4 h-4 text-[#7839EE]' />}
+          title={
+            <div className='flex items-center space-x-1'>
+              <div>{t('dataset.retrieval.hybrid_search.title')}</div>
+              <div className='flex h-full items-center px-1.5 rounded-md border border-[#E0EAFF] text-xs font-medium text-[#444CE7]'>{t('dataset.retrieval.hybrid_search.recommend')}</div>
+            </div>
+          }
+          description={t('dataset.retrieval.hybrid_search.description')}
+          isChosen={value.search_method === RETRIEVE_METHOD.hybrid}
+          onChosen={() => onChange({
+            ...value,
+            search_method: RETRIEVE_METHOD.hybrid,
+          })}
+          chosenConfig={
+            <RetrievalParamConfig
+              type={RETRIEVE_METHOD.hybrid}
+              value={value}
+              onChange={onChange}
+            />
+          }
+        />
+      )}
+    </div>
+  )
+}
+export default React.memo(RetrievalMethodConfig)

+ 64 - 0
web/app/components/datasets/common/retrieval-method-info/index.tsx

@@ -0,0 +1,64 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import type { RetrievalConfig } from '@/types/app'
+import { RETRIEVE_METHOD } from '@/types/app'
+import RadioCard from '@/app/components/base/radio-card'
+import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
+import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
+import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
+
+type Props = {
+  value: RetrievalConfig
+}
+
+export const getIcon = (type: RETRIEVE_METHOD) => {
+  return ({
+    [RETRIEVE_METHOD.semantic]: Semantic,
+    [RETRIEVE_METHOD.fullText]: FileSearch02,
+    [RETRIEVE_METHOD.hybrid]: PatternRecognition,
+    [RETRIEVE_METHOD.invertedIndex]: HighPriority,
+  })[type] || FileSearch02
+}
+
+const EconomicalRetrievalMethodConfig: FC<Props> = ({
+  // type,
+  value,
+}) => {
+  const { t } = useTranslation()
+  const type = value.search_method
+  const Icon = getIcon(type)
+  return (
+    <div className='space-y-2'>
+      <RadioCard
+        icon={<Icon className='w-4 h-4 text-[#7839EE]' />}
+        title={t(`dataset.retrieval.${type}.title`)}
+        description={t(`dataset.retrieval.${type}.description`)}
+        noRadio
+        chosenConfigWrapClassName='!pb-3'
+        chosenConfig={
+          <div className='flex flex-wrap leading-[18px] text-xs font-normal'>
+            {value.reranking_model.reranking_model_name && (
+              <div className='mr-8 flex space-x-1'>
+                <div className='text-gray-500'>{t('common.modelProvider.rerankModel.key')}</div>
+                <div className='font-medium text-gray-800'>{value.reranking_model.reranking_model_name}</div>
+              </div>
+            )}
+
+            <div className='mr-8 flex space-x-1'>
+              <div className='text-gray-500'>{t('appDebug.datasetConfig.top_k')}</div>
+              <div className='font-medium text-gray-800'>{value.top_k}</div>
+            </div>
+
+            <div className='mr-8 flex space-x-1'>
+              <div className='text-gray-500'>{t('appDebug.datasetConfig.score_threshold')}</div>
+              <div className='font-medium text-gray-800'>{value.score_threshold}</div>
+            </div>
+          </div>
+        }
+      />
+    </div>
+  )
+}
+export default React.memo(EconomicalRetrievalMethodConfig)

+ 131 - 0
web/app/components/datasets/common/retrieval-param-config/index.tsx

@@ -0,0 +1,131 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import cn from 'classnames'
+import TopKItem from '@/app/components/base/param-item/top-k-item'
+import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
+import { RETRIEVE_METHOD } from '@/types/app'
+import Switch from '@/app/components/base/switch'
+import Tooltip from '@/app/components/base/tooltip-plus'
+import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
+import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
+import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
+import type { RetrievalConfig } from '@/types/app'
+import { useProviderContext } from '@/context/provider-context'
+
+type Props = {
+  type: RETRIEVE_METHOD
+  value: RetrievalConfig
+  onChange: (value: RetrievalConfig) => void
+}
+
+const RetrievalParamConfig: FC<Props> = ({
+  type,
+  value,
+  onChange,
+}) => {
+  const { t } = useTranslation()
+  const canToggleRerankModalEnable = type !== RETRIEVE_METHOD.hybrid
+  const isEconomical = type === RETRIEVE_METHOD.invertedIndex
+  const {
+    rerankDefaultModel,
+  } = useProviderContext()
+
+  const rerankModel = (() => {
+    if (value.reranking_model) {
+      return {
+        provider_name: value.reranking_model.reranking_provider_name,
+        model_name: value.reranking_model.reranking_model_name,
+      }
+    }
+    else if (rerankDefaultModel) {
+      return {
+        provider_name: rerankDefaultModel.model_provider.provider_name,
+        model_name: rerankDefaultModel.model_name,
+      }
+    }
+  })()
+
+  return (
+    <div>
+      {!isEconomical && (
+        <div>
+          <div className='flex h-8 items-center text-[13px] font-medium text-gray-900 space-x-2'>
+            {canToggleRerankModalEnable && (
+              <Switch
+                size='md'
+                defaultValue={value.reranking_enable}
+                onChange={(v) => {
+                  onChange({
+                    ...value,
+                    reranking_enable: v,
+                  })
+                }}
+              />
+            )}
+            <div className='flex items-center'>
+              <span className='mr-0.5'>{t('common.modelProvider.rerankModel.key')}</span>
+              <Tooltip popupContent={<div className="w-[200px]">{t('common.modelProvider.rerankModel.tip')}</div>}>
+                <HelpCircle className='w-[14px] h-[14px] text-gray-400' />
+              </Tooltip>
+            </div>
+          </div>
+          <div>
+            <ModelSelector
+              whenEmptyGoToSetting
+              popClassName='!max-w-[100%] !w-full'
+              value={rerankModel && { providerName: rerankModel.provider_name, modelName: rerankModel.model_name } as any}
+              modelType={ModelType.reranking}
+              readonly={!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid}
+              onChange={(v) => {
+                onChange({
+                  ...value,
+                  reranking_model: {
+                    reranking_provider_name: v.model_provider.provider_name,
+                    reranking_model_name: v.model_name,
+                  },
+                })
+              }}
+            />
+          </div>
+        </div>
+      )}
+
+      <div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-6')}>
+        <TopKItem
+          className='grow'
+          value={value.top_k}
+          onChange={(_key, v) => {
+            onChange({
+              ...value,
+              top_k: v,
+            })
+          }}
+          enable={true}
+        />
+        {(!isEconomical && !(value.search_method === RETRIEVE_METHOD.fullText && !value.reranking_enable)) && (
+          <ScoreThresholdItem
+            className='grow'
+            value={value.score_threshold}
+            onChange={(_key, v) => {
+              onChange({
+                ...value,
+                score_threshold: v,
+              })
+            }}
+            enable={value.score_threshold_enable}
+            hasSwitch={true}
+            onSwitchChange={(_key, v) => {
+              onChange({
+                ...value,
+                score_threshold_enable: v,
+              })
+            }}
+          />
+        )}
+      </div>
+    </div>
+  )
+}
+export default React.memo(RetrievalParamConfig)

+ 100 - 6
web/app/components/datasets/create/step-two/index.tsx

@@ -7,6 +7,7 @@ import { XMarkIcon } from '@heroicons/react/20/solid'
 import cn from 'classnames'
 import Link from 'next/link'
 import { groupBy } from 'lodash-es'
+import RetrievalMethodInfo from '../../common/retrieval-method-info'
 import PreviewItem, { PreviewType } from './preview-item'
 import LanguageSelect from './language-select'
 import s from './index.module.css'
@@ -19,7 +20,10 @@ import {
 } from '@/service/datasets'
 import Button from '@/app/components/base/button'
 import Loading from '@/app/components/base/loading'
-
+import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
+import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
+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'
@@ -31,6 +35,8 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general'
 import { useDatasetDetailContext } from '@/context/dataset-detail'
 import I18n from '@/context/i18n'
 import { IS_CE_EDITION } from '@/config'
+import { RETRIEVE_METHOD } from '@/types/app'
+import { useProviderContext } from '@/context/provider-context'
 
 type ValueOf<T> = T[keyof T]
 type StepTwoProps = {
@@ -78,7 +84,7 @@ const StepTwo = ({
   const { t } = useTranslation()
   const { locale } = useContext(I18n)
 
-  const { mutateDatasetRes } = useDatasetDetailContext()
+  const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
   const scrollRef = useRef<HTMLDivElement>(null)
   const [scrolled, setScrolled] = useState(false)
   const previewScrollRef = useRef<HTMLDivElement>(null)
@@ -254,7 +260,10 @@ const StepTwo = ({
       }
     }
   }
-
+  const {
+    rerankDefaultModel,
+    isRerankDefaultModelVaild,
+  } = useProviderContext()
   const getCreationParams = () => {
     let params
     if (isSetting) {
@@ -263,9 +272,30 @@ const StepTwo = ({
         doc_form: docForm,
         doc_language: docLanguage,
         process_rule: getProcessRule(),
+        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        retrieval_model: retrievalConfig, // Readonly. If want to changed, just go to settings page.
       } as CreateDocumentReq
     }
-    else {
+    else { // create
+      const indexMethod = getIndexing_technique()
+      if (
+        !isReRankModelSelected({
+          rerankDefaultModel,
+          isRerankDefaultModelVaild,
+          // eslint-disable-next-line @typescript-eslint/no-use-before-define
+          retrievalConfig,
+          indexMethod: indexMethod as string,
+        })
+      ) {
+        Toast.notify({ type: 'error', message: t('appDebug.datasetConfig.rerankModelRequired') })
+        return
+      }
+      const postRetrievalConfig = ensureRerankModelSelected({
+        rerankDefaultModel: rerankDefaultModel!,
+        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        retrievalConfig,
+        indexMethod: indexMethod as string,
+      })
       params = {
         data_source: {
           type: dataSourceType,
@@ -277,6 +307,8 @@ const StepTwo = ({
         process_rule: getProcessRule(),
         doc_form: docForm,
         doc_language: docLanguage,
+
+        retrieval_model: postRetrievalConfig,
       } as CreateDocumentReq
       if (dataSourceType === DataSourceType.FILE) {
         params.data_source.info_list.file_info_list = {
@@ -330,7 +362,7 @@ const StepTwo = ({
       setIsCreating(true)
       if (!datasetId) {
         res = await createFirstDocument({
-          body: params,
+          body: params as CreateDocumentReq,
         })
         updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
         updateResultCache && updateResultCache(res)
@@ -338,7 +370,7 @@ const StepTwo = ({
       else {
         res = await createDocument({
           datasetId,
-          body: params,
+          body: params as CreateDocumentReq,
         })
         updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
         updateResultCache && updateResultCache(res)
@@ -441,6 +473,18 @@ const StepTwo = ({
     }
   }, [segmentationType, indexType])
 
+  const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict || {
+    search_method: RETRIEVE_METHOD.semantic,
+    reranking_enable: false,
+    reranking_model: {
+      reranking_provider_name: rerankDefaultModel?.model_provider.provider_name,
+      reranking_model_name: rerankDefaultModel?.model_name,
+    },
+    top_k: 3,
+    score_threshold_enable: false,
+    score_threshold: 0.5,
+  } as RetrievalConfig)
+
   return (
     <div className='flex w-full h-full'>
       <div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
@@ -626,6 +670,56 @@ const StepTwo = ({
                 )}
               </div>
             )}
+            {/* Retrieval Method Config */}
+            <div>
+              {!datasetId
+                ? (
+                  <div className={s.label}>
+                    {t('datasetSettings.form.retrievalSetting.title')}
+                    <div className='leading-[18px] text-xs font-normal text-gray-500'>
+                      <a target='_blank' href='https://docs.dify.ai/v/zh-hans/advanced/retrieval-augment' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
+                      {t('datasetSettings.form.retrievalSetting.longDescription')}
+                    </div>
+                  </div>
+                )
+                : (
+                  <div className={cn(s.label, 'flex justify-between items-center')}>
+                    <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+                  </div>
+                )}
+
+              <div className='max-w-[640px]'>
+                {!datasetId
+                  ? (<>
+                    {getIndexing_technique() === IndexingType.QUALIFIED
+                      ? (
+                        <RetrievalMethodConfig
+                          value={retrievalConfig}
+                          onChange={setRetrievalConfig}
+                        />
+                      )
+                      : (
+                        <EconomicalRetrievalMethodConfig
+                          value={retrievalConfig}
+                          onChange={setRetrievalConfig}
+                        />
+                      )}
+                  </>)
+                  : (
+                    <div>
+                      <RetrievalMethodInfo
+                        value={retrievalConfig}
+                      />
+                      <div className='mt-2 text-xs text-gray-500 font-medium'>
+                        {t('datasetCreation.stepTwo.retrivalSettedTip')}
+                        <Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
+                      </div>
+                    </div>
+                  )}
+
+              </div>
+            </div>
+
             <div className={s.source}>
               <div className={s.sourceContent}>
                 {dataSourceType === DataSourceType.FILE && (

+ 10 - 4
web/app/components/datasets/documents/detail/completed/SegmentCard.tsx

@@ -141,10 +141,16 @@ const SegmentCard: FC<ISegmentCardProps> = ({
                 )}
             </div>
           </>
-          : <div className={s.hitTitleWrapper}>
-            <div className={cn(s.commonIcon, s.targetIcon, loading ? '!bg-gray-300' : '', '!w-3.5 !h-3.5')} />
-            <ProgressBar percent={score ?? 0} loading={loading} />
-          </div>}
+          : (
+            score !== null
+              ? (
+                <div className={s.hitTitleWrapper}>
+                  <div className={cn(s.commonIcon, s.targetIcon, loading ? '!bg-gray-300' : '', '!w-3.5 !h-3.5')} />
+                  <ProgressBar percent={score ?? 0} loading={loading} />
+                </div>
+              )
+              : null
+          )}
       </div>
       {loading
         ? (

+ 24 - 0
web/app/components/datasets/hit-testing/index.tsx

@@ -6,16 +6,20 @@ import useSWR from 'swr'
 import { omit } from 'lodash-es'
 import cn from 'classnames'
 import dayjs from 'dayjs'
+import { useContext } from 'use-context-selector'
 import SegmentCard from '../documents/detail/completed/SegmentCard'
 import docStyle from '../documents/detail/completed/style.module.css'
 import Textarea from './textarea'
 import s from './style.module.css'
 import HitDetail from './hit-detail'
+import ModifyRetrievalModal from './modify-retrieval-modal'
 import type { HitTestingResponse, HitTesting as HitTestingType } from '@/models/datasets'
 import Loading from '@/app/components/base/loading'
 import Modal from '@/app/components/base/modal'
 import Pagination from '@/app/components/base/pagination'
 import { fetchTestingRecords } from '@/service/datasets'
+import DatasetDetailContext from '@/context/dataset-detail'
+import type { RetrievalConfig } from '@/types/app'
 
 const limit = 10
 
@@ -55,6 +59,11 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
     setCurrParagraph({ paraInfo: detail, showModal: true })
   }
 
+  const { dataset: currentDataset } = useContext(DatasetDetailContext)
+
+  const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
+  const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
+
   return (
     <div className={s.container}>
       <div className={s.leftDiv}>
@@ -70,6 +79,9 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
           setLoading={setSubmitLoading}
           setText={setText}
           text={text}
+          onClickRetrievalMethod={() => setIsShowModifyRetrievalModal(true)}
+          retrievalConfig={retrievalConfig}
+          isEconomy={currentDataset?.indexing_technique === 'economy'}
         />
         <div className={cn(s.title, 'mt-8 mb-2')}>{t('datasetHitTesting.recents')}</div>
         {(!recordsRes && !error)
@@ -178,6 +190,18 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
           }}
         />}
       </Modal>
+      {isShowModifyRetrievalModal && (
+        <ModifyRetrievalModal
+          indexMethod={currentDataset?.indexing_technique || ''}
+          value={retrievalConfig}
+          isShow={isShowModifyRetrievalModal}
+          onHide={() => setIsShowModifyRetrievalModal(false)}
+          onSave={(value) => {
+            setRetrievalConfig(value)
+            setIsShowModifyRetrievalModal(false)
+          }}
+        />
+      )}
     </div>
   )
 }

+ 95 - 0
web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx

@@ -0,0 +1,95 @@
+'use client'
+import type { FC } from 'react'
+import React, { useRef, useState } from 'react'
+import { useClickAway } from 'ahooks'
+import { useTranslation } from 'react-i18next'
+import { XClose } from '@/app/components/base/icons/src/vender/line/general'
+import type { RetrievalConfig } from '@/types/app'
+import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
+import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
+import Button from '@/app/components/base/button'
+
+type Props = {
+  indexMethod: string
+  value: RetrievalConfig
+  isShow: boolean
+  onHide: () => void
+  onSave: (value: RetrievalConfig) => void
+}
+
+const ModifyRetrievalModal: FC<Props> = ({
+  indexMethod,
+  value,
+  isShow,
+  onHide,
+  onSave,
+}) => {
+  const ref = useRef(null)
+  const { t } = useTranslation()
+  const [retrievalConfig, setRetrievalConfig] = useState(value)
+
+  useClickAway(() => {
+    if (ref)
+      onHide()
+  }, ref)
+
+  if (!isShow)
+    return null
+
+  return (
+    <div
+      className='fixed top-16 right-2 flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10'
+      style={{
+        width: 632,
+        height: 'calc(100vh - 72px)',
+      }}
+      ref={ref}
+    >
+      <div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
+        <div className='text-base font-semibold text-gray-900'>
+          <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+          <div className='leading-[18px] text-xs font-normal text-gray-500'>
+            <a target='_blank' href='https://docs.dify.ai/v/zh-hans/advanced/retrieval-augment' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
+            {t('datasetSettings.form.retrievalSetting.description')}
+          </div>
+        </div>
+        <div className='flex items-center'>
+          <div
+            onClick={onHide}
+            className='flex justify-center items-center w-6 h-6 cursor-pointer'
+          >
+            <XClose className='w-4 h-4 text-gray-500' />
+          </div>
+        </div>
+      </div>
+
+      <div className='p-6 border-b' style={{
+        borderBottom: 'rgba(0, 0, 0, 0.05)',
+      }}>
+        {indexMethod === 'high_quality'
+          ? (
+            <RetrievalMethodConfig
+              value={retrievalConfig}
+              onChange={setRetrievalConfig}
+            />
+          )
+          : (
+            <EconomicalRetrievalMethodConfig
+              value={retrievalConfig}
+              onChange={setRetrievalConfig}
+            />
+          )}
+      </div>
+      <div
+        className='flex justify-end pt-6 px-6 border-t'
+        style={{
+          borderColor: 'rgba(0, 0, 0, 0.05)',
+        }}
+      >
+        <Button className='mr-2 flex-shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
+        <Button type='primary' className='flex-shrink-0' onClick={() => onSave(retrievalConfig)} >{t('common.operation.save')}</Button>
+      </div>
+    </div>
+  )
+}
+export default React.memo(ModifyRetrievalModal)

+ 3 - 3
web/app/components/datasets/hit-testing/style.module.css

@@ -20,8 +20,8 @@
   @apply text-sm font-normal text-gray-500;
 }
 .textarea {
-  min-height: 96px;
-  @apply border-none resize-none font-normal caret-primary-600 text-gray-700 text-sm w-full bg-gray-25 focus-visible:outline-none placeholder:text-gray-300 placeholder:text-sm placeholder:font-normal !important;
+  height: 220px;
+  @apply border-none resize-none font-normal caret-primary-600 text-gray-700 text-sm w-full  focus-visible:outline-none placeholder:text-gray-300 placeholder:text-sm placeholder:font-normal !important;
 }
 .table {
   @apply text-[13px] text-gray-500;
@@ -46,7 +46,7 @@
 }
 
 .wrapper {
-  @apply relative border border-primary-600 min-h-[200px] rounded-xl pt-3 pb-14 px-4 bg-gray-25;
+  @apply relative border border-primary-600 rounded-xl;
 }
 
 .cardWrapper {

+ 105 - 79
web/app/components/datasets/hit-testing/textarea.tsx

@@ -1,26 +1,30 @@
-import type { FC } from "react";
+import type { FC } from 'react'
 import { useContext } from 'use-context-selector'
-import { DocumentTextIcon } from "@heroicons/react/24/solid";
-import { useTranslation } from "react-i18next";
-import { hitTesting } from "@/service/datasets";
+import { useTranslation } from 'react-i18next'
+import cn from 'classnames'
+import Button from '../../base/button'
+import Tag from '../../base/tag'
+import Tooltip from '../../base/tooltip'
+import { getIcon } from '../common/retrieval-method-info'
+import s from './style.module.css'
 import DatasetDetailContext from '@/context/dataset-detail'
-import { HitTestingResponse } from "@/models/datasets";
-import cn from "classnames";
-import Button from "../../base/button";
-import Tag from "../../base/tag";
-import Tooltip from "../../base/tooltip";
-import s from "./style.module.css";
-import { asyncRunSafe } from "@/utils";
+import type { HitTestingResponse } from '@/models/datasets'
+import { hitTesting } from '@/service/datasets'
+import { asyncRunSafe } from '@/utils'
+import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
 
 type Props = {
-  datasetId: string;
-  onUpdateList: () => void;
-  setHitResult: (res: HitTestingResponse) => void;
-  loading: boolean;
-  setLoading: (v: boolean) => void;
-  text: string;
-  setText: (v: string) => void;
-};
+  datasetId: string
+  onUpdateList: () => void
+  setHitResult: (res: HitTestingResponse) => void
+  loading: boolean
+  setLoading: (v: boolean) => void
+  text: string
+  setText: (v: string) => void
+  onClickRetrievalMethod: () => void
+  retrievalConfig: RetrievalConfig
+  isEconomy: boolean
+}
 
 const TextAreaWithButton: FC<Props> = ({
   datasetId,
@@ -30,87 +34,109 @@ const TextAreaWithButton: FC<Props> = ({
   loading,
   text,
   setText,
+  onClickRetrievalMethod,
+  retrievalConfig,
+  isEconomy,
 }) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation()
   const { indexingTechnique } = useContext(DatasetDetailContext)
 
-  // 处理文本框内容变化的函数
   function handleTextChange(event: any) {
-    setText(event.target.value);
+    setText(event.target.value)
   }
 
-  // 处理按钮点击的函数
   const onSubmit = async () => {
-    setLoading(true);
+    setLoading(true)
     const [e, res] = await asyncRunSafe<HitTestingResponse>(
-      hitTesting({ datasetId, queryText: text }) as Promise<HitTestingResponse>
-    );
+      hitTesting({ datasetId, queryText: text, retrieval_model: retrievalConfig }) as Promise<HitTestingResponse>,
+    )
     if (!e) {
-      setHitResult(res);
-      onUpdateList?.();
+      setHitResult(res)
+      onUpdateList?.()
     }
-    setLoading(false);
-  };
+    setLoading(false)
+  }
 
+  const retrievalMethod = isEconomy ? RETRIEVE_METHOD.invertedIndex : retrievalConfig.search_method
+  const Icon = getIcon(retrievalMethod)
   return (
     <>
       <div className={s.wrapper}>
-        <div className="flex items-center mb-3">
-          <DocumentTextIcon className="w-4 h-4 text-primary-600 mr-2" />
-          <span className="text-gray-800 font-semibold text-sm">
-            {t("datasetHitTesting.input.title")}
-          </span>
-        </div>
-        <textarea
-          value={text}
-          onChange={handleTextChange}
-          placeholder={t("datasetHitTesting.input.placeholder") as string}
-          className={s.textarea}
-        />
-        <div className="absolute inset-x-0 bottom-0 flex items-center justify-between mx-4 mt-2 mb-4">
-          {text?.length > 200 ? (
+        <div className='pt-2 rounded-tl-xl rounded-tr-xl bg-[#EEF4FF]'>
+          <div className="px-4 pb-2 flex justify-between h-8 items-center">
+            <span className="text-gray-800 font-semibold text-sm">
+              {t('datasetHitTesting.input.title')}
+            </span>
             <Tooltip
-              content={t("datasetHitTesting.input.countWarning") as string}
-              selector="hit-testing-warning"
+              selector={'change-retrieval-method'}
+              htmlContent={t('dataset.retrieval.changeRetrievalMethod')}
             >
-              <div>
-                <Tag color="red" className="!text-red-600">
-                  {text?.length}
-                  <span className="text-red-300 mx-0.5">/</span>
+              <div
+                onClick={onClickRetrievalMethod}
+                className='flex px-2 h-7 items-center space-x-1 bg-white hover:bg-[#ECE9FE] rounded-md shadow-sm cursor-pointer text-[#6927DA]'
+              >
+                <Icon className='w-3.5 h-3.5'></Icon>
+                <div className='text-xs font-medium'>{t(`dataset.retrieval.${retrievalMethod}.title`)}</div>
+              </div>
+            </Tooltip>
+          </div>
+          <div className='h-2 rounded-tl-xl rounded-tr-xl bg-white'></div>
+        </div>
+        <div className='px-4 pb-11'>
+          <textarea
+            value={text}
+            onChange={handleTextChange}
+            placeholder={t('datasetHitTesting.input.placeholder') as string}
+            className={s.textarea}
+          />
+          <div className="absolute inset-x-0 bottom-0 flex items-center justify-between mx-4 mt-2 mb-2">
+            {text?.length > 200
+              ? (
+                <Tooltip
+                  content={t('datasetHitTesting.input.countWarning') as string}
+                  selector="hit-testing-warning"
+                >
+                  <div>
+                    <Tag color="red" className="!text-red-600">
+                      {text?.length}
+                      <span className="text-red-300 mx-0.5">/</span>
                   200
+                    </Tag>
+                  </div>
+                </Tooltip>
+              )
+              : (
+                <Tag
+                  color="gray"
+                  className={cn('!text-gray-500', text?.length ? '' : 'opacity-50')}
+                >
+                  {text?.length}
+                  <span className="text-gray-300 mx-0.5">/</span>
+              200
                 </Tag>
+              )}
+            <Tooltip
+              selector="hit-testing-submit"
+              disabled={indexingTechnique === 'high_quality'}
+              content={t('datasetHitTesting.input.indexWarning') as string}
+            >
+              <div>
+                <Button
+                  onClick={onSubmit}
+                  type="primary"
+                  loading={loading}
+                  disabled={indexingTechnique !== 'high_quality' ? true : (!text?.length || text?.length > 200)}
+                >
+                  {t('datasetHitTesting.input.testing')}
+                </Button>
               </div>
             </Tooltip>
-          ) : (
-            <Tag
-              color="gray"
-              className={cn("!text-gray-500", text?.length ? "" : "opacity-50")}
-            >
-              {text?.length}
-              <span className="text-gray-300 mx-0.5">/</span>
-              200
-            </Tag>
-          )}
-          <Tooltip
-            selector="hit-testing-submit"
-            disabled={indexingTechnique === 'high_quality'}
-            content={t("datasetHitTesting.input.indexWarning") as string}
-          >
-            <div>
-              <Button
-                onClick={onSubmit}
-                type="primary"
-                loading={loading}
-                disabled={indexingTechnique !== 'high_quality' ? true : (!text?.length || text?.length > 200)}
-              >
-                {t("datasetHitTesting.input.testing")}
-              </Button>
-            </div>
-          </Tooltip>
+          </div>
         </div>
+
       </div>
     </>
-  );
-};
+  )
+}
 
-export default TextAreaWithButton;
+export default TextAreaWithButton

+ 55 - 1
web/app/components/datasets/settings/form/index.tsx

@@ -9,6 +9,8 @@ import { useSWRConfig } from 'swr'
 import { unstable_serialize } from 'swr/infinite'
 import PermissionsRadio from '../permissions-radio'
 import IndexMethodRadio from '../index-method-radio'
+import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
+import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
 import { ToastContext } from '@/app/components/base/toast'
 import Button from '@/app/components/base/button'
 import { updateDatasetSetting } from '@/service/datasets'
@@ -17,8 +19,10 @@ import ModelSelector from '@/app/components/header/account-setting/model-page/mo
 import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
 import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
 import DatasetDetailContext from '@/context/dataset-detail'
+import { type RetrievalConfig } from '@/types/app'
 import { useModalContext } from '@/context/modal-context'
-
+import { useProviderContext } from '@/context/provider-context'
+import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
 const rowClass = `
   flex justify-between py-4
 `
@@ -51,6 +55,12 @@ const Form = () => {
   const [description, setDescription] = useState(currentDataset?.description ?? '')
   const [permission, setPermission] = useState(currentDataset?.permission)
   const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique)
+  const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
+  const {
+    rerankDefaultModel,
+    isRerankDefaultModelVaild,
+  } = useProviderContext()
+
   const handleSave = async () => {
     if (loading)
       return
@@ -58,6 +68,22 @@ const Form = () => {
       notify({ type: 'error', message: t('datasetSettings.form.nameError') })
       return
     }
+    if (
+      !isReRankModelSelected({
+        rerankDefaultModel,
+        isRerankDefaultModelVaild,
+        retrievalConfig,
+        indexMethod,
+      })
+    ) {
+      notify({ type: 'error', message: t('appDebug.datasetConfig.rerankModelRequired') })
+      return
+    }
+    const postRetrievalConfig = ensureRerankModelSelected({
+      rerankDefaultModel: rerankDefaultModel!,
+      retrievalConfig,
+      indexMethod,
+    })
     try {
       setLoading(true)
       await updateDatasetSetting({
@@ -67,6 +93,7 @@ const Form = () => {
           description,
           permission,
           indexing_technique: indexMethod,
+          retrieval_model: postRetrievalConfig,
         },
       })
       notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
@@ -172,6 +199,33 @@ const Form = () => {
           </div>
         </div>
       )}
+      {/* Retrieval Method Config */}
+      <div className={rowClass}>
+        <div className={labelClass}>
+          <div>
+            <div>{t('datasetSettings.form.retrievalSetting.title')}</div>
+            <div className='leading-[18px] text-xs font-normal text-gray-500'>
+              <a target='_blank' href='https://docs.dify.ai/v/zh-hans/advanced/retrieval-augment' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
+              {t('datasetSettings.form.retrievalSetting.description')}
+            </div>
+          </div>
+        </div>
+        <div className='w-[480px]'>
+          {indexMethod === 'high_quality'
+            ? (
+              <RetrievalMethodConfig
+                value={retrievalConfig}
+                onChange={setRetrievalConfig}
+              />
+            )
+            : (
+              <EconomicalRetrievalMethodConfig
+                value={retrievalConfig}
+                onChange={setRetrievalConfig}
+              />
+            )}
+        </div>
+      </div>
       {currentDataset?.embedding_available && (
         <div className={rowClass}>
           <div className={labelClass} />

+ 3 - 0
web/app/components/datasets/settings/permissions-radio/index.tsx

@@ -13,12 +13,14 @@ const radioClass = `
 type IPermissionsRadioProps = {
   value?: DataSet['permission']
   onChange: (v?: DataSet['permission']) => void
+  itemClassName?: string
   disable?: boolean
 }
 
 const PermissionsRadio = ({
   value,
   onChange,
+  itemClassName,
   disable,
 }: IPermissionsRadioProps) => {
   const { t } = useTranslation()
@@ -41,6 +43,7 @@ const PermissionsRadio = ({
             key={option.key}
             className={classNames(
               itemClass,
+              itemClassName,
               s.item,
               option.key === value && s['item-active'],
               disable && s.disable,

+ 53 - 0
web/app/components/header/account-setting/model-page/configs/cohere.tsx

@@ -0,0 +1,53 @@
+import { ProviderEnum } from '../declarations'
+import type { ProviderConfig } from '../declarations'
+import { Cohere, CohereText } from '@/app/components/base/icons/src/public/llm'
+
+const config: ProviderConfig = {
+  selector: {
+    name: {
+      'en': 'cohere',
+      'zh-Hans': 'cohere',
+    },
+    icon: <Cohere className='w-full h-full' />,
+  },
+  item: {
+    key: ProviderEnum.cohere,
+    titleIcon: {
+      'en': <CohereText className='w-[120px] h-6' />,
+      'zh-Hans': <CohereText className='w-[120px] h-6' />,
+    },
+  },
+  modal: {
+    key: ProviderEnum.cohere,
+    title: {
+      'en': 'cohere',
+      'zh-Hans': 'cohere',
+    },
+    icon: <Cohere className='w-6 h-6' />,
+    link: {
+      href: 'https://dashboard.cohere.com/api-keys',
+      label: {
+        'en': 'Get your API key from cohere',
+        'zh-Hans': '从 cohere 获取 API Key',
+      },
+    },
+    validateKeys: ['api_key'],
+    fields: [
+      {
+        type: 'text',
+        key: 'api_key',
+        required: true,
+        label: {
+          'en': 'API Key',
+          'zh-Hans': 'API Key',
+        },
+        placeholder: {
+          'en': 'Enter your API key here',
+          'zh-Hans': '在此输入您的 API Key',
+        },
+      },
+    ],
+  },
+}
+
+export default config

+ 2 - 0
web/app/components/header/account-setting/model-page/configs/index.ts

@@ -13,6 +13,7 @@ import openllm from './openllm'
 import localai from './localai'
 import zhipuai from './zhipuai'
 import baichuan from './baichuan'
+import cohere from './cohere'
 
 export default {
   openai,
@@ -30,4 +31,5 @@ export default {
   localai,
   zhipuai,
   baichuan,
+  cohere,
 }

+ 2 - 0
web/app/components/header/account-setting/model-page/declarations.ts

@@ -45,6 +45,7 @@ export enum ProviderEnum {
   'localai' = 'localai',
   'zhipuai' = 'zhipuai',
   'baichuan' = 'baichuan',
+  'cohere' = 'cohere',
 }
 
 export type ProviderConfigItem = {
@@ -67,6 +68,7 @@ export enum ModelType {
   textGeneration = 'text-generation',
   embeddings = 'embeddings',
   speech2text = 'speech2text',
+  reranking = 'reranking',
 }
 
 export enum ModelFeature {

+ 7 - 104
web/app/components/header/account-setting/model-page/index.tsx

@@ -3,33 +3,28 @@ import useSWR from 'swr'
 import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
 import type {
-  BackendModel,
   FormValue,
   ProviderConfigModal,
   ProviderEnum,
 } from './declarations'
-import ModelSelector from './model-selector'
 import ModelCard from './model-card'
 import ModelItem from './model-item'
 import ModelModal from './model-modal'
+import SystemModel from './system-model'
 import config from './configs'
 import { ConfigurableProviders } from './utils'
-import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
 import {
   changeModelProviderPriority,
   deleteModelProvider,
   deleteModelProviderModel,
-  fetchDefaultModal,
   fetchModelProviders,
   setModelProvider,
-  updateDefaultModel,
 } from '@/service/common'
 import { useToastContext } from '@/app/components/base/toast'
 import Confirm from '@/app/components/base/confirm/common'
 import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
 import { useEventEmitterContextContext } from '@/context/event-emitter'
 import { useProviderContext } from '@/context/provider-context'
-import Tooltip from '@/app/components/base/tooltip'
 import I18n from '@/context/i18n'
 
 const MODEL_CARD_LIST = [
@@ -37,13 +32,6 @@ const MODEL_CARD_LIST = [
   config.anthropic,
 ]
 
-const titleClassName = `
-flex items-center h-9 text-sm font-medium text-gray-900
-`
-const tipClassName = `
-ml-0.5 w-[14px] h-[14px] text-gray-400
-`
-
 type DeleteModel = {
   model_name: string
   model_type: string
@@ -54,13 +42,8 @@ const ModelPage = () => {
   const { locale } = useContext(I18n)
   const {
     updateModelList,
-    embeddingsDefaultModel,
-    mutateEmbeddingsDefaultModel,
-    speech2textDefaultModel,
-    mutateSpeech2textDefaultModel,
   } = useProviderContext()
   const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
-  const { data: textGenerationDefaultModel, mutate: mutateTextGenerationDefaultModel } = useSWR('/workspaces/current/default-model?model_type=text-generation', fetchDefaultModal)
   const [showModal, setShowModal] = useState(false)
   const { notify } = useToastContext()
   const { eventEmitter } = useEventEmitterContextContext()
@@ -76,6 +59,7 @@ const ModelPage = () => {
       config.azure_openai,
       config.replicate,
       config.huggingface_hub,
+      config.cohere,
       config.zhipuai,
       config.baichuan,
       config.spark,
@@ -91,6 +75,7 @@ const ModelPage = () => {
   else {
     modelList = [
       config.huggingface_hub,
+      config.cohere,
       config.zhipuai,
       config.spark,
       config.baichuan,
@@ -127,6 +112,7 @@ const ModelPage = () => {
     updateModelList(ModelType.textGeneration)
     updateModelList(ModelType.embeddings)
     updateModelList(ModelType.speech2text)
+    updateModelList(ModelType.reranking)
     mutateProviders()
   }
   const handleSave = async (originValue?: FormValue) => {
@@ -210,95 +196,12 @@ const ModelPage = () => {
     }
   }
 
-  const mutateDefaultModel = (type: ModelType) => {
-    if (type === ModelType.textGeneration)
-      mutateTextGenerationDefaultModel()
-    if (type === ModelType.embeddings)
-      mutateEmbeddingsDefaultModel()
-    if (type === ModelType.speech2text)
-      mutateSpeech2textDefaultModel()
-  }
-  const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => {
-    const res = await updateDefaultModel({
-      url: '/workspaces/current/default-model',
-      body: {
-        model_type: type,
-        provider_name: v.model_provider.provider_name,
-        model_name: v.model_name,
-      },
-    })
-    if (res.result === 'success') {
-      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
-      mutateDefaultModel(type)
-    }
-  }
-
   return (
     <div className='relative pt-1 -mt-2'>
-      <div className='grid grid-cols-3 gap-4 mb-5'>
-        <div className='w-full'>
-          <div className={titleClassName}>
-            {t('common.modelProvider.systemReasoningModel.key')}
-            <Tooltip
-              selector='model-page-system-reasoning-model-tip'
-              htmlContent={
-                <div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
-              }
-            >
-              <HelpCircle className={tipClassName} />
-            </Tooltip>
-          </div>
-          <div>
-            <ModelSelector
-              value={textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name }}
-              modelType={ModelType.textGeneration}
-              onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
-            />
-          </div>
-        </div>
-        <div className='w-full'>
-          <div className={titleClassName}>
-            {t('common.modelProvider.embeddingModel.key')}
-            <Tooltip
-              selector='model-page-system-embedding-model-tip'
-              htmlContent={
-                <div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
-              }
-            >
-              <HelpCircle className={tipClassName} />
-            </Tooltip>
-          </div>
-          <div>
-            <ModelSelector
-              value={embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name }}
-              modelType={ModelType.embeddings}
-              onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
-            />
-          </div>
-        </div>
-        <div className='w-full'>
-          <div className={titleClassName}>
-            {t('common.modelProvider.speechToTextModel.key')}
-            <Tooltip
-              selector='model-page-system-speechToText-model-tip'
-              htmlContent={
-                <div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
-              }
-            >
-              <HelpCircle className={tipClassName} />
-            </Tooltip>
-          </div>
-          <div>
-            <ModelSelector
-              value={speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name }}
-              modelType={ModelType.speech2text}
-              onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
-            />
-          </div>
-        </div>
+      <div className='flex items-center justify-between mb-2 h-8'>
+        <div className='text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
+        <SystemModel />
       </div>
-      <div className='mb-5 h-[0.5px] bg-gray-100' />
-      <div className='mb-3 text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
       <div className='grid grid-cols-2 gap-4 mb-6'>
         {
           MODEL_CARD_LIST.map((model, index) => (

+ 28 - 4
web/app/components/header/account-setting/model-page/model-selector/index.tsx

@@ -34,6 +34,7 @@ type Props = {
   popClassName?: string
   readonly?: boolean
   triggerIconSmall?: boolean
+  whenEmptyGoToSetting?: boolean
 }
 
 type ModelOption = {
@@ -57,10 +58,17 @@ const ModelSelector: FC<Props> = ({
   popClassName,
   readonly,
   triggerIconSmall,
+  whenEmptyGoToSetting,
 }) => {
   const { t } = useTranslation()
   const { setShowAccountSettingModal } = useModalContext()
-  const { textGenerationModelList, embeddingsModelList, speech2textModelList, agentThoughtModelList } = useProviderContext()
+  const {
+    textGenerationModelList,
+    embeddingsModelList,
+    speech2textModelList,
+    rerankModelList,
+    agentThoughtModelList,
+  } = useProviderContext()
   const [search, setSearch] = useState('')
   const modelList = supportAgentThought
     ? agentThoughtModelList
@@ -68,6 +76,7 @@ const ModelSelector: FC<Props> = ({
       [ModelType.textGeneration]: textGenerationModelList,
       [ModelType.embeddings]: embeddingsModelList,
       [ModelType.speech2text]: speech2textModelList,
+      [ModelType.reranking]: rerankModelList,
     })[modelType]
   const currModel = modelList.find(item => item.model_name === value?.modelName && item.model_provider.provider_name === value.providerName)
   const allModelNames = (() => {
@@ -116,7 +125,7 @@ const ModelSelector: FC<Props> = ({
   return (
     <div className=''>
       <Popover className='relative'>
-        <Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
+        <Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto bg-gray-100 opacity-50' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
           {
             ({ open }) => (
               <>
@@ -130,7 +139,7 @@ const ModelSelector: FC<Props> = ({
                           providerName={value.providerName}
                         />
                         <div className='mr-1.5 grow flex items-center text-left text-sm text-gray-900 truncate'>
-                          <ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name} />
+                          <ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name || value.modelName} />
                           {isShowModelModeType && (
                             <ModelModeTypeLabel className='ml-2' type={currModel?.model_mode as ModelModeType} />
                           )}
@@ -237,7 +246,22 @@ const ModelSelector: FC<Props> = ({
                   return null
                 })
               }
-              {(search && filteredModelList.length === 0) && (
+              {
+                whenEmptyGoToSetting && modelList.length === 0 && (
+                  <div className='pt-6'>
+                    <div className='flex items-center justify-center mx-auto mb-2 w-12 h-12 rounded-[10px] border border-[#EAECF5]'>
+                      <CubeOutline className='w-6 h-6 text-gray-500' />
+                    </div>
+                    <div className='mb-1 text-center text-[13px] font-medium text-gray-500'>
+                      {t('common.modelProvider.selector.emptyTip')}
+                    </div>
+                    <div className='mb-6 text-center text-xs text-primary-500'>
+                      <span onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('common.modelProvider.selector.emptySetting')}</span>
+                    </div>
+                  </div>
+                )
+              }
+              {modelList.length !== 0 && (search && filteredModelList.length === 0) && (
                 <div className='px-3 pt-1.5 h-[30px] text-center text-xs text-gray-500'>{t('common.modelProvider.noModelFound', { model: search })}</div>
               )}
 

+ 205 - 0
web/app/components/header/account-setting/model-page/system-model/index.tsx

@@ -0,0 +1,205 @@
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import ModelSelector from '../model-selector'
+import type {
+  BackendModel, ProviderEnum,
+} from '../declarations'
+import Tooltip from '@/app/components/base/tooltip'
+import { HelpCircle, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
+import {
+  PortalToFollowElem,
+  PortalToFollowElemContent,
+  PortalToFollowElemTrigger,
+} from '@/app/components/base/portal-to-follow-elem'
+import { useProviderContext } from '@/context/provider-context'
+import { updateDefaultModel } from '@/service/common'
+import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
+import { useToastContext } from '@/app/components/base/toast'
+import Button from '@/app/components/base/button'
+
+const SystemModel = () => {
+  const { t } = useTranslation()
+  const {
+    textGenerationDefaultModel,
+    mutateTextGenerationDefaultModel,
+    embeddingsDefaultModel,
+    mutateEmbeddingsDefaultModel,
+    speech2textDefaultModel,
+    mutateSpeech2textDefaultModel,
+    rerankDefaultModel,
+    mutateRerankDefaultModel,
+  } = useProviderContext()
+  const { notify } = useToastContext()
+  const [open, setOpen] = useState(false)
+  const [selectedModel, setSelectedModel] = useState<Record<ModelType, { providerName: ProviderEnum; modelName: string } | undefined>>({
+    [ModelType.textGeneration]: textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name },
+    [ModelType.embeddings]: embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name },
+    [ModelType.speech2text]: speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name },
+    [ModelType.reranking]: rerankDefaultModel && { providerName: rerankDefaultModel.model_provider.provider_name, modelName: rerankDefaultModel.model_name },
+  })
+
+  const mutateDefaultModel = (types: ModelType[]) => {
+    types.forEach((type) => {
+      if (type === ModelType.textGeneration)
+        mutateTextGenerationDefaultModel()
+      if (type === ModelType.embeddings)
+        mutateEmbeddingsDefaultModel()
+      if (type === ModelType.speech2text)
+        mutateSpeech2textDefaultModel()
+      if (type === ModelType.reranking)
+        mutateRerankDefaultModel()
+    })
+  }
+  const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => {
+    setSelectedModel({
+      ...selectedModel,
+      [type]: {
+        providerName: v.model_provider.provider_name,
+        modelName: v.model_name,
+      },
+    })
+  }
+  const handleSave = async () => {
+    const kesArray = Object.keys(selectedModel) as ModelType[]
+    const res = await updateDefaultModel({
+      url: '/workspaces/current/default-model',
+      body: {
+        model_settings: kesArray.map((key) => {
+          return {
+            model_type: key,
+            provider_name: selectedModel?.[key]?.providerName,
+            model_name: selectedModel?.[key]?.modelName,
+          }
+        }),
+      },
+    })
+    if (res.result === 'success') {
+      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
+      mutateDefaultModel(kesArray)
+    }
+  }
+
+  return (
+    <PortalToFollowElem
+      open={open}
+      onOpenChange={setOpen}
+      placement='bottom-end'
+      offset={{
+        mainAxis: 4,
+        crossAxis: 8,
+      }}
+    >
+      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
+        <div className={`
+          flex items-center px-2 h-6 text-xs text-gray-700 cursor-pointer rounded-md border-[0.5px] border-gray-200 shadow-xs
+          hover:bg-gray-100 hover:shadow-none
+          ${open && 'bg-gray-100 shadow-none'}
+        `}>
+          <Settings01 className='mr-1 w-3 h-3 text-gray-500' />
+          {t('common.modelProvider.systemModelSettings')}
+        </div>
+      </PortalToFollowElemTrigger>
+      <PortalToFollowElemContent className='z-50'>
+        <div className='pt-4 w-[360px] rounded-xl border-[0.5px] border-black/5 bg-white shadow-xl'>
+          <div className='px-6 py-1'>
+            <div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
+              {t('common.modelProvider.systemReasoningModel.key')}
+              <Tooltip
+                selector='model-page-system-reasoning-model-tip'
+                htmlContent={
+                  <div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
+                }
+              >
+                <HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
+              </Tooltip>
+            </div>
+            <div>
+              <ModelSelector
+                value={selectedModel[ModelType.textGeneration]}
+                modelType={ModelType.textGeneration}
+                onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
+              />
+            </div>
+          </div>
+          <div className='px-6 py-1'>
+            <div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
+              {t('common.modelProvider.embeddingModel.key')}
+              <Tooltip
+                selector='model-page-system-embedding-model-tip'
+                htmlContent={
+                  <div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
+                }
+              >
+                <HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
+              </Tooltip>
+            </div>
+            <div>
+              <ModelSelector
+                value={selectedModel[ModelType.embeddings]}
+                modelType={ModelType.embeddings}
+                onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
+              />
+            </div>
+          </div>
+          <div className='px-6 py-1'>
+            <div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
+              {t('common.modelProvider.rerankModel.key')}
+              <Tooltip
+                selector='model-page-system-rerankModel-model-tip'
+                htmlContent={
+                  <div className='w-[261px] text-gray-500'>{t('common.modelProvider.rerankModel.tip')}</div>
+                }
+              >
+                <HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
+              </Tooltip>
+            </div>
+            <div>
+              <ModelSelector
+                value={selectedModel[ModelType.reranking]}
+                modelType={ModelType.reranking}
+                onChange={v => handleChangeDefaultModel(ModelType.reranking, v)}
+              />
+            </div>
+          </div>
+          <div className='px-6 py-1'>
+            <div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
+              {t('common.modelProvider.speechToTextModel.key')}
+              <Tooltip
+                selector='model-page-system-speechToText-model-tip'
+                htmlContent={
+                  <div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
+                }
+              >
+                <HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
+              </Tooltip>
+            </div>
+            <div>
+              <ModelSelector
+                value={selectedModel[ModelType.speech2text]}
+                modelType={ModelType.speech2text}
+                onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
+              />
+            </div>
+          </div>
+          <div className='flex items-center justify-end px-6 py-4'>
+            <Button
+              className='mr-2 !h-8 !text-[13px]'
+              onClick={() => setOpen(false)}
+            >
+              {t('common.operation.cancel')}
+            </Button>
+            <Button
+              type='primary'
+              className='!h-8 !text-[13px]'
+              onClick={handleSave}
+            >
+              {t('common.operation.save')}
+            </Button>
+          </div>
+        </div>
+      </PortalToFollowElemContent>
+    </PortalToFollowElem>
+  )
+}
+
+export default SystemModel

+ 5 - 0
web/config/index.ts

@@ -141,3 +141,8 @@ export const VAR_ITEM_TEMPLATE = {
 export const appDefaultIconBackground = '#D5F5F6'
 
 export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
+
+export const DATASET_DEFAULT = {
+  top_k: 2,
+  score_threshold: 0.5,
+}

+ 8 - 5
web/context/debug-configuration.ts

@@ -20,7 +20,7 @@ import type {
 import type { ExternalDataTool } from '@/models/common'
 import type { DataSet } from '@/models/datasets'
 import type { VisionSettings } from '@/types/app'
-import { ModelModeType, Resolution, TransferMethod } from '@/types/app'
+import { ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
 import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
 
 type IDebugConfiguration = {
@@ -180,11 +180,14 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
   showSelectDataSet: () => { },
   setDataSets: () => { },
   datasetConfigs: {
-    top_k: 2,
-    score_threshold: {
-      enable: false,
-      value: 0.7,
+    retrieval_model: RETRIEVE_TYPE.oneWay,
+    reranking_model: {
+      reranking_provider_name: '',
+      reranking_model_name: '',
     },
+    top_k: 2,
+    score_threshold_enabled: false,
+    score_threshold: 0.7,
   },
   setDatasetConfigs: () => {},
   hasSetContextVar: false,

+ 37 - 2
web/context/provider-context.tsx

@@ -2,29 +2,44 @@
 
 import { createContext, useContext } from 'use-context-selector'
 import useSWR from 'swr'
-import { fetchDefaultModal, fetchModelList } from '@/service/common'
+import { fetchDefaultModal, fetchModelList, fetchSupportRetrievalMethods } from '@/service/common'
 import { ModelFeature, ModelType } from '@/app/components/header/account-setting/model-page/declarations'
 import type { BackendModel } from '@/app/components/header/account-setting/model-page/declarations'
+import type { RETRIEVE_METHOD } from '@/types/app'
 const ProviderContext = createContext<{
   textGenerationModelList: BackendModel[]
   embeddingsModelList: BackendModel[]
   speech2textModelList: BackendModel[]
+  rerankModelList: BackendModel[]
   agentThoughtModelList: BackendModel[]
   updateModelList: (type: ModelType) => void
+  textGenerationDefaultModel?: BackendModel
+  mutateTextGenerationDefaultModel: () => void
   embeddingsDefaultModel?: BackendModel
   mutateEmbeddingsDefaultModel: () => void
   speech2textDefaultModel?: BackendModel
   mutateSpeech2textDefaultModel: () => void
+  rerankDefaultModel?: BackendModel
+  isRerankDefaultModelVaild: boolean
+  mutateRerankDefaultModel: () => void
+  supportRetrievalMethods: RETRIEVE_METHOD[]
 }>({
       textGenerationModelList: [],
       embeddingsModelList: [],
       speech2textModelList: [],
+      rerankModelList: [],
       agentThoughtModelList: [],
       updateModelList: () => {},
+      textGenerationDefaultModel: undefined,
+      mutateTextGenerationDefaultModel: () => {},
       speech2textDefaultModel: undefined,
       mutateSpeech2textDefaultModel: () => {},
       embeddingsDefaultModel: undefined,
       mutateEmbeddingsDefaultModel: () => {},
+      rerankDefaultModel: undefined,
+      isRerankDefaultModelVaild: false,
+      mutateRerankDefaultModel: () => {},
+      supportRetrievalMethods: [],
     })
 
 export const useProviderContext = () => useContext(ProviderContext)
@@ -35,21 +50,34 @@ type ProviderContextProviderProps = {
 export const ProviderContextProvider = ({
   children,
 }: ProviderContextProviderProps) => {
+  const { data: textGenerationDefaultModel, mutate: mutateTextGenerationDefaultModel } = useSWR('/workspaces/current/default-model?model_type=text-generation', fetchDefaultModal)
   const { data: embeddingsDefaultModel, mutate: mutateEmbeddingsDefaultModel } = useSWR('/workspaces/current/default-model?model_type=embeddings', fetchDefaultModal)
   const { data: speech2textDefaultModel, mutate: mutateSpeech2textDefaultModel } = useSWR('/workspaces/current/default-model?model_type=speech2text', fetchDefaultModal)
+  const { data: rerankDefaultModel, mutate: mutateRerankDefaultModel } = useSWR('/workspaces/current/default-model?model_type=reranking', fetchDefaultModal)
   const fetchModelListUrlPrefix = '/workspaces/current/models/model-type/'
   const { data: textGenerationModelList, mutate: mutateTextGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelType.textGeneration}`, fetchModelList)
   const { data: embeddingsModelList, mutate: mutateEmbeddingsModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelType.embeddings}`, fetchModelList)
-  const { data: speech2textModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelType.speech2text}`, fetchModelList)
+  const { data: speech2textModelList, mutate: mutateSpeech2textModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelType.speech2text}`, fetchModelList)
+  const { data: rerankModelList, mutate: mutateRerankModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelType.reranking}`, fetchModelList)
+  const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods)
+
   const agentThoughtModelList = textGenerationModelList?.filter((item) => {
     return item.features?.includes(ModelFeature.agentThought)
   })
 
+  const isRerankDefaultModelVaild = !!rerankModelList?.find(
+    item => item.model_name === rerankDefaultModel?.model_name && item.model_provider.provider_name === rerankDefaultModel?.model_provider.provider_name,
+  )
+
   const updateModelList = (type: ModelType) => {
     if (type === ModelType.textGeneration)
       mutateTextGenerationModelList()
     if (type === ModelType.embeddings)
       mutateEmbeddingsModelList()
+    if (type === ModelType.speech2text)
+      mutateSpeech2textModelList()
+    if (type === ModelType.reranking)
+      mutateRerankModelList()
   }
 
   return (
@@ -57,12 +85,19 @@ export const ProviderContextProvider = ({
       textGenerationModelList: textGenerationModelList || [],
       embeddingsModelList: embeddingsModelList || [],
       speech2textModelList: speech2textModelList || [],
+      rerankModelList: rerankModelList || [],
       agentThoughtModelList: agentThoughtModelList || [],
       updateModelList,
+      textGenerationDefaultModel,
+      mutateTextGenerationDefaultModel,
       embeddingsDefaultModel,
       mutateEmbeddingsDefaultModel,
       speech2textDefaultModel,
       mutateSpeech2textDefaultModel,
+      rerankDefaultModel,
+      isRerankDefaultModelVaild,
+      mutateRerankDefaultModel,
+      supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [],
     }}>
       {children}
     </ProviderContext.Provider>

+ 11 - 0
web/i18n/lang/app-debug.en.ts

@@ -305,11 +305,22 @@ const translation = {
   },
   result: 'Output Text',
   datasetConfig: {
+    settingTitle: 'Retrieve Settings',
+    retrieveOneWay: {
+      title: 'N-to-1 retrieval',
+      description: 'Based on user intent and dataset descriptions, the Agent autonomously selects the best dataset for querying. Best for applications with distinct, limited datasets.',
+    },
+    retrieveMultiWay: {
+      title: 'Multi-path retrieval',
+      description: 'Based on user intent, queries across all datasets, retrieves relevant text from multi-sources, and selects the best results matching the user query after reranking. Configuration of the Rerank model API is required.',
+    },
+    rerankModelRequired: 'Rerank model is required',
     params: 'Params',
     top_k: 'Top K',
     top_kTip: 'Used to filter segments that are most similar to user questions. The system will also dynamically adjust the value of Top K, according to max_tokens of the selected model.',
     score_threshold: 'Score Threshold',
     score_thresholdTip: 'Used to set the similarity threshold for segment filtering.',
+    retrieveChangeTip: 'Modifying the index mode and retrieval mode may affect applications associated with this dataset.',
   },
 }
 

+ 11 - 0
web/i18n/lang/app-debug.zh.ts

@@ -299,11 +299,22 @@ const translation = {
   },
   result: '结果',
   datasetConfig: {
+    settingTitle: '召回设置',
+    retrieveOneWay: {
+      title: 'N选1召回',
+      description: '根据用户意图和数据集描述,由 Agent 自主判断选择最匹配的单个数据集来查询相关文本,适合数据集区分度大且数据集数量偏少的应用。',
+    },
+    retrieveMultiWay: {
+      title: '多路召回',
+      description: '根据用户意图同时匹配所有数据集,从多路数据集查询相关文本片段,经过重排序步骤,从多路查询结果中选择匹配用户问题的最佳结果,需配置 Rerank 模型 API。',
+    },
+    rerankModelRequired: '请选择 Rerank 模型',
     params: '参数设置',
     top_k: 'Top K',
     top_kTip: '用于筛选与用户问题相似度最高的文本片段。系统同时会根据选用模型上下文窗口大小动态调整分段数量。',
     score_threshold: 'Score 阈值',
     score_thresholdTip: '用于设置文本片段筛选的相似度阈值。',
+    retrieveChangeTip: '修改索引模式和检索模式可能会影响与该数据集关联的应用程序。',
   },
 }
 

+ 11 - 3
web/i18n/lang/common.en.ts

@@ -223,6 +223,8 @@ const translation = {
     },
   },
   modelProvider: {
+    systemModelSettings: 'System Model Settings',
+    systemModelSettingsLink: 'Why is it necessary to set up a system model?',
     selectModel: 'Select your model',
     setupModelFirst: 'Please set up your model first',
     systemReasoningModel: {
@@ -237,6 +239,10 @@ const translation = {
       key: 'Speech-to-Text Model',
       tip: 'Set the default model for speech-to-text input in conversation.',
     },
+    rerankModel: {
+      key: 'Rerank Model',
+      tip: 'Rerank model will reorder the candidate document list based on the semantic match with  user query, improving the results of semantic ranking',
+    },
     quota: 'Quota',
     searchModel: 'Search model',
     noModelFound: 'No model found for {{model}}',
@@ -244,6 +250,8 @@ const translation = {
     showMoreModelProvider: 'Show more model provider',
     selector: {
       tip: 'This model has been removed. Please add a model or select another model.',
+      emptyTip: 'No available models',
+      emptySetting: 'Please go to settings to configure',
     },
     card: {
       quota: 'QUOTA',
@@ -351,7 +359,7 @@ const translation = {
   },
   datasetMenus: {
     documents: 'Documents',
-    hitTesting: 'Hit Testing',
+    hitTesting: 'Retrieval Testing',
     settings: 'Settings',
     emptyTip: 'The data set has not been associated, please go to the application or plug-in to complete the association.',
     viewDoc: 'View documentation',
@@ -382,9 +390,9 @@ const translation = {
       title: 'CITATIONS',
       linkToDataset: 'Link to dataset',
       characters: 'Characters:',
-      hitCount: 'Hit count:',
+      hitCount: 'Retrieval count:',
       vectorHash: 'Vector hash:',
-      hitScore: 'Hit Score:',
+      hitScore: 'Retrieval Score:',
     },
   },
   promptEditor: {

+ 11 - 3
web/i18n/lang/common.zh.ts

@@ -223,6 +223,8 @@ const translation = {
     },
   },
   modelProvider: {
+    systemModelSettings: '系统模型设置',
+    systemModelSettingsLink: '为什么需要设置系统模型?',
     selectModel: '选择您的模型',
     setupModelFirst: '请先设置您的模型',
     systemReasoningModel: {
@@ -237,6 +239,10 @@ const translation = {
       key: '语音转文本模型',
       tip: '设置对话中语音转文字输入的默认使用模型。',
     },
+    rerankModel: {
+      key: 'Rerank 模型',
+      tip: '重排序模型将根据候选文档列表与用户问题语义匹配度进行重新排序,从而改进语义排序的结果',
+    },
     quota: '额度',
     searchModel: '搜索模型',
     noModelFound: '找不到模型 {{model}}',
@@ -244,6 +250,8 @@ const translation = {
     showMoreModelProvider: '显示更多模型提供商',
     selector: {
       tip: '该模型已被删除。请添模型或选择其他模型。',
+      emptyTip: '无可用模型',
+      emptySetting: '请前往设置进行配置',
     },
     card: {
       quota: '额度',
@@ -351,7 +359,7 @@ const translation = {
   },
   datasetMenus: {
     documents: '文档',
-    hitTesting: '命中测试',
+    hitTesting: '召回测试',
     settings: '设置',
     emptyTip: ' 数据集尚未关联,请前往应用程序或插件完成关联。',
     viewDoc: '查看文档',
@@ -382,9 +390,9 @@ const translation = {
       title: '引用',
       linkToDataset: '跳转至数据集',
       characters: '字符:',
-      hitCount: '命中次数:',
+      hitCount: '召回次数:',
       vectorHash: '向量哈希:',
-      hitScore: '命中得分:',
+      hitScore: '召回得分:',
     },
   },
   promptEditor: {

+ 1 - 0
web/i18n/lang/dataset-creation.en.ts

@@ -101,6 +101,7 @@ const translation = {
     previewSwitchTipEnd: ' consume additional tokens',
     characters: 'characters',
     indexSettedTip: 'To change the index method, please go to the ',
+    retrivalSettedTip: 'To change the index method, please go to the ',
     datasetSettingLink: 'dataset settings.',
   },
   stepThree: {

+ 1 - 0
web/i18n/lang/dataset-creation.zh.ts

@@ -101,6 +101,7 @@ const translation = {
     previewSwitchTipEnd: '消耗额外的 token',
     characters: '字符',
     indexSettedTip: '要更改索引方法,请转到',
+    retrivalSettedTip: '要更改检索方法,请转到',
     datasetSettingLink: '数据集设置。',
   },
   stepThree: {

+ 3 - 3
web/i18n/lang/dataset-documents.en.ts

@@ -8,7 +8,7 @@ const translation = {
       header: {
         fileName: 'FILE NAME',
         words: 'WORDS',
-        hitCount: 'HIT COUNT',
+        hitCount: 'RETRIEVAL COUNT',
         uploadTime: 'UPLOAD TIME',
         status: 'STATUS',
         action: 'ACTION',
@@ -216,7 +216,7 @@ const translation = {
         segmentLength: 'Segment length',
         avgParagraphLength: 'Avg. paragraph length',
         paragraphs: 'Paragraphs',
-        hitCount: 'Hit count',
+        hitCount: 'Retrieval count',
         embeddingTime: 'Embedding time',
         embeddedSpend: 'Embedded spend',
       },
@@ -332,7 +332,7 @@ const translation = {
     addKeyWord: 'Add key word',
     keywordError: 'The maximum length of keyword is 20',
     characters: 'characters',
-    hitCount: 'hit count',
+    hitCount: 'Retrieval count',
     vectorHash: 'Vector hash: ',
     questionPlaceholder: 'add question here',
     questionEmpty: 'Question can not be empty',

+ 3 - 3
web/i18n/lang/dataset-documents.zh.ts

@@ -8,7 +8,7 @@ const translation = {
       header: {
         fileName: '文件名',
         words: '字符数',
-        hitCount: '命中次数',
+        hitCount: '召回次数',
         uploadTime: '上传时间',
         status: '状态',
         action: '操作',
@@ -215,7 +215,7 @@ const translation = {
         segmentLength: '段落长度',
         avgParagraphLength: '平均段落长度',
         paragraphs: '段落数量',
-        hitCount: '命中次数',
+        hitCount: '召回次数',
         embeddingTime: '嵌入时间',
         embeddedSpend: '嵌入花费',
       },
@@ -331,7 +331,7 @@ const translation = {
     addKeyWord: '添加关键词',
     keywordError: '关键词最大长度为 20',
     characters: '字符',
-    hitCount: '命中次数',
+    hitCount: '召回次数',
     vectorHash: '向量哈希:',
     questionPlaceholder: '在这里添加问题',
     questionEmpty: '问题不能为空',

+ 9 - 9
web/i18n/lang/dataset-hit-testing.en.ts

@@ -1,13 +1,13 @@
 const translation = {
-  title: "Hit Testing",
-  desc: "Test the hitting effect of the dataset based on the given query text.",
+  title: 'Retrieval Testing',
+  desc: 'Test the hitting effect of the dataset based on the given query text.',
   dateTimeFormat: 'MM/DD/YYYY hh:mm A',
   recents: 'Recents',
   table: {
     header: {
-      source: "Source",
-      text: "Text",
-      time: "Time",
+      source: 'Source',
+      text: 'Text',
+      time: 'Time',
     },
   },
   input: {
@@ -18,11 +18,11 @@ const translation = {
     testing: 'Testing',
   },
   hit: {
-    title: "HIT PARAGRAPHS",
-    emptyTip: 'Hit Testing results will show here',
+    title: 'RETRIEVAL PARAGRAPHS',
+    emptyTip: 'Retrieval Testing results will show here',
   },
   noRecentTip: 'No recent query results here',
   viewChart: 'View VECTOR CHART',
-};
+}
 
-export default translation;
+export default translation

+ 7 - 7
web/i18n/lang/dataset-hit-testing.zh.ts

@@ -1,13 +1,13 @@
 const translation = {
-  title: '命中测试',
-  desc: '基于给定的查询文本测试数据集的命中效果。',
+  title: '召回测试',
+  desc: '基于给定的查询文本测试数据集的召回效果。',
   dateTimeFormat: 'YYYY-MM-DD HH:mm',
   recents: '最近查询',
   table: {
     header: {
-      source: "数据源",
-      text: "文本",
-      time: "时间",
+      source: '数据源',
+      text: '文本',
+      time: '时间',
     },
   },
   input: {
@@ -18,8 +18,8 @@ const translation = {
     testing: '测试',
   },
   hit: {
-    title: "命中段落",
-    emptyTip: '命中测试结果将展示在这里',
+    title: '召回段落',
+    emptyTip: '召回测试结果将展示在这里',
   },
   noRecentTip: '最近无查询结果',
   viewChart: '查看向量图表',

+ 6 - 0
web/i18n/lang/dataset-settings.en.ts

@@ -20,6 +20,12 @@ const translation = {
     embeddingModel: 'Embedding Model',
     embeddingModelTip: 'Change the embedded model, please go to ',
     embeddingModelTipLink: 'Settings',
+    retrievalSetting: {
+      title: 'Retrieval setting',
+      learnMore: 'Learn more',
+      description: ' about retrieval method.',
+      longDescription: ' about retrieval method, you can change this at any time in the dataset settings.',
+    },
     save: 'Save',
   },
 }

+ 6 - 0
web/i18n/lang/dataset-settings.zh.ts

@@ -20,6 +20,12 @@ const translation = {
     embeddingModel: 'Embedding 模型',
     embeddingModelTip: '修改 Embedding 模型,请去',
     embeddingModelTipLink: '设置',
+    retrievalSetting: {
+      title: '检索设置',
+      learnMore: '了解更多',
+      description: '关于检索方法。',
+      longDescription: '关于检索方法,您可以随时在数据集设置中更改此设置。',
+    },
     save: '保存',
   },
 }

+ 21 - 0
web/i18n/lang/dataset.en.ts

@@ -20,6 +20,27 @@ const translation = {
   unavailableTip: 'Embedding model is not available, the default embedding model needs to be configured',
   datasets: 'DATASETS',
   datasetsApi: 'API',
+  retrieval: {
+    semantic_search: {
+      title: 'Vector Search',
+      description: 'Generate query embeddings and search for the text chunk most similar to its vector representation.',
+    },
+    full_text_search: {
+      title: 'Full-Text Search',
+      description: 'Index all terms in the document, allowing users to search any term and retrieve relevant text chunk containing those terms.',
+    },
+    hybrid_search: {
+      title: 'Hybrid Search',
+      description: 'Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user\'s query. Configuration of the Rerank model APIis necessary.',
+      recommend: 'Recommend',
+    },
+    invertedIndex: {
+      title: 'Inverted Index',
+      description: 'Inverted Index is a structure used for efficient retrieval. Organized by terms, each term points to documents or web pages containing it.',
+    },
+    change: 'Change',
+    changeRetrievalMethod: 'Change retrieval method',
+  },
 }
 
 export default translation

+ 21 - 0
web/i18n/lang/dataset.zh.ts

@@ -20,6 +20,27 @@ const translation = {
   unavailableTip: '由于 embedding 模型不可用,需要配置默认 embedding 模型',
   datasets: '数据集',
   datasetsApi: 'API',
+  retrieval: {
+    semantic_search: {
+      title: '向量检索',
+      description: '通过生成查询嵌入并查询与其向量表示最相似的文本分段',
+    },
+    full_text_search: {
+      title: '全文检索',
+      description: '索引文档中的所有词汇,从而允许用户查询任意词汇,并返回包含这些词汇的文本片段',
+    },
+    hybrid_search: {
+      title: '混合检索',
+      description: '同时执行全文检索和向量检索,并应用重排序步骤,从两类查询结果中选择匹配用户问题的最佳结果,需配置 Rerank 模型 API',
+      recommend: '推荐',
+    },
+    invertedIndex: {
+      title: '倒排索引',
+      description: '倒排索引是一种用于高效检索的结构。按术语组织,每个术语指向包含它的文档或网页',
+    },
+    change: '更改',
+    changeRetrievalMethod: '更改检索方法',
+  },
 }
 
 export default translation

+ 4 - 0
web/models/datasets.ts

@@ -1,5 +1,6 @@
 import type { AppMode } from './app'
 import type { DataSourceNotionPage } from './common'
+import type { RetrievalConfig } from '@/types/app'
 
 export enum DataSourceType {
   FILE = 'upload_file',
@@ -25,6 +26,8 @@ export type DataSet = {
   embedding_model: string
   embedding_model_provider: string
   embedding_available: boolean
+  retrieval_model_dict: RetrievalConfig
+  retrieval_model: RetrievalConfig
 }
 
 export type CustomFile = File & {
@@ -193,6 +196,7 @@ export type DocumentReq = {
 
 export type CreateDocumentReq = DocumentReq & {
   data_source: DataSource
+  retrieval_model: RetrievalConfig
 }
 
 export type IndexingEstimateParams = DocumentReq & Partial<DataSource> & {

+ 9 - 2
web/models/debug.ts

@@ -1,4 +1,4 @@
-import type { ModelModeType } from '@/types/app'
+import type { ModelModeType, RETRIEVE_TYPE } from '@/types/app'
 export type Inputs = Record<string, string | number | object>
 
 export enum PromptMode {
@@ -107,9 +107,16 @@ export type DatasetConfigItem = {
   enable: boolean
   value: number
 }
+
 export type DatasetConfigs = {
+  retrieval_model: RETRIEVE_TYPE
+  reranking_model: {
+    reranking_provider_name: string
+    reranking_model_name: string
+  }
   top_k: number
-  score_threshold: DatasetConfigItem
+  score_threshold_enabled: boolean
+  score_threshold: number
 }
 
 export type DebugRequestBody = {

+ 8 - 0
web/service/common.ts

@@ -27,6 +27,7 @@ import type {
   ValidateOpenAIKeyResponse,
 } from '@/models/app'
 import type { BackendModel, ProviderMap } from '@/app/components/header/account-setting/model-page/declarations'
+import type { RETRIEVE_METHOD } from '@/types/app'
 
 export const login: Fetcher<CommonResponse & { data: string }, { url: string; body: Record<string, any> }> = ({ url, body }) => {
   return post(url, { body }) as Promise<CommonResponse & { data: string }>
@@ -237,3 +238,10 @@ export const fetchCodeBasedExtensionList: Fetcher<CodeBasedExtension, string> =
 export const moderate = (url: string, body: { app_id: string; text: string }) => {
   return post(url, { body }) as Promise<ModerateResponse>
 }
+
+type RetrievalMethodsRes = {
+  'retrieval_method': RETRIEVE_METHOD[]
+}
+export const fetchSupportRetrievalMethods: Fetcher<RetrievalMethodsRes, string> = (url) => {
+  return get<RetrievalMethodsRes>(url)
+}

+ 4 - 3
web/service/datasets.ts

@@ -27,6 +27,7 @@ import type {
   ApikeysListResponse,
   CreateApiKeyResponse,
 } from '@/models/app'
+import type { RetrievalConfig } from '@/types/app'
 
 // apis for documents in a dataset
 
@@ -48,7 +49,7 @@ export const fetchDatasetDetail: Fetcher<DataSet, string> = (datasetId: string)
   return get<DataSet>(`/datasets/${datasetId}`)
 }
 
-export const updateDatasetSetting: Fetcher<DataSet, { datasetId: string; body: Partial<Pick<DataSet, 'name' | 'description' | 'permission' | 'indexing_technique'>> }> = ({ datasetId, body }) => {
+export const updateDatasetSetting: Fetcher<DataSet, { datasetId: string; body: Partial<Pick<DataSet, 'name' | 'description' | 'permission' | 'indexing_technique' | 'retrieval_model'>> }> = ({ datasetId, body }) => {
   return patch<DataSet>(`/datasets/${datasetId}`, { body })
 }
 
@@ -182,8 +183,8 @@ export const checkSegmentBatchImportProgress: Fetcher<{ job_id: string; job_stat
 }
 
 // hit testing
-export const hitTesting: Fetcher<HitTestingResponse, { datasetId: string; queryText: string }> = ({ datasetId, queryText }) => {
-  return post<HitTestingResponse>(`/datasets/${datasetId}/hit-testing`, { body: { query: queryText } })
+export const hitTesting: Fetcher<HitTestingResponse, { datasetId: string; queryText: string; retrieval_model: RetrievalConfig }> = ({ datasetId, queryText, retrieval_model }) => {
+  return post<HitTestingResponse>(`/datasets/${datasetId}/hit-testing`, { body: { query: queryText, retrieval_model } })
 }
 
 export const fetchTestingRecords: Fetcher<HitTestingRecordsResponse, { datasetId: string; params: { page: number; limit: number } }> = ({ datasetId, params }) => {

+ 24 - 0
web/types/app.ts

@@ -22,6 +22,18 @@ export enum ModelModeType {
   'unset' = '',
 }
 
+export enum RETRIEVE_TYPE {
+  oneWay = 'single',
+  multiWay = 'multiple',
+}
+
+export enum RETRIEVE_METHOD {
+  semantic = 'semantic_search',
+  fullText = 'full_text_search',
+  hybrid = 'hybrid_search',
+  invertedIndex = 'invertedIndex',
+}
+
 export type VariableInput = {
   key: string
   name: string
@@ -311,3 +323,15 @@ export type VisionFile = {
   url: string
   upload_file_id: string
 }
+
+export type RetrievalConfig = {
+  search_method: RETRIEVE_METHOD
+  reranking_enable: boolean
+  reranking_model: {
+    reranking_provider_name: string
+    reranking_model_name: string
+  }
+  top_k: number
+  score_threshold_enable: boolean
+  score_threshold: number
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä