Wangbo 5 months ago
parent
commit
8ff09b2497
69 changed files with 8247 additions and 47 deletions
  1. 12 0
      app/(commonLayout)/app/(appDetailLayout)/layout.tsx
  2. 5 5
      app/(commonLayout)/apps/AppCard.tsx
  3. 6 5
      app/(commonLayout)/apps/Apps.tsx
  4. 21 16
      app/(commonLayout)/apps/NewAppCard.tsx
  5. BIN
      app/(commonLayout)/apps/assets/bg.png
  6. BIN
      app/(commonLayout)/apps/assets/bg1.png
  7. 14 9
      app/(commonLayout)/apps/page.tsx
  8. 6 0
      app/(commonLayout)/apps/style.module.css
  9. 11 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/api/page.tsx
  10. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx
  11. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx
  12. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx
  13. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/page.tsx
  14. 9 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/style.module.css
  15. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx
  16. 262 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/layout.tsx
  17. 20 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/settings/page.tsx
  18. 18 0
      app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/style.module.css
  19. 16 0
      app/(commonLayout)/database/(datasetDetailLayout)/layout.tsx
  20. 41 0
      app/(commonLayout)/database/ApiServer.tsx
  21. 127 0
      app/(commonLayout)/database/Container.tsx
  22. 133 0
      app/(commonLayout)/database/Database.tsx
  23. 220 0
      app/(commonLayout)/database/DatabaseCard.tsx
  24. 240 0
      app/(commonLayout)/database/DatasetCard.tsx
  25. 19 0
      app/(commonLayout)/database/DatasetFooter.tsx
  26. 88 0
      app/(commonLayout)/database/Datasets.tsx
  27. 35 0
      app/(commonLayout)/database/Doc.tsx
  28. 38 0
      app/(commonLayout)/database/NewDatabaseCard.tsx
  29. 38 0
      app/(commonLayout)/database/NewDatasetCard.tsx
  30. 8 0
      app/(commonLayout)/database/connect/page.tsx
  31. 12 0
      app/(commonLayout)/database/create/page.tsx
  32. 11 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx
  33. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx
  34. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx
  35. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx
  36. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx
  37. 9 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/style.module.css
  38. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx
  39. 262 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
  40. 20 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx
  41. 18 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/style.module.css
  42. 16 0
      app/(commonLayout)/database/datasets/(datasetDetailLayout)/layout.tsx
  43. 41 0
      app/(commonLayout)/database/datasets/ApiServer.tsx
  44. 129 0
      app/(commonLayout)/database/datasets/Container.tsx
  45. 88 0
      app/(commonLayout)/database/datasets/Database.tsx
  46. 240 0
      app/(commonLayout)/database/datasets/DatabaseCard.tsx
  47. 240 0
      app/(commonLayout)/database/datasets/DatasetCard.tsx
  48. 19 0
      app/(commonLayout)/database/datasets/DatasetFooter.tsx
  49. 88 0
      app/(commonLayout)/database/datasets/Datasets.tsx
  50. 35 0
      app/(commonLayout)/database/datasets/Doc.tsx
  51. 38 0
      app/(commonLayout)/database/datasets/NewDatabaseCard.tsx
  52. 38 0
      app/(commonLayout)/database/datasets/NewDatasetCard.tsx
  53. 8 0
      app/(commonLayout)/database/datasets/connect/page.tsx
  54. 12 0
      app/(commonLayout)/database/datasets/create/page.tsx
  55. 14 0
      app/(commonLayout)/database/datasets/layout.tsx
  56. 11 0
      app/(commonLayout)/database/datasets/page.tsx
  57. 11 0
      app/(commonLayout)/database/datasets/store.ts
  58. 1319 0
      app/(commonLayout)/database/datasets/template/template.en.mdx
  59. 1321 0
      app/(commonLayout)/database/datasets/template/template.zh.mdx
  60. 14 0
      app/(commonLayout)/database/layout.tsx
  61. 11 0
      app/(commonLayout)/database/page.tsx
  62. 11 0
      app/(commonLayout)/database/store.ts
  63. 1319 0
      app/(commonLayout)/database/template/template.en.mdx
  64. 1321 0
      app/(commonLayout)/database/template/template.zh.mdx
  65. 13 9
      app/(commonLayout)/datasets/Container.tsx
  66. 2 2
      app/(commonLayout)/datasets/DatasetCard.tsx
  67. 1 0
      app/(commonLayout)/datasets/Datasets.tsx
  68. 1 1
      app/(commonLayout)/datasets/NewDatasetCard.tsx
  69. 7 0
      app/(commonLayout)/explore/installed/[appId]/page.tsx

+ 12 - 0
app/(commonLayout)/app/(appDetailLayout)/layout.tsx

@@ -1,22 +1,33 @@
 'use client'
+// 导入FC类型,用于定义函数组件
 import type { FC } from 'react'
+// 导入React库
 import React, { useEffect } from 'react'
+// 导入Next.js的路由库
 import { useRouter } from 'next/navigation'
+// 导入自定义的AppContext
 import { useAppContext } from '@/context/app-context'
 
+// 定义AppDetail组件的props类型
 export type IAppDetail = {
   children: React.ReactNode
 }
 
+// 定义AppDetail组件
 const AppDetail: FC<IAppDetail> = ({ children }) => {
+  // 获取Next.js的路由实例
   const router = useRouter()
+  // 获取AppContext中的isCurrentWorkspaceDatasetOperator属性
   const { isCurrentWorkspaceDatasetOperator } = useAppContext()
 
+  // useEffect钩子,在isCurrentWorkspaceDatasetOperator属性发生变化时执行
   useEffect(() => {
+    // 如果isCurrentWorkspaceDatasetOperator为true,则跳转到'/datasets'页面
     if (isCurrentWorkspaceDatasetOperator)
       return router.replace('/datasets')
   }, [isCurrentWorkspaceDatasetOperator])
 
+  // 返回组件的内容
   return (
     <>
       {children}
@@ -24,4 +35,5 @@ const AppDetail: FC<IAppDetail> = ({ children }) => {
   )
 }
 
+// 使用React.memo优化组件,避免不必要的渲染
 export default React.memo(AppDetail)

+ 5 - 5
app/(commonLayout)/apps/AppCard.tsx

@@ -291,11 +291,11 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
               <div className='truncate' title={app.name}>{app.name}</div>
             </div>
             <div className='flex items-center text-[10px] leading-[18px] text-gray-500 font-medium'>
-              {app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
-              {app.mode === 'chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
-              {app.mode === 'agent-chat' && <div className='truncate'>{t('app.types.agent').toUpperCase()}</div>}
-              {app.mode === 'workflow' && <div className='truncate'>{t('app.types.workflow').toUpperCase()}</div>}
-              {app.mode === 'completion' && <div className='truncate'>{t('app.types.completion').toUpperCase()}</div>}
+              {app.mode === 'advanced-chat'}
+              {app.mode === 'chat'}
+              {app.mode === 'agent-chat'}
+              {app.mode === 'workflow'}
+              {app.mode === 'completion'}
             </div>
           </div>
         </div>

+ 6 - 5
app/(commonLayout)/apps/Apps.tsx

@@ -10,6 +10,7 @@ import {
   RiExchange2Line,
   RiMessage3Line,
   RiRobot3Line,
+  RiStackLine
 } from '@remixicon/react'
 import AppCard from './AppCard'
 import NewAppCard from './NewAppCard'
@@ -75,10 +76,10 @@ const Apps = () => {
 
   const anchorRef = useRef<HTMLDivElement>(null)
   const options = [
-    { value: 'all', text: t('app.types.all'), icon: <RiApps2Line className='w-[14px] h-[14px] mr-1' /> },
-    { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='w-[14px] h-[14px] mr-1' /> },
-    { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='w-[14px] h-[14px] mr-1' /> },
-    { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='w-[14px] h-[14px] mr-1' /> },
+    { value: 'all', text: t('app.types.all')},
+    // { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='w-[14px] h-[14px] mr-1' /> },
+    // { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='w-[14px] h-[14px] mr-1' /> },
+    // { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='w-[14px] h-[14px] mr-1' /> },
   ]
 
   useEffect(() => {
@@ -125,7 +126,7 @@ const Apps = () => {
 
   return (
     <>
-      <div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
+      <div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] z-10 flex-wrap gap-y-2'>
         <TabSliderNew
           value={activeTab}
           onChange={setActiveTab}

+ 21 - 16
app/(commonLayout)/apps/NewAppCard.tsx

@@ -11,7 +11,11 @@ import CreateAppModal from '@/app/components/app/create-app-modal'
 import CreateFromDSLModal, { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal'
 import { useProviderContext } from '@/context/provider-context'
 import { FileArrow01, FilePlus01, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
-
+import style from './style.module.css'
+import cn from '@/utils/classnames'
+import {
+  RiStickyNoteAddLine
+} from '@remixicon/react'
 export type CreateAppCardProps = {
   onSuccess?: () => void
 }
@@ -38,28 +42,29 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
   return (
     <a
       ref={ref}
-      className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl border-[0.5px] border-black/5'
+      className={cn('relative col-span-1 flex flex-col justify-between min-h-[160px] rounded-xl border-[0.5px] border-black/5', style.background)}
     >
       <div className='grow p-2 rounded-t-xl'>
-        <div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-gray-500'>{t('app.createApp')}</div>
-        <div className='flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppModal(true)}>
-          <FilePlus01 className='shrink-0 mr-2 w-4 h-4' />
+        <div className='px-6 pt-2 pb-1 text-sm font-semibold font-medium leading-[18px]'>{t('app.createApp')}</div>
+        <div style={{marginTop:'5px'}} className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:bg-white' onClick={() => setShowNewAppModal(true)}>
+          <RiStickyNoteAddLine className='shrink-0 mr-2 w-4 h-4' />
           {t('app.newApp.startFromBlank')}
         </div>
-        <div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppTemplateDialog(true)}>
+        {/* <div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:bg-white' onClick={() => setShowNewAppTemplateDialog(true)}>
           <FilePlus02 className='shrink-0 mr-2 w-4 h-4' />
           {t('app.newApp.startFromTemplate')}
-        </div>
-      </div>
-      <div
-        className='p-2 border-t-[0.5px] border-black/5 rounded-b-xl'
-        onClick={() => setShowCreateFromDSLModal(true)}
-      >
-        <div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white'>
-          <FileArrow01 className='shrink-0 mr-2 w-4 h-4' />
-          {t('app.importDSL')}
-        </div>
+        </div> */}
+        {/* <div
+          className='rounded-b-xl'
+          onClick={() => setShowCreateFromDSLModal(true)}
+        >
+          <div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:bg-white'>
+            <FileArrow01 className='shrink-0 mr-2 w-4 h-4' />
+            {t('app.importDSL')}
+          </div>
+        </div> */}
       </div>
+
       <CreateAppModal
         show={showNewAppModal}
         onClose={() => setShowNewAppModal(false)}

BIN
app/(commonLayout)/apps/assets/bg.png


BIN
app/(commonLayout)/apps/assets/bg1.png


+ 14 - 9
app/(commonLayout)/apps/page.tsx

@@ -1,29 +1,34 @@
 'use client'
+// 导入useContextSelector函数,用于从上下文中选择数据
 import { useContextSelector } from 'use-context-selector'
+// 导入useTranslation函数,用于翻译文本
 import { useTranslation } from 'react-i18next'
+// 导入样式文件
 import style from '../list.module.css'
+// 导入Apps组件
 import Apps from './Apps'
+// 导入classNames函数,用于处理类名
 import classNames from '@/utils/classnames'
+// 导入AppContext上下文
 import AppContext from '@/context/app-context'
+// 导入LicenseStatus类型
 import { LicenseStatus } from '@/types/feature'
 
+// 定义AppList组件
 const AppList = () => {
+  // 使用useTranslation函数获取翻译函数t
   const { t } = useTranslation()
+  // 使用useContextSelector函数从AppContext上下文中选择systemFeatures数据
   const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)
 
+  // 返回组件内容
   return (
-    <div className='relative flex flex-col overflow-y-auto bg-gray-100 shrink-0 h-0 grow'>
+    <div className='relative flex flex-col overflow-y-auto shrink-0 h-0 grow'>
       <Apps />
-      {systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'>
-        <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3>
-        <p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('app.communityIntro')}</p>
-        <div className='flex items-center gap-2 mt-3'>
-          <a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
-          <a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>
-        </div>
-      </footer>}
+      
     </div >
   )
 }
 
+// 导出AppList组件npm
 export default AppList

+ 6 - 0
app/(commonLayout)/apps/style.module.css

@@ -20,6 +20,12 @@
   @apply text-gray-700 text-sm;
 }
 
+.background {
+  /* background-image: url('assets/bg.png'); */
+  background-color: rgba(255,255,255,.6);
+  background-size: cover;
+}
+
 /* .completionPic {
   background-image: url(~@/app/components/app-sidebar/completion.png)
 }

+ 11 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/api/page.tsx

@@ -0,0 +1,11 @@
+import React from 'react'
+
+type Props = {}
+
+const page = (props: Props) => {
+  return (
+    <div>dataset detail api</div>
+  )
+}
+
+export default page

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import MainDetail from '@/app/components/datasets/documents/detail'
+
+export type IDocumentDetailProps = {
+  params: { datasetId: string; documentId: string }
+}
+
+const DocumentDetail = async ({
+  params: { datasetId, documentId },
+}: IDocumentDetailProps) => {
+  return (
+    <MainDetail datasetId={datasetId} documentId={documentId} />
+  )
+}
+
+export default DocumentDetail

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Settings from '@/app/components/datasets/documents/detail/settings'
+
+export type IProps = {
+  params: { datasetId: string; documentId: string }
+}
+
+const DocumentSettings = async ({
+  params: { datasetId, documentId },
+}: IProps) => {
+  return (
+    <Settings datasetId={datasetId} documentId={documentId} />
+  )
+}
+
+export default DocumentSettings

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import DatasetUpdateForm from '@/app/components/datasets/create'
+
+export type IProps = {
+  params: { datasetId: string }
+}
+
+const Create = async ({
+  params: { datasetId },
+}: IProps) => {
+  return (
+    <DatasetUpdateForm datasetId={datasetId} />
+  )
+}
+
+export default Create

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Main from '@/app/components/database/documents'
+
+export type IProps = {
+  params: { datasetId: string }
+}
+
+const Documents = async ({
+  params: { datasetId },
+}: IProps) => {
+  return (
+    <Main datasetId={datasetId} />
+  )
+}
+
+export default Documents

+ 9 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/documents/style.module.css

@@ -0,0 +1,9 @@
+.logTable td {
+  padding: 7px 8px;
+  box-sizing: border-box;
+  max-width: 200px;
+}
+
+.pagination li {
+  list-style: none;
+}

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Main from '@/app/components/datasets/hit-testing'
+
+type Props = {
+  params: { datasetId: string }
+}
+
+const HitTesting = ({
+  params: { datasetId },
+}: Props) => {
+  return (
+    <Main datasetId={datasetId} />
+  )
+}
+
+export default HitTesting

+ 262 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/layout.tsx

@@ -0,0 +1,262 @@
+'use client'
+import type { FC, SVGProps } from 'react'
+import React, { useEffect, useMemo } from 'react'
+import { usePathname } from 'next/navigation'
+import useSWR from 'swr'
+import { useTranslation } from 'react-i18next'
+import { useBoolean } from 'ahooks'
+import {
+  Cog8ToothIcon,
+  // CommandLineIcon,
+  Squares2X2Icon,
+  // eslint-disable-next-line sort-imports
+  PuzzlePieceIcon,
+  DocumentTextIcon,
+  PaperClipIcon,
+  QuestionMarkCircleIcon,
+} from '@heroicons/react/24/outline'
+import {
+  Cog8ToothIcon as Cog8ToothSolidIcon,
+  // CommandLineIcon as CommandLineSolidIcon,
+  DocumentTextIcon as DocumentTextSolidIcon,
+} from '@heroicons/react/24/solid'
+import Link from 'next/link'
+import s from './style.module.css'
+import classNames from '@/utils/classnames'
+import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
+import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
+import AppSideBar from '@/app/components/app-sidebar'
+import Divider from '@/app/components/base/divider'
+import AppIcon from '@/app/components/base/app-icon'
+import Loading from '@/app/components/base/loading'
+import FloatPopoverContainer from '@/app/components/base/float-popover-container'
+import DatasetDetailContext from '@/context/dataset-detail'
+import { DataSourceType } from '@/models/datasets'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import { LanguagesSupported } from '@/i18n/language'
+import { useStore } from '@/app/components/app/store'
+import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
+import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
+import { getLocaleOnClient } from '@/i18n'
+import { useAppContext } from '@/context/app-context'
+
+export type IAppDetailLayoutProps = {
+  children: React.ReactNode
+  params: { datasetId: string }
+}
+
+type ILikedItemProps = {
+  type?: 'plugin' | 'app'
+  appStatus?: boolean
+  detail: RelatedApp
+  isMobile: boolean
+}
+
+const LikedItem = ({
+  type = 'app',
+  detail,
+  isMobile,
+}: ILikedItemProps) => {
+  return (
+    <Link className={classNames(s.itemWrapper, 'px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
+      <div className={classNames(s.iconWrapper, 'mr-0')}>
+        <AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
+        {type === 'app' && (
+          <span className='absolute bottom-[-2px] right-[-2px] w-3.5 h-3.5 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
+            {detail.mode === 'advanced-chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'agent-chat' && (
+              <CuteRobot className='w-2.5 h-2.5 text-indigo-600' />
+            )}
+            {detail.mode === 'chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'completion' && (
+              <AiText className='w-2.5 h-2.5 text-[#0E9384]' />
+            )}
+            {detail.mode === 'workflow' && (
+              <Route className='w-2.5 h-2.5 text-[#f79009]' />
+            )}
+          </span>
+        )}
+      </div>
+      {!isMobile && <div className={classNames(s.appInfo, 'ml-2')}>{detail?.name || '--'}</div>}
+    </Link>
+  )
+}
+
+const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <g clipPath="url(#clip0_4610_6951)">
+      <path d="M10.6666 5.33325V3.33325L12.6666 1.33325L13.3332 2.66659L14.6666 3.33325L12.6666 5.33325H10.6666ZM10.6666 5.33325L7.9999 7.99988M14.6666 7.99992C14.6666 11.6818 11.6818 14.6666 7.99992 14.6666C4.31802 14.6666 1.33325 11.6818 1.33325 7.99992C1.33325 4.31802 4.31802 1.33325 7.99992 1.33325M11.3333 7.99992C11.3333 9.84087 9.84087 11.3333 7.99992 11.3333C6.15897 11.3333 4.66659 9.84087 4.66659 7.99992C4.66659 6.15897 6.15897 4.66659 7.99992 4.66659" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
+    </g>
+    <defs>
+      <clipPath id="clip0_4610_6951">
+        <rect width="16" height="16" fill="white" />
+      </clipPath>
+    </defs>
+  </svg>
+}
+
+const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <path fillRule="evenodd" clipRule="evenodd" d="M12.7733 0.67512C12.9848 0.709447 13.1669 0.843364 13.2627 1.03504L13.83 2.16961L14.9646 2.73689C15.1563 2.83273 15.2902 3.01486 15.3245 3.22639C15.3588 3.43792 15.2894 3.65305 15.1379 3.80458L13.1379 5.80458C13.0128 5.92961 12.8433 5.99985 12.6665 5.99985H10.9426L8.47124 8.47124C8.21089 8.73159 7.78878 8.73159 7.52843 8.47124C7.26808 8.21089 7.26808 7.78878 7.52843 7.52843L9.9998 5.05707V3.33318C9.9998 3.15637 10.07 2.9868 10.1951 2.86177L12.1951 0.861774C12.3466 0.710244 12.5617 0.640794 12.7733 0.67512Z" fill="#155EEF" />
+    <path d="M1.99984 7.99984C1.99984 4.68613 4.68613 1.99984 7.99984 1.99984C8.36803 1.99984 8.6665 1.70136 8.6665 1.33317C8.6665 0.964981 8.36803 0.666504 7.99984 0.666504C3.94975 0.666504 0.666504 3.94975 0.666504 7.99984C0.666504 12.0499 3.94975 15.3332 7.99984 15.3332C12.0499 15.3332 15.3332 12.0499 15.3332 7.99984C15.3332 7.63165 15.0347 7.33317 14.6665 7.33317C14.2983 7.33317 13.9998 7.63165 13.9998 7.99984C13.9998 11.3135 11.3135 13.9998 7.99984 13.9998C4.68613 13.9998 1.99984 11.3135 1.99984 7.99984Z" fill="#155EEF" />
+    <path d="M5.33317 7.99984C5.33317 6.52708 6.52708 5.33317 7.99984 5.33317C8.36803 5.33317 8.6665 5.03469 8.6665 4.6665C8.6665 4.29831 8.36803 3.99984 7.99984 3.99984C5.7907 3.99984 3.99984 5.7907 3.99984 7.99984C3.99984 10.209 5.7907 11.9998 7.99984 11.9998C10.209 11.9998 11.9998 10.209 11.9998 7.99984C11.9998 7.63165 11.7014 7.33317 11.3332 7.33317C10.965 7.33317 10.6665 7.63165 10.6665 7.99984C10.6665 9.4726 9.4726 10.6665 7.99984 10.6665C6.52708 10.6665 5.33317 9.4726 5.33317 7.99984Z" fill="#155EEF" />
+  </svg>
+}
+
+const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
+    <path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
+  </svg>
+}
+
+type IExtraInfoProps = {
+  isMobile: boolean
+  relatedApps?: RelatedAppResponse
+}
+
+const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
+  const locale = getLocaleOnClient()
+  const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    setShowTips(!isMobile)
+  }, [isMobile, setShowTips])
+
+  return <div className='w-full flex flex-col items-center'>
+    <Divider className='mt-5' />
+    {(relatedApps?.data && relatedApps?.data?.length > 0) && (
+      <>
+        {!isMobile && <div className='w-full px-2 pb-1 pt-4 uppercase text-xs text-gray-500 font-medium'>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
+        {isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
+          {relatedApps?.total || '--'}
+          <PaperClipIcon className='h-4 w-4 text-gray-700' />
+        </div>}
+        {relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
+      </>
+    )}
+    {!relatedApps?.data?.length && (
+      <FloatPopoverContainer
+        placement='bottom-start'
+        open={isShowTips}
+        toggle={toggleTips}
+        isMobile={isMobile}
+        triggerElement={
+          <div className={classNames('h-7 w-7 inline-flex justify-center items-center rounded-lg bg-transparent', isShowTips && '!bg-gray-50')}>
+            <QuestionMarkCircleIcon className='h-4 w-4 flex-shrink-0 text-gray-500' />
+          </div>
+        }
+      >
+        <div className={classNames('mt-5 p-3', isMobile && 'border-[0.5px] border-gray-200 shadow-lg rounded-lg bg-white w-[160px]')}>
+          <div className='flex items-center justify-start gap-2'>
+            <div className={s.emptyIconDiv}>
+              <Squares2X2Icon className='w-3 h-3 text-gray-500' />
+            </div>
+            <div className={s.emptyIconDiv}>
+              <PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
+            </div>
+          </div>
+          <div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
+          <a
+            className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
+            href={
+              locale === LanguagesSupported[1]
+                ? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate_knowledge_within_application'
+                : 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
+            }
+            target='_blank' rel='noopener noreferrer'
+          >
+            <BookOpenIcon className='mr-1' />
+            {t('common.datasetMenus.viewDoc')}
+          </a>
+        </div>
+      </FloatPopoverContainer>
+    )}
+  </div>
+}
+
+const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
+  const {
+    children,
+    params: { datasetId },
+  } = props
+  const pathname = usePathname()
+  const hideSideBar = /documents\/create$/.test(pathname)
+  const { t } = useTranslation()
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
+  const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
+    url: 'fetchDatasetDetail',
+    datasetId,
+  }, apiParams => fetchDatasetDetail(apiParams.datasetId))
+
+  const { data: relatedApps } = useSWR({
+    action: 'fetchDatasetRelatedApps',
+    datasetId,
+  }, apiParams => fetchDatasetRelatedApps(apiParams.datasetId))
+
+  const navigation = useMemo(() => {
+    const baseNavigation = [
+      { name: t('common.datasetMenus.hitTesting'), href: `/datasets/${datasetId}/hitTesting`, icon: TargetIcon, selectedIcon: TargetSolidIcon },
+      // { name: 'api & webhook', href: `/datasets/${datasetId}/api`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
+      { name: t('common.datasetMenus.settings'), href: `/datasets/${datasetId}/settings`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon },
+    ]
+
+    if (datasetRes?.provider !== 'external') {
+      baseNavigation.unshift({
+        name: t('common.datasetMenus.documents'),
+        href: `/datasets/${datasetId}/documents`,
+        icon: DocumentTextIcon,
+        selectedIcon: DocumentTextSolidIcon,
+      })
+    }
+    return baseNavigation
+  }, [datasetRes?.provider, datasetId, t])
+
+  useEffect(() => {
+    if (datasetRes)
+      document.title = `${datasetRes.name || 'Dataset'} - Dify`
+  }, [datasetRes])
+
+  const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand)
+
+  useEffect(() => {
+    const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
+    const mode = isMobile ? 'collapse' : 'expand'
+    setAppSiderbarExpand(isMobile ? mode : localeMode)
+  }, [isMobile, setAppSiderbarExpand])
+
+  if (!datasetRes && !error)
+    return <Loading />
+
+  return (
+    <div className='grow flex overflow-hidden'>
+      {!hideSideBar && <AppSideBar
+        title={datasetRes?.name || '--'}
+        icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
+        icon_background={datasetRes?.icon_background || '#F5F5F5'}
+        desc={datasetRes?.description || '--'}
+        isExternal={datasetRes?.provider === 'external'}
+        navigation={navigation}
+        extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} /> : undefined}
+        iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
+      />}
+      <DatasetDetailContext.Provider value={{
+        indexingTechnique: datasetRes?.indexing_technique,
+        dataset: datasetRes,
+        mutateDatasetRes: () => mutateDatasetRes(),
+      }}>
+        <div className="bg-white grow overflow-hidden">{children}</div>
+      </DatasetDetailContext.Provider>
+    </div>
+  )
+}
+export default React.memo(DatasetDetailLayout)

+ 20 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/settings/page.tsx

@@ -0,0 +1,20 @@
+import React from 'react'
+import { getLocaleOnServer, useTranslation as translate } from '@/i18n/server'
+import Form from '@/app/components/datasets/settings/form'
+
+const Settings = async () => {
+  const locale = getLocaleOnServer()
+  const { t } = await translate(locale, 'dataset-settings')
+
+  return (
+    <div className='bg-white h-full overflow-y-auto'>
+      <div className='px-6 py-3'>
+        <div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
+        <div className='text-sm text-gray-500'>{t('desc')}</div>
+      </div>
+      <Form />
+    </div>
+  )
+}
+
+export default Settings

+ 18 - 0
app/(commonLayout)/database/(datasetDetailLayout)/[datasetId]/style.module.css

@@ -0,0 +1,18 @@
+.itemWrapper {
+  @apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
+}
+.appInfo {
+  @apply truncate text-gray-700 text-sm font-normal;
+}
+.iconWrapper {
+  @apply relative w-6 h-6 rounded-lg;
+}
+.statusPoint {
+  @apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
+}
+.subTitle {
+  @apply uppercase text-xs text-gray-500 font-medium px-3 pb-2 pt-4;
+}
+.emptyIconDiv {
+  @apply h-7 w-7 bg-gray-50 border border-[#EAECF5] inline-flex justify-center items-center rounded-lg;
+}

+ 16 - 0
app/(commonLayout)/database/(datasetDetailLayout)/layout.tsx

@@ -0,0 +1,16 @@
+import type { FC } from 'react'
+import React from 'react'
+
+export type IDatasetDetail = {
+  children: React.ReactNode
+}
+
+const AppDetail: FC<IDatasetDetail> = ({ children }) => {
+  return (
+    <>
+      {children}
+    </>
+  )
+}
+
+export default React.memo(AppDetail)

+ 41 - 0
app/(commonLayout)/database/ApiServer.tsx

@@ -0,0 +1,41 @@
+'use client'
+
+import type { FC } from 'react'
+import { useTranslation } from 'react-i18next'
+import CopyFeedback from '@/app/components/base/copy-feedback'
+import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
+import { randomString } from '@/utils'
+
+type ApiServerProps = {
+  apiBaseUrl: string
+}
+const ApiServer: FC<ApiServerProps> = ({
+  apiBaseUrl,
+}) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='flex items-center flex-wrap gap-y-2'>
+      <div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg leading-5'>
+        <div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md shrink-0'>{t('appApi.apiServer')}</div>
+        <div className='px-1 truncate w-fit sm:w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
+        <div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
+        <CopyFeedback
+          content={apiBaseUrl}
+          selectorId={randomString(8)}
+          className={'!w-6 !h-6 hover:bg-gray-200'}
+        />
+      </div>
+      <div className='flex items-center mr-2 px-3 h-8 bg-[#ECFDF3] text-xs font-semibold text-[#039855] rounded-lg border-[0.5px] border-[#D1FADF]'>
+        {t('appApi.ok')}
+      </div>
+      <SecretKeyButton
+        className='flex-shrink-0 !h-8 bg-white'
+        textCls='!text-gray-700 font-medium'
+        iconCls='stroke-[1.2px]'
+      />
+    </div>
+  )
+}
+
+export default ApiServer

+ 127 - 0
app/(commonLayout)/database/Container.tsx

@@ -0,0 +1,127 @@
+'use client'
+
+// Libraries
+import { useEffect, useMemo, useRef, useState } from 'react'
+import { useRouter } from 'next/navigation'
+import { useTranslation } from 'react-i18next'
+import { useDebounceFn } from 'ahooks'
+
+// Components
+import ExternalAPIPanel from '../../components/datasets/external-api/external-api-panel'
+import Datasets from './Datasets'
+import Database from './Database'
+
+import DatasetFooter from './DatasetFooter'
+import ApiServer from './ApiServer'
+import Doc from './Doc'
+import TabSliderNew from '@/app/components/base/tab-slider-new'
+import TagManagementModal from '@/app/components/base/tag-management'
+import TagFilter from '@/app/components/base/tag-management/filter'
+import Button from '@/app/components/base/button'
+import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
+import SearchInput from '@/app/components/base/search-input'
+
+// Services
+import { fetchDatasetApiBaseUrl } from '@/service/datasets'
+
+// Hooks
+import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
+import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
+import { useAppContext } from '@/context/app-context'
+import { useExternalApiPanel } from '@/context/external-api-panel-context'
+// eslint-disable-next-line import/order
+import { useQuery } from '@tanstack/react-query'
+
+const Container = () => {
+  const { t } = useTranslation()
+  const router = useRouter()
+  const { currentWorkspace } = useAppContext()
+  const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
+  const { showExternalApiPanel, setShowExternalApiPanel } = useExternalApiPanel()
+
+  const options = useMemo(() => {
+    return [
+      { value: 'dataset', text: t('dataset.database') },
+      // ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]),
+    ]
+  }, [currentWorkspace.role, t])
+
+  const [activeTab, setActiveTab] = useTabSearchParams({
+    defaultTab: 'dataset',
+  })
+  const containerRef = useRef<HTMLDivElement>(null)
+  const { data } = useQuery(
+    {
+      queryKey: ['datasetApiBaseInfo'],
+      queryFn: () => fetchDatasetApiBaseUrl('/datasets/api-base-info'),
+      enabled: activeTab !== 'dataset',
+    },
+  )
+
+  const [keywords, setKeywords] = useState('')
+  const [searchKeywords, setSearchKeywords] = useState('')
+  const { run: handleSearch } = useDebounceFn(() => {
+    setSearchKeywords(keywords)
+  }, { wait: 500 })
+  const handleKeywordsChange = (value: string) => {
+    setKeywords(value)
+    handleSearch()
+  }
+  const [tagFilterValue, setTagFilterValue] = useState<string[]>([])
+  const [tagIDs, setTagIDs] = useState<string[]>([])
+  const { run: handleTagsUpdate } = useDebounceFn(() => {
+    setTagIDs(tagFilterValue)
+  }, { wait: 500 })
+  const handleTagsChange = (value: string[]) => {
+    setTagFilterValue(value)
+    handleTagsUpdate()
+  }
+
+  useEffect(() => {
+    if (currentWorkspace.role === 'normal')
+      return router.replace('/apps')
+  }, [currentWorkspace, router])
+
+  return (
+    // 数据库主页面
+    <div ref={containerRef} className='grow relative flex flex-col overflow-y-auto'>
+      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] z-10 flex-wrap gap-y-2'>
+        <TabSliderNew
+          value={activeTab}
+          onChange={newActiveTab => setActiveTab(newActiveTab)}
+          options={options}
+        />
+        {activeTab === 'dataset' && (
+          <div className='flex items-center gap-2'>
+            <TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
+            <SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
+            <div className="w-[1px] h-4 bg-divider-regular" />
+            {/* <Button
+              className='gap-0.5 shadows-shadow-xs'
+              onClick={() => setShowExternalApiPanel(true)}
+            >
+              <ApiConnectionMod className='w-4 h-4 text-components-button-secondary-text' />
+              <div className='flex px-0.5 justify-center items-center gap-1 text-components-button-secondary-text system-sm-medium'>{t('dataset.externalAPIPanelTitle')}</div>
+            </Button> */}
+          </div>
+        )}
+        {/* {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />} */}
+      </div>
+      {activeTab === 'dataset' && (
+        <>
+          <Database containerRef={containerRef} tags={tagIDs} keywords={searchKeywords} />
+          {/* <DatasetFooter /> */}
+          {showTagManagementModal && (
+            <TagManagementModal type='knowledge' show={showTagManagementModal} />
+          )}
+        </>
+
+      )}
+      {/* {activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />} */}
+
+      {showExternalApiPanel && <ExternalAPIPanel onClose={() => setShowExternalApiPanel(false)} />}
+    </div>
+  )
+}
+
+export default Container

+ 133 - 0
app/(commonLayout)/database/Database.tsx

@@ -0,0 +1,133 @@
+// Database.js
+'use client'
+
+import { useEffect, useRef } from 'react';
+import useSWRInfinite from 'swr/infinite';
+import { debounce } from 'lodash-es';
+import { useTranslation } from 'react-i18next';
+import NewDatabaseCard from './NewDatabaseCard';
+import DatasetCard from './DatabaseCard'; // 确保正确导入 DatasetCard 组件
+import type { DataSetListResponse } from '@/models/datasets';
+import { fetchDatasets } from '@/service/datasets';
+import { useAppContext } from '@/context/app-context';
+import { RiStackLine } from '@remixicon/react';
+import { link } from 'fs';
+
+const getKey = (
+  pageIndex: number,
+  previousPageData: DataSetListResponse,
+  tags: string[],
+  keyword: string,
+) => {
+  if (!pageIndex || previousPageData.has_more) {
+    const params: any = {
+      url: 'datasets',
+      params: {
+        page: pageIndex + 1,
+        limit: 30,
+      },
+    };
+    if (tags.length)
+      params.params.tag_ids = tags;
+    if (keyword)
+      params.params.keyword = keyword;
+    return params;
+  }
+  return null;
+};
+
+type Props = {
+  containerRef: React.RefObject<HTMLDivElement>,
+  tags: string[],
+  keywords: string,
+};
+
+// 创建虚拟数据集
+const mockDatasets = [
+  {
+    id: 'mock-1',
+    name: '种植数据库',
+    description: '',
+    tags: [{ id: '1', name: '' }],
+    provider: 'internal',
+    embedding_available: true,
+    document_count: 500,
+    word_count: 100000,
+    app_count: 3,
+  },
+  {
+    id: 'mock-2',
+    name: '病害数据库',
+    description: '',
+    tags: [{ id: '3', name: '' }],
+    provider: 'internal',
+    embedding_available: true,
+    document_count: 300,
+    word_count: 75000,
+    app_count: 2,
+  },
+  {
+    id: 'mock-3',
+    name: '产量数据库',
+    description: '',
+    tags: [{ id: '5', name: '' }],
+    provider: 'internal',
+    embedding_available: true,
+    document_count: 400,
+    word_count: 90000,
+    app_count: 4,
+  },
+];
+
+const Database = ({
+  containerRef,
+  tags,
+  keywords,
+}: Props) => {
+  const { isCurrentWorkspaceEditor } = useAppContext();
+  const { data, isLoading, setSize, mutate } = useSWRInfinite(
+    (pageIndex: number, previousPageData: DataSetListResponse) => getKey(pageIndex, previousPageData, tags, keywords),
+    fetchDatasets,
+    { revalidateFirstPage: false, revalidateAll: true },
+  );
+  const loadingStateRef = useRef(false);
+  const anchorRef = useRef<HTMLAnchorElement>(null);
+
+  const { t } = useTranslation();
+
+  useEffect(() => {
+    loadingStateRef.current = isLoading;
+    document.title = `${t('dataset.knowledge')} - Dify`;
+  }, [isLoading]);
+
+  useEffect(() => {
+    const onScroll = debounce(() => {
+      if (!loadingStateRef.current) {
+        const { scrollTop, clientHeight } = containerRef.current!;
+        const anchorOffset = anchorRef.current!.offsetTop;
+        if (anchorOffset - scrollTop - clientHeight < 100)
+          setSize(size => size + 1);
+      }
+    }, 50);
+
+    containerRef.current?.addEventListener('scroll', onScroll);
+    return () => containerRef.current?.removeEventListener('scroll', onScroll);
+  }, []);
+
+  // Flatten the datasets array and concatenate with mock data if no real data is available yet.
+  const allDatasets = data ? data.flatMap(({ data: datasets }) => datasets) : [];
+  const displayedDatasets = mockDatasets.length > 0 ? mockDatasets : mockDatasets;
+
+  return (
+    <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
+      {isCurrentWorkspaceEditor && <NewDatabaseCard ref={anchorRef} />}
+      {displayedDatasets.map(dataset => (
+      
+        <DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />
+      
+      ))}
+    </nav>
+  );
+};
+
+export default Database;

+ 220 - 0
app/(commonLayout)/database/DatabaseCard.tsx

@@ -0,0 +1,220 @@
+// DatasetCard.js
+import React, { useContext, useCallback, useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { RiMoreFill } from '@remixicon/react';
+import cn from 'classnames';
+import Confirm from '@/app/components/base/confirm';
+import { ToastContext } from '@/app/components/base/toast';
+import { checkIsUsedInApp, deleteDataset } from '@/service/datasets';
+import type { DataSet } from '@/models/datasets';
+import Tooltip from '@/app/components/base/tooltip';
+import { Folder } from '@/app/components/base/icons/src/vender/solid/files';
+import CustomPopover from '@/app/components/base/popover';
+import Divider from '@/app/components/base/divider';
+import RenameDatasetModal from '@/app/components/datasets/rename-modal';
+import TagSelector from '@/app/components/base/tag-management/selector';
+import CornerLabel from '@/app/components/base/corner-label';
+import { useAppContext } from '@/context/app-context';
+import { useRouter } from 'next/navigation';
+
+export type DatasetCardProps = {
+  dataset: DataSet,
+  onSuccess?: () => void,
+};
+
+const DatasetCard = ({ dataset, onSuccess }: DatasetCardProps) => {
+  const { t } = useTranslation();
+  const { notify } = useContext(ToastContext);
+  const { push } = useRouter();
+  const EXTERNAL_PROVIDER = 'external' as const;
+
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext();
+  const [tags, setTags] = useState<Tag[]>(dataset.tags);
+
+  const [showRenameModal, setShowRenameModal] = useState(false);
+  const [showConfirmDelete, setShowConfirmDelete] = useState(false);
+  const [confirmMessage, setConfirmMessage] = useState('');
+
+  const isExternalProvider = (provider: string): boolean => provider === EXTERNAL_PROVIDER;
+
+  const detectIsUsedByApp = useCallback(async () => {
+    try {
+      const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id);
+      setConfirmMessage(isUsedByApp ? t('dataset.datasetUsedByApp')! : t('dataset.deleteDatasetConfirmContent')!);
+    } catch (e: any) {
+      const res = await e.json();
+      notify({ type: 'error', message: res?.message || 'Unknown error' });
+    }
+    setShowConfirmDelete(true);
+  }, [dataset.id, notify, t]);
+
+  const onConfirmDelete = useCallback(async () => {
+    try {
+      await deleteDataset(dataset.id);
+      notify({ type: 'success', message: t('dataset.datasetDeleted') });
+      if (onSuccess) onSuccess();
+    } catch (e: any) {
+      // Handle errors here
+    }
+    setShowConfirmDelete(false);
+  }, [dataset.id, notify, onSuccess, t]);
+
+  const Operations = ({ showDelete, ...props }) => {
+    const onMouseLeave = async () => {
+      props.onClose?.();
+    };
+
+    const onClickRename = (e) => {
+      e.stopPropagation();
+      props.onClick?.();
+      e.preventDefault();
+      setShowRenameModal(true);
+    };
+
+    const onClickDelete = (e) => {
+      e.stopPropagation();
+      props.onClick?.();
+      e.preventDefault();
+      detectIsUsedByApp();
+    };
+
+    return (
+      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
+        <div
+          className='h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer'
+          onClick={onClickRename}
+        >
+          <span className='text-gray-700 text-sm'>{t('common.operation.settings')}</span>
+        </div>
+        {showDelete && (
+          <>
+            <Divider className="!my-1" />
+            <div
+              className='group h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-red-50 rounded-lg cursor-pointer'
+              onClick={onClickDelete}
+            >
+              <span className={cn('text-gray-700 text-sm', 'group-hover:text-red-500')}>
+                {t('common.operation.delete')}
+              </span>
+            </div>
+          </>
+        )}
+      </div>
+    );
+  };
+
+  useEffect(() => {
+    setTags(dataset.tags);
+  }, [dataset]);
+
+  return (
+    <>
+      <div
+        className='group relative col-span-1 bg-white border-[0.5px] border-solid border-transparent rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
+        data-disable-nprogress={true}
+        onClick={(e) => {
+          e.preventDefault();
+          isExternalProvider(dataset.provider)
+            ? push(`/database/${dataset.id}/hitTesting`)
+            : push(`/database/${dataset.id}/documents`);
+        }}
+      >
+        {isExternalProvider(dataset.provider) && (
+          <CornerLabel label='External' className='absolute right-0' labelClassName='rounded-tr-xl' />
+        )}
+        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
+          <div className={cn(
+            'shrink-0 flex items-center justify-center p-2.5 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}>
+            <Folder className='w-5 h-5 text-[#444CE7]' />
+          </div>
+          <div className='grow w-0 py-[1px]'>
+            <div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
+              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
+              {!dataset.embedding_available && (
+                <Tooltip popupContent={t('dataset.unavailableTip')}>
+                  <span className='shrink-0 inline-flex w-max ml-1 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
+                </Tooltip>
+              )}
+            </div>
+          </div>
+        </div>
+        <div
+          className={cn(
+            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-gray-500 group-hover:line-clamp-2 group-hover:max-h-[36px]',
+            tags.length ? 'line-clamp-2' : 'line-clamp-4',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}
+          title={dataset.description}
+        >
+          {dataset.description}
+        </div>
+        <div className={cn(
+          'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
+          tags.length ? 'flex' : '!hidden group-hover:!flex',
+        )}>
+          <div className={cn('grow flex items-center gap-1 w-0', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
+            e.stopPropagation();
+            e.preventDefault();
+          }}>
+            <div className={cn(
+              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
+              tags.length ? '!block' : '!hidden',
+            )}>
+              <TagSelector
+                position='bl'
+                type='knowledge'
+                targetID={dataset.id}
+                value={tags.map(tag => tag.id)}
+                selectedTags={tags}
+                onCacheUpdate={setTags}
+                onChange={onSuccess}
+              />
+            </div>
+          </div>
+          <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
+          <div className='!hidden group-hover:!flex shrink-0'>
+            <CustomPopover
+              htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
+              position="br"
+              trigger="click"
+              btnElement={
+                <div className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'>
+                  <RiMoreFill className='w-4 h-4 text-gray-700' />
+                </div>
+              }
+              btnClassName={open =>
+                cn(
+                  open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
+                  'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
+                )
+              }
+              className={'!w-[128px] h-fit !z-20'}
+            />
+          </div>
+        </div>
+      </div>
+      {showRenameModal && (
+        <RenameDatasetModal
+          show={showRenameModal}
+          dataset={dataset}
+          onClose={() => setShowRenameModal(false)}
+          onSuccess={onSuccess}
+        />
+      )}
+      {showConfirmDelete && (
+        <Confirm
+          title={t('dataset.deleteDatasetConfirmTitle')}
+          content={confirmMessage}
+          isShow={showConfirmDelete}
+          onConfirm={onConfirmDelete}
+          onCancel={() => setShowConfirmDelete(false)}
+        />
+      )}
+    </>
+  );
+};
+
+export default DatasetCard;
+

+ 240 - 0
app/(commonLayout)/database/DatasetCard.tsx

@@ -0,0 +1,240 @@
+'use client'
+
+import { useContext } from 'use-context-selector'
+import { useRouter } from 'next/navigation'
+import { useCallback, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { RiMoreFill } from '@remixicon/react'
+import cn from '@/utils/classnames'
+import Confirm from '@/app/components/base/confirm'
+import { ToastContext } from '@/app/components/base/toast'
+import { checkIsUsedInApp, deleteDataset } from '@/service/datasets'
+import type { DataSet } from '@/models/datasets'
+import Tooltip from '@/app/components/base/tooltip'
+import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
+import type { HtmlContentProps } from '@/app/components/base/popover'
+import CustomPopover from '@/app/components/base/popover'
+import Divider from '@/app/components/base/divider'
+import RenameDatasetModal from '@/app/components/datasets/rename-modal'
+import type { Tag } from '@/app/components/base/tag-management/constant'
+import TagSelector from '@/app/components/base/tag-management/selector'
+import CornerLabel from '@/app/components/base/corner-label'
+import { useAppContext } from '@/context/app-context'
+
+export type DatasetCardProps = {
+  dataset: DataSet
+  onSuccess?: () => void
+}
+
+const DatasetCard = ({
+  dataset,
+  onSuccess,
+}: DatasetCardProps) => {
+  const { t } = useTranslation()
+  const { notify } = useContext(ToastContext)
+  const { push } = useRouter()
+  const EXTERNAL_PROVIDER = 'external' as const
+
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
+  const [tags, setTags] = useState<Tag[]>(dataset.tags)
+
+  const [showRenameModal, setShowRenameModal] = useState(false)
+  const [showConfirmDelete, setShowConfirmDelete] = useState(false)
+  const [confirmMessage, setConfirmMessage] = useState<string>('')
+  const isExternalProvider = (provider: string): boolean => provider === EXTERNAL_PROVIDER
+  const detectIsUsedByApp = useCallback(async () => {
+    try {
+      const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id)
+      setConfirmMessage(isUsedByApp ? t('dataset.datasetUsedByApp')! : t('dataset.deleteDatasetConfirmContent')!)
+    }
+    catch (e: any) {
+      const res = await e.json()
+      notify({ type: 'error', message: res?.message || 'Unknown error' })
+    }
+
+    setShowConfirmDelete(true)
+  }, [dataset.id, notify, t])
+  const onConfirmDelete = useCallback(async () => {
+    try {
+      await deleteDataset(dataset.id)
+      notify({ type: 'success', message: t('dataset.datasetDeleted') })
+      if (onSuccess)
+        onSuccess()
+    }
+    catch (e: any) {
+    }
+    setShowConfirmDelete(false)
+  }, [dataset.id, notify, onSuccess, t])
+
+  const Operations = (props: HtmlContentProps & { showDelete: boolean }) => {
+    const onMouseLeave = async () => {
+      props.onClose?.()
+    }
+    const onClickRename = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      setShowRenameModal(true)
+    }
+    const onClickDelete = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      // detectIsUsedByApp()
+    }
+    return (
+      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
+        <div className='h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer' onClick={onClickRename}>
+          <span className='text-gray-700 text-sm'>{t('common.operation.settings')}</span>
+        </div>
+        {props.showDelete && (
+          <>
+            <Divider className="!my-1" />
+            <div
+              className='group h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-red-50 rounded-lg cursor-pointer'
+              onClick={onClickDelete}
+            >
+              <span className={cn('text-gray-700 text-sm', 'group-hover:text-red-500')}>
+                {t('common.operation.delete')}
+              </span>
+            </div>
+          </>
+        )}
+      </div>
+    )
+  }
+
+  useEffect(() => {
+    setTags(dataset.tags)
+  }, [dataset])
+
+  return (
+    <>
+      <div
+        className='group relative col-span-1 bg-white border-[0.5px] border-solid border-transparent rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
+        data-disable-nprogress={true}
+        onClick={(e) => {
+          e.preventDefault()
+          isExternalProvider(dataset.provider)
+            ? push(`/datasets/${dataset.id}/hitTesting`)
+            : push(`/datasets/${dataset.id}/documents`)
+        }}
+      >
+        {isExternalProvider(dataset.provider) && <CornerLabel label='External' className='absolute right-0' labelClassName='rounded-tr-xl' />}
+        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
+          <div className={cn(
+            'shrink-0 flex items-center justify-center p-2.5 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}>
+            <Folder className='w-5 h-5 text-[#444CE7]' />
+          </div>
+          <div className='grow w-0 py-[1px]'>
+            <div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
+              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
+              {!dataset.embedding_available && (
+                <Tooltip
+                  popupContent={t('dataset.unavailableTip')}
+                >
+                  <span className='shrink-0 inline-flex w-max ml-1 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
+                </Tooltip>
+              )}
+            </div>
+            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
+              <div
+                className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
+                title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
+              >
+                {dataset.provider === 'external'
+                  ? <>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                  : <>
+                    <span>{dataset.document_count}{t('dataset.documentCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{Math.round(dataset.word_count / 1000)}{t('dataset.wordCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                }
+              </div>
+            </div>
+          </div>
+        </div>
+        <div
+          className={cn(
+            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-gray-500 group-hover:line-clamp-2 group-hover:max-h-[36px]',
+            tags.length ? 'line-clamp-2' : 'line-clamp-4',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}
+          title={dataset.description}>
+          {dataset.description}
+        </div>
+        <div className={cn(
+          'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
+          tags.length ? 'flex' : '!hidden group-hover:!flex',
+        )}>
+          <div className={cn('grow flex items-center gap-1 w-0', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
+            e.stopPropagation()
+            e.preventDefault()
+          }}>
+            <div className={cn(
+              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
+              tags.length ? '!block' : '!hidden',
+            )}>
+              <TagSelector
+                position='bl'
+                type='knowledge'
+                targetID={dataset.id}
+                value={tags.map(tag => tag.id)}
+                selectedTags={tags}
+                onCacheUpdate={setTags}
+                onChange={onSuccess}
+              />
+            </div>
+          </div>
+          <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
+          <div className='!hidden group-hover:!flex shrink-0'>
+            <CustomPopover
+              htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
+              position="br"
+              trigger="click"
+              btnElement={
+                <div
+                  className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
+                >
+                  <RiMoreFill className='w-4 h-4 text-gray-700' />
+                </div>
+              }
+              btnClassName={open =>
+                cn(
+                  open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
+                  'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
+                )
+              }
+              className={'!w-[128px] h-fit !z-20'}
+            />
+          </div>
+        </div>
+      </div>
+      {showRenameModal && (
+        <RenameDatasetModal
+          show={showRenameModal}
+          dataset={dataset}
+          onClose={() => setShowRenameModal(false)}
+          onSuccess={onSuccess}
+        />
+      )}
+      {showConfirmDelete && (
+        <Confirm
+          title={t('dataset.deleteDatasetConfirmTitle')}
+          content={confirmMessage}
+          isShow={showConfirmDelete}
+          onConfirm={onConfirmDelete}
+          onCancel={() => setShowConfirmDelete(false)}
+        />
+      )}
+    </>
+  )
+}
+
+export default DatasetCard

+ 19 - 0
app/(commonLayout)/database/DatasetFooter.tsx

@@ -0,0 +1,19 @@
+'use client'
+
+import { useTranslation } from 'react-i18next'
+
+const DatasetFooter = () => {
+  const { t } = useTranslation()
+
+  return (
+    <footer className='px-12 py-6 grow-0 shrink-0'>
+      <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('dataset.didYouKnow')}</h3>
+      <p className='mt-1 text-sm font-normal leading-tight text-gray-700'>
+        {t('dataset.intro1')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro2')}</span>{t('dataset.intro3')}<br />
+        {t('dataset.intro4')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro5')}</span>{t('dataset.intro6')}
+      </p>
+    </footer>
+  )
+}
+
+export default DatasetFooter

+ 88 - 0
app/(commonLayout)/database/Datasets.tsx

@@ -0,0 +1,88 @@
+'use client'
+
+import { useEffect, useRef } from 'react'
+import useSWRInfinite from 'swr/infinite'
+import { debounce } from 'lodash-es'
+import { useTranslation } from 'react-i18next'
+import NewDatasetCard from './NewDatasetCard'
+import DatasetCard from './DatasetCard'
+import type { DataSetListResponse } from '@/models/datasets'
+import { fetchDatasets } from '@/service/datasets'
+import { useAppContext } from '@/context/app-context'
+
+const getKey = (
+  pageIndex: number,
+  previousPageData: DataSetListResponse,
+  tags: string[],
+  keyword: string,
+) => {
+  if (!pageIndex || previousPageData.has_more) {
+    const params: any = {
+      url: 'datasets',
+      params: {
+        page: pageIndex + 1,
+        limit: 30,
+      },
+    }
+    if (tags.length)
+      params.params.tag_ids = tags
+    if (keyword)
+      params.params.keyword = keyword
+    return params
+  }
+  return null
+}
+
+type Props = {
+  containerRef: React.RefObject<HTMLDivElement>
+  tags: string[]
+  keywords: string
+}
+
+const Datasets = ({
+  containerRef,
+  tags,
+  keywords,
+}: Props) => {
+  const { isCurrentWorkspaceEditor } = useAppContext()
+  const { data, isLoading, setSize, mutate } = useSWRInfinite(
+    (pageIndex: number, previousPageData: DataSetListResponse) => getKey(pageIndex, previousPageData, tags, keywords),
+    fetchDatasets,
+    { revalidateFirstPage: false, revalidateAll: true },
+  )
+  const loadingStateRef = useRef(false)
+  const anchorRef = useRef<HTMLAnchorElement>(null)
+
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    loadingStateRef.current = isLoading
+    document.title = `${t('dataset.knowledge')} - Dify`
+  }, [isLoading])
+
+  useEffect(() => {
+    const onScroll = debounce(() => {
+      if (!loadingStateRef.current) {
+        const { scrollTop, clientHeight } = containerRef.current!
+        const anchorOffset = anchorRef.current!.offsetTop
+        if (anchorOffset - scrollTop - clientHeight < 100)
+          setSize(size => size + 1)
+      }
+    }, 50)
+
+    containerRef.current?.addEventListener('scroll', onScroll)
+    return () => containerRef.current?.removeEventListener('scroll', onScroll)
+  }, [])
+
+  return (
+    <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
+      { isCurrentWorkspaceEditor && <NewDatasetCard ref={anchorRef} /> }
+      {data?.map(({ data: datasets }) => datasets.map(dataset => (
+        <DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
+      ))}
+    </nav>
+    
+  )
+}
+
+export default Datasets

+ 35 - 0
app/(commonLayout)/database/Doc.tsx

@@ -0,0 +1,35 @@
+'use client'
+
+import { type FC, useEffect } from 'react'
+import { useContext } from 'use-context-selector'
+import TemplateEn from './template/template.en.mdx'
+import TemplateZh from './template/template.zh.mdx'
+import I18n from '@/context/i18n'
+import { LanguagesSupported } from '@/i18n/language'
+
+type DocProps = {
+  apiBaseUrl: string
+}
+const Doc: FC<DocProps> = ({
+  apiBaseUrl,
+}) => {
+  const { locale } = useContext(I18n)
+
+  useEffect(() => {
+    const hash = location.hash
+    if (hash)
+      document.querySelector(hash)?.scrollIntoView()
+  }, [])
+
+  return (
+    <article className='mx-1 px-4 sm:mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
+      {
+        locale !== LanguagesSupported[1]
+          ? <TemplateEn apiBaseUrl={apiBaseUrl} />
+          : <TemplateZh apiBaseUrl={apiBaseUrl} />
+      }
+    </article>
+  )
+}
+
+export default Doc

+ 38 - 0
app/(commonLayout)/database/NewDatabaseCard.tsx

@@ -0,0 +1,38 @@
+'use client'
+
+import { forwardRef } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+  RiAddLine,
+  RiArrowRightLine,
+} from '@remixicon/react'
+
+const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
+  const { t } = useTranslation()
+
+  return (
+    <div style={{backgroundColor:'rgba(255,255,255,.6)'}} className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
+      min-h-[160px] transition-all duration-200 ease-in-out'
+    >
+      <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>
+        <div className='flex items-center gap-3'>
+          <div className='w-10 h-10 p-2 flex items-center justify-center border border-dashed border-divider-regular rounded-lg
+            bg-background-default-lighter group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
+          >
+            <RiAddLine className='w-4 h-4 text-text-tertiary group-hover:text-text-accent'/>
+          </div>
+          <div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDatabase')}</div>
+        </div>
+      </a>
+      <div className='p-4 pt-0 text-text-tertiary system-xs-regular'>{t('dataset.createDatasetIntro')}</div>
+      <a className='group flex p-4 items-center gap-1 border-t-[0.5px] border-divider-subtle rounded-b-xl cursor-pointer' href='/datasets/connect'>
+        <div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDatabase')}</div>
+        <RiArrowRightLine className='w-3.5 h-3.5 text-text-tertiary group-hover:text-text-accent' />
+      </a>
+    </div>
+  )
+})
+
+CreateAppCard.displayName = 'CreateAppCard'
+
+export default CreateAppCard

+ 38 - 0
app/(commonLayout)/database/NewDatasetCard.tsx

@@ -0,0 +1,38 @@
+'use client'
+
+import { forwardRef } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+  RiAddLine,
+  RiArrowRightLine,
+} from '@remixicon/react'
+
+const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
+      min-h-[160px] transition-all duration-200 ease-in-out'
+    >
+      <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>
+        <div className='flex items-center gap-3'>
+          <div className='w-10 h-10 p-2 flex items-center justify-center border border-dashed border-divider-regular rounded-lg
+            bg-background-default-lighter group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
+          >
+            <RiAddLine className='w-4 h-4 text-text-tertiary group-hover:text-text-accent'/>
+          </div>
+          <div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDataset')}</div>
+        </div>
+      </a>
+      <div className='p-4 pt-0 text-text-tertiary system-xs-regular'>{t('dataset.createDatasetIntro')}</div>
+      <a className='group flex p-4 items-center gap-1 border-t-[0.5px] border-divider-subtle rounded-b-xl cursor-pointer' href='/datasets/connect'>
+        <div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDataset')}</div>
+        <RiArrowRightLine className='w-3.5 h-3.5 text-text-tertiary group-hover:text-text-accent' />
+      </a>
+    </div>
+  )
+})
+
+CreateAppCard.displayName = 'CreateAppCard'
+
+export default CreateAppCard

+ 8 - 0
app/(commonLayout)/database/connect/page.tsx

@@ -0,0 +1,8 @@
+import React from 'react'
+import ExternalKnowledgeBaseConnector from '@/app/components/datasets/external-knowledge-base/connector'
+
+const ExternalKnowledgeBaseCreation = () => {
+  return <ExternalKnowledgeBaseConnector />
+}
+
+export default ExternalKnowledgeBaseCreation

+ 12 - 0
app/(commonLayout)/database/create/page.tsx

@@ -0,0 +1,12 @@
+import React from 'react'
+import DatasetUpdateForm from '@/app/components/datasets/create'
+
+type Props = {}
+
+const DatasetCreation = async (props: Props) => {
+  return (
+    <DatasetUpdateForm />
+  )
+}
+
+export default DatasetCreation

+ 11 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx

@@ -0,0 +1,11 @@
+import React from 'react'
+
+type Props = {}
+
+const page = (props: Props) => {
+  return (
+    <div>dataset detail api</div>
+  )
+}
+
+export default page

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import MainDetail from '@/app/components/datasets/documents/detail'
+
+export type IDocumentDetailProps = {
+  params: { datasetId: string; documentId: string }
+}
+
+const DocumentDetail = async ({
+  params: { datasetId, documentId },
+}: IDocumentDetailProps) => {
+  return (
+    <MainDetail datasetId={datasetId} documentId={documentId} />
+  )
+}
+
+export default DocumentDetail

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Settings from '@/app/components/datasets/documents/detail/settings'
+
+export type IProps = {
+  params: { datasetId: string; documentId: string }
+}
+
+const DocumentSettings = async ({
+  params: { datasetId, documentId },
+}: IProps) => {
+  return (
+    <Settings datasetId={datasetId} documentId={documentId} />
+  )
+}
+
+export default DocumentSettings

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import DatasetUpdateForm from '@/app/components/datasets/create'
+
+export type IProps = {
+  params: { datasetId: string }
+}
+
+const Create = async ({
+  params: { datasetId },
+}: IProps) => {
+  return (
+    <DatasetUpdateForm datasetId={datasetId} />
+  )
+}
+
+export default Create

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Main from '@/app/components/datasets/documents'
+
+export type IProps = {
+  params: { datasetId: string }
+}
+
+const Documents = async ({
+  params: { datasetId },
+}: IProps) => {
+  return (
+    <Main datasetId={datasetId} />
+  )
+}
+
+export default Documents

+ 9 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/documents/style.module.css

@@ -0,0 +1,9 @@
+.logTable td {
+  padding: 7px 8px;
+  box-sizing: border-box;
+  max-width: 200px;
+}
+
+.pagination li {
+  list-style: none;
+}

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import Main from '@/app/components/datasets/hit-testing'
+
+type Props = {
+  params: { datasetId: string }
+}
+
+const HitTesting = ({
+  params: { datasetId },
+}: Props) => {
+  return (
+    <Main datasetId={datasetId} />
+  )
+}
+
+export default HitTesting

+ 262 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx

@@ -0,0 +1,262 @@
+'use client'
+import type { FC, SVGProps } from 'react'
+import React, { useEffect, useMemo } from 'react'
+import { usePathname } from 'next/navigation'
+import useSWR from 'swr'
+import { useTranslation } from 'react-i18next'
+import { useBoolean } from 'ahooks'
+import {
+  Cog8ToothIcon,
+  // CommandLineIcon,
+  Squares2X2Icon,
+  // eslint-disable-next-line sort-imports
+  PuzzlePieceIcon,
+  DocumentTextIcon,
+  PaperClipIcon,
+  QuestionMarkCircleIcon,
+} from '@heroicons/react/24/outline'
+import {
+  Cog8ToothIcon as Cog8ToothSolidIcon,
+  // CommandLineIcon as CommandLineSolidIcon,
+  DocumentTextIcon as DocumentTextSolidIcon,
+} from '@heroicons/react/24/solid'
+import Link from 'next/link'
+import s from './style.module.css'
+import classNames from '@/utils/classnames'
+import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
+import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
+import AppSideBar from '@/app/components/app-sidebar'
+import Divider from '@/app/components/base/divider'
+import AppIcon from '@/app/components/base/app-icon'
+import Loading from '@/app/components/base/loading'
+import FloatPopoverContainer from '@/app/components/base/float-popover-container'
+import DatasetDetailContext from '@/context/dataset-detail'
+import { DataSourceType } from '@/models/datasets'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import { LanguagesSupported } from '@/i18n/language'
+import { useStore } from '@/app/components/app/store'
+import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
+import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
+import { getLocaleOnClient } from '@/i18n'
+import { useAppContext } from '@/context/app-context'
+
+export type IAppDetailLayoutProps = {
+  children: React.ReactNode
+  params: { datasetId: string }
+}
+
+type ILikedItemProps = {
+  type?: 'plugin' | 'app'
+  appStatus?: boolean
+  detail: RelatedApp
+  isMobile: boolean
+}
+
+const LikedItem = ({
+  type = 'app',
+  detail,
+  isMobile,
+}: ILikedItemProps) => {
+  return (
+    <Link className={classNames(s.itemWrapper, 'px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
+      <div className={classNames(s.iconWrapper, 'mr-0')}>
+        <AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
+        {type === 'app' && (
+          <span className='absolute bottom-[-2px] right-[-2px] w-3.5 h-3.5 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
+            {detail.mode === 'advanced-chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'agent-chat' && (
+              <CuteRobot className='w-2.5 h-2.5 text-indigo-600' />
+            )}
+            {detail.mode === 'chat' && (
+              <ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
+            )}
+            {detail.mode === 'completion' && (
+              <AiText className='w-2.5 h-2.5 text-[#0E9384]' />
+            )}
+            {detail.mode === 'workflow' && (
+              <Route className='w-2.5 h-2.5 text-[#f79009]' />
+            )}
+          </span>
+        )}
+      </div>
+      {!isMobile && <div className={classNames(s.appInfo, 'ml-2')}>{detail?.name || '--'}</div>}
+    </Link>
+  )
+}
+
+const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <g clipPath="url(#clip0_4610_6951)">
+      <path d="M10.6666 5.33325V3.33325L12.6666 1.33325L13.3332 2.66659L14.6666 3.33325L12.6666 5.33325H10.6666ZM10.6666 5.33325L7.9999 7.99988M14.6666 7.99992C14.6666 11.6818 11.6818 14.6666 7.99992 14.6666C4.31802 14.6666 1.33325 11.6818 1.33325 7.99992C1.33325 4.31802 4.31802 1.33325 7.99992 1.33325M11.3333 7.99992C11.3333 9.84087 9.84087 11.3333 7.99992 11.3333C6.15897 11.3333 4.66659 9.84087 4.66659 7.99992C4.66659 6.15897 6.15897 4.66659 7.99992 4.66659" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
+    </g>
+    <defs>
+      <clipPath id="clip0_4610_6951">
+        <rect width="16" height="16" fill="white" />
+      </clipPath>
+    </defs>
+  </svg>
+}
+
+const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <path fillRule="evenodd" clipRule="evenodd" d="M12.7733 0.67512C12.9848 0.709447 13.1669 0.843364 13.2627 1.03504L13.83 2.16961L14.9646 2.73689C15.1563 2.83273 15.2902 3.01486 15.3245 3.22639C15.3588 3.43792 15.2894 3.65305 15.1379 3.80458L13.1379 5.80458C13.0128 5.92961 12.8433 5.99985 12.6665 5.99985H10.9426L8.47124 8.47124C8.21089 8.73159 7.78878 8.73159 7.52843 8.47124C7.26808 8.21089 7.26808 7.78878 7.52843 7.52843L9.9998 5.05707V3.33318C9.9998 3.15637 10.07 2.9868 10.1951 2.86177L12.1951 0.861774C12.3466 0.710244 12.5617 0.640794 12.7733 0.67512Z" fill="#155EEF" />
+    <path d="M1.99984 7.99984C1.99984 4.68613 4.68613 1.99984 7.99984 1.99984C8.36803 1.99984 8.6665 1.70136 8.6665 1.33317C8.6665 0.964981 8.36803 0.666504 7.99984 0.666504C3.94975 0.666504 0.666504 3.94975 0.666504 7.99984C0.666504 12.0499 3.94975 15.3332 7.99984 15.3332C12.0499 15.3332 15.3332 12.0499 15.3332 7.99984C15.3332 7.63165 15.0347 7.33317 14.6665 7.33317C14.2983 7.33317 13.9998 7.63165 13.9998 7.99984C13.9998 11.3135 11.3135 13.9998 7.99984 13.9998C4.68613 13.9998 1.99984 11.3135 1.99984 7.99984Z" fill="#155EEF" />
+    <path d="M5.33317 7.99984C5.33317 6.52708 6.52708 5.33317 7.99984 5.33317C8.36803 5.33317 8.6665 5.03469 8.6665 4.6665C8.6665 4.29831 8.36803 3.99984 7.99984 3.99984C5.7907 3.99984 3.99984 5.7907 3.99984 7.99984C3.99984 10.209 5.7907 11.9998 7.99984 11.9998C10.209 11.9998 11.9998 10.209 11.9998 7.99984C11.9998 7.63165 11.7014 7.33317 11.3332 7.33317C10.965 7.33317 10.6665 7.63165 10.6665 7.99984C10.6665 9.4726 9.4726 10.6665 7.99984 10.6665C6.52708 10.6665 5.33317 9.4726 5.33317 7.99984Z" fill="#155EEF" />
+  </svg>
+}
+
+const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
+  return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
+    <path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
+    <path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
+  </svg>
+}
+
+type IExtraInfoProps = {
+  isMobile: boolean
+  relatedApps?: RelatedAppResponse
+}
+
+const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
+  const locale = getLocaleOnClient()
+  const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    setShowTips(!isMobile)
+  }, [isMobile, setShowTips])
+
+  return <div className='w-full flex flex-col items-center'>
+    <Divider className='mt-5' />
+    {(relatedApps?.data && relatedApps?.data?.length > 0) && (
+      <>
+        {!isMobile && <div className='w-full px-2 pb-1 pt-4 uppercase text-xs text-gray-500 font-medium'>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
+        {isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
+          {relatedApps?.total || '--'}
+          <PaperClipIcon className='h-4 w-4 text-gray-700' />
+        </div>}
+        {relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
+      </>
+    )}
+    {!relatedApps?.data?.length && (
+      <FloatPopoverContainer
+        placement='bottom-start'
+        open={isShowTips}
+        toggle={toggleTips}
+        isMobile={isMobile}
+        triggerElement={
+          <div className={classNames('h-7 w-7 inline-flex justify-center items-center rounded-lg bg-transparent', isShowTips && '!bg-gray-50')}>
+            <QuestionMarkCircleIcon className='h-4 w-4 flex-shrink-0 text-gray-500' />
+          </div>
+        }
+      >
+        <div className={classNames('mt-5 p-3', isMobile && 'border-[0.5px] border-gray-200 shadow-lg rounded-lg bg-white w-[160px]')}>
+          <div className='flex items-center justify-start gap-2'>
+            <div className={s.emptyIconDiv}>
+              <Squares2X2Icon className='w-3 h-3 text-gray-500' />
+            </div>
+            <div className={s.emptyIconDiv}>
+              <PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
+            </div>
+          </div>
+          <div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
+          <a
+            className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
+            href={
+              locale === LanguagesSupported[1]
+                ? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate_knowledge_within_application'
+                : 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
+            }
+            target='_blank' rel='noopener noreferrer'
+          >
+            <BookOpenIcon className='mr-1' />
+            {t('common.datasetMenus.viewDoc')}
+          </a>
+        </div>
+      </FloatPopoverContainer>
+    )}
+  </div>
+}
+
+const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
+  const {
+    children,
+    params: { datasetId },
+  } = props
+  const pathname = usePathname()
+  const hideSideBar = /documents\/create$/.test(pathname)
+  const { t } = useTranslation()
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
+  const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
+    url: 'fetchDatasetDetail',
+    datasetId,
+  }, apiParams => fetchDatasetDetail(apiParams.datasetId))
+
+  const { data: relatedApps } = useSWR({
+    action: 'fetchDatasetRelatedApps',
+    datasetId,
+  }, apiParams => fetchDatasetRelatedApps(apiParams.datasetId))
+
+  const navigation = useMemo(() => {
+    const baseNavigation = [
+      { name: t('common.datasetMenus.hitTesting'), href: `/datasets/${datasetId}/hitTesting`, icon: TargetIcon, selectedIcon: TargetSolidIcon },
+      // { name: 'api & webhook', href: `/datasets/${datasetId}/api`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
+      { name: t('common.datasetMenus.settings'), href: `/datasets/${datasetId}/settings`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon },
+    ]
+
+    if (datasetRes?.provider !== 'external') {
+      baseNavigation.unshift({
+        name: t('common.datasetMenus.documents'),
+        href: `/datasets/${datasetId}/documents`,
+        icon: DocumentTextIcon,
+        selectedIcon: DocumentTextSolidIcon,
+      })
+    }
+    return baseNavigation
+  }, [datasetRes?.provider, datasetId, t])
+
+  useEffect(() => {
+    if (datasetRes)
+      document.title = `${datasetRes.name || 'Dataset'} - Dify`
+  }, [datasetRes])
+
+  const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand)
+
+  useEffect(() => {
+    const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
+    const mode = isMobile ? 'collapse' : 'expand'
+    setAppSiderbarExpand(isMobile ? mode : localeMode)
+  }, [isMobile, setAppSiderbarExpand])
+
+  if (!datasetRes && !error)
+    return <Loading />
+
+  return (
+    <div className='grow flex overflow-hidden'>
+      {!hideSideBar && <AppSideBar
+        title={datasetRes?.name || '--'}
+        icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
+        icon_background={datasetRes?.icon_background || '#F5F5F5'}
+        desc={datasetRes?.description || '--'}
+        isExternal={datasetRes?.provider === 'external'}
+        navigation={navigation}
+        extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} /> : undefined}
+        iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
+      />}
+      <DatasetDetailContext.Provider value={{
+        indexingTechnique: datasetRes?.indexing_technique,
+        dataset: datasetRes,
+        mutateDatasetRes: () => mutateDatasetRes(),
+      }}>
+        <div className="bg-white grow overflow-hidden">{children}</div>
+      </DatasetDetailContext.Provider>
+    </div>
+  )
+}
+export default React.memo(DatasetDetailLayout)

+ 20 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx

@@ -0,0 +1,20 @@
+import React from 'react'
+import { getLocaleOnServer, useTranslation as translate } from '@/i18n/server'
+import Form from '@/app/components/datasets/settings/form'
+
+const Settings = async () => {
+  const locale = getLocaleOnServer()
+  const { t } = await translate(locale, 'dataset-settings')
+
+  return (
+    <div className='bg-white h-full overflow-y-auto'>
+      <div className='px-6 py-3'>
+        <div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
+        <div className='text-sm text-gray-500'>{t('desc')}</div>
+      </div>
+      <Form />
+    </div>
+  )
+}
+
+export default Settings

+ 18 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/[datasetId]/style.module.css

@@ -0,0 +1,18 @@
+.itemWrapper {
+  @apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
+}
+.appInfo {
+  @apply truncate text-gray-700 text-sm font-normal;
+}
+.iconWrapper {
+  @apply relative w-6 h-6 rounded-lg;
+}
+.statusPoint {
+  @apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
+}
+.subTitle {
+  @apply uppercase text-xs text-gray-500 font-medium px-3 pb-2 pt-4;
+}
+.emptyIconDiv {
+  @apply h-7 w-7 bg-gray-50 border border-[#EAECF5] inline-flex justify-center items-center rounded-lg;
+}

+ 16 - 0
app/(commonLayout)/database/datasets/(datasetDetailLayout)/layout.tsx

@@ -0,0 +1,16 @@
+import type { FC } from 'react'
+import React from 'react'
+
+export type IDatasetDetail = {
+  children: React.ReactNode
+}
+
+const AppDetail: FC<IDatasetDetail> = ({ children }) => {
+  return (
+    <>
+      {children}
+    </>
+  )
+}
+
+export default React.memo(AppDetail)

+ 41 - 0
app/(commonLayout)/database/datasets/ApiServer.tsx

@@ -0,0 +1,41 @@
+'use client'
+
+import type { FC } from 'react'
+import { useTranslation } from 'react-i18next'
+import CopyFeedback from '@/app/components/base/copy-feedback'
+import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
+import { randomString } from '@/utils'
+
+type ApiServerProps = {
+  apiBaseUrl: string
+}
+const ApiServer: FC<ApiServerProps> = ({
+  apiBaseUrl,
+}) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='flex items-center flex-wrap gap-y-2'>
+      <div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg leading-5'>
+        <div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md shrink-0'>{t('appApi.apiServer')}</div>
+        <div className='px-1 truncate w-fit sm:w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
+        <div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
+        <CopyFeedback
+          content={apiBaseUrl}
+          selectorId={randomString(8)}
+          className={'!w-6 !h-6 hover:bg-gray-200'}
+        />
+      </div>
+      <div className='flex items-center mr-2 px-3 h-8 bg-[#ECFDF3] text-xs font-semibold text-[#039855] rounded-lg border-[0.5px] border-[#D1FADF]'>
+        {t('appApi.ok')}
+      </div>
+      <SecretKeyButton
+        className='flex-shrink-0 !h-8 bg-white'
+        textCls='!text-gray-700 font-medium'
+        iconCls='stroke-[1.2px]'
+      />
+    </div>
+  )
+}
+
+export default ApiServer

+ 129 - 0
app/(commonLayout)/database/datasets/Container.tsx

@@ -0,0 +1,129 @@
+'use client'
+
+// Libraries
+import { useEffect, useMemo, useRef, useState } from 'react'
+import { useRouter } from 'next/navigation'
+import { useTranslation } from 'react-i18next'
+import { useDebounceFn } from 'ahooks'
+
+// Components
+import ExternalAPIPanel from '../../components/datasets/external-api/external-api-panel'
+import Datasets from './Datasets'
+import Database from './Database'
+
+import DatasetFooter from './DatasetFooter'
+import ApiServer from './ApiServer'
+import Doc from './Doc'
+import TabSliderNew from '@/app/components/base/tab-slider-new'
+import TagManagementModal from '@/app/components/base/tag-management'
+import TagFilter from '@/app/components/base/tag-management/filter'
+import Button from '@/app/components/base/button'
+import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
+import SearchInput from '@/app/components/base/search-input'
+
+// Services
+import { fetchDatasetApiBaseUrl } from '@/service/datasets'
+
+// Hooks
+import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
+import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
+import { useAppContext } from '@/context/app-context'
+import { useExternalApiPanel } from '@/context/external-api-panel-context'
+// eslint-disable-next-line import/order
+import { useQuery } from '@tanstack/react-query'
+
+const Container = () => {
+  const { t } = useTranslation()
+  const router = useRouter()
+  const { currentWorkspace } = useAppContext()
+  const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
+  const { showExternalApiPanel, setShowExternalApiPanel } = useExternalApiPanel()
+
+  const options = useMemo(() => {
+    return [
+      { value: 'dataset', text: t('dataset.datasets') },
+      ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]),
+    ]
+  }, [currentWorkspace.role, t])
+
+  const [activeTab, setActiveTab] = useTabSearchParams({
+    defaultTab: 'dataset',
+  })
+  const containerRef = useRef<HTMLDivElement>(null)
+  const { data } = useQuery(
+    {
+      queryKey: ['datasetApiBaseInfo'],
+      queryFn: () => fetchDatasetApiBaseUrl('/datasets/api-base-info'),
+      enabled: activeTab !== 'dataset',
+    },
+  )
+
+  const [keywords, setKeywords] = useState('')
+  const [searchKeywords, setSearchKeywords] = useState('')
+  const { run: handleSearch } = useDebounceFn(() => {
+    setSearchKeywords(keywords)
+  }, { wait: 500 })
+  const handleKeywordsChange = (value: string) => {
+    setKeywords(value)
+    handleSearch()
+  }
+  const [tagFilterValue, setTagFilterValue] = useState<string[]>([])
+  const [tagIDs, setTagIDs] = useState<string[]>([])
+  const { run: handleTagsUpdate } = useDebounceFn(() => {
+    setTagIDs(tagFilterValue)
+  }, { wait: 500 })
+  const handleTagsChange = (value: string[]) => {
+    setTagFilterValue(value)
+    handleTagsUpdate()
+  }
+
+  useEffect(() => {
+    if (currentWorkspace.role === 'normal')
+      return router.replace('/apps')
+  }, [currentWorkspace, router])
+
+  return (
+    <div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
+      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
+        <TabSliderNew
+          value={activeTab}
+          onChange={newActiveTab => setActiveTab(newActiveTab)}
+          options={options}
+        />
+        {activeTab === 'dataset' && (
+          <div className='flex items-center gap-2'>
+            <TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
+            <SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
+            <div className="w-[1px] h-4 bg-divider-regular" />
+            <Button
+              className='gap-0.5 shadows-shadow-xs'
+              onClick={() => setShowExternalApiPanel(true)}
+            >
+              <ApiConnectionMod className='w-4 h-4 text-components-button-secondary-text' />
+              <div className='flex px-0.5 justify-center items-center gap-1 text-components-button-secondary-text system-sm-medium'>{t('dataset.externalAPIPanelTitle')}</div>
+            </Button>
+          </div>
+        )}
+        {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
+      </div>
+      {activeTab === 'dataset' && (
+        <>
+          <Datasets containerRef={containerRef} tags={tagIDs} keywords={searchKeywords} />
+         
+
+          <Database containerRef={containerRef} tags={tagIDs} keywords={searchKeywords} />
+          {/* <DatasetFooter /> */}
+          {showTagManagementModal && (
+            <TagManagementModal type='knowledge' show={showTagManagementModal} />
+          )}
+        </>
+
+      )}
+      {activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />}
+
+      {showExternalApiPanel && <ExternalAPIPanel onClose={() => setShowExternalApiPanel(false)} />}
+    </div>
+  )
+}
+
+export default Container

+ 88 - 0
app/(commonLayout)/database/datasets/Database.tsx

@@ -0,0 +1,88 @@
+'use client'
+
+import { useEffect, useRef } from 'react'
+import useSWRInfinite from 'swr/infinite'
+import { debounce } from 'lodash-es'
+import { useTranslation } from 'react-i18next'
+import NewDatabaseCard from './NewDatabaseCard'
+import DatabaseCard from './DatabaseCard'
+import type { DataSetListResponse } from '@/models/datasets'
+import { fetchDatasets } from '@/service/datasets'
+import { useAppContext } from '@/context/app-context'
+
+const getKey = (
+  pageIndex: number,
+  previousPageData: DataSetListResponse,
+  tags: string[],
+  keyword: string,
+) => {
+  if (!pageIndex || previousPageData.has_more) {
+    const params: any = {
+      url: 'datasets',
+      params: {
+        page: pageIndex + 1,
+        limit: 30,
+      },
+    }
+    if (tags.length)
+      params.params.tag_ids = tags
+    if (keyword)
+      params.params.keyword = keyword
+    return params
+  }
+  return null
+}
+
+type Props = {
+  containerRef: React.RefObject<HTMLDivElement>
+  tags: string[]
+  keywords: string
+}
+
+const Database = ({
+  containerRef,
+  tags,
+  keywords,
+}: Props) => {
+  const { isCurrentWorkspaceEditor } = useAppContext()
+  const { data, isLoading, setSize, mutate } = useSWRInfinite(
+    (pageIndex: number, previousPageData: DataSetListResponse) => getKey(pageIndex, previousPageData, tags, keywords),
+    fetchDatasets,
+    { revalidateFirstPage: false, revalidateAll: true },
+  )
+  const loadingStateRef = useRef(false)
+  const anchorRef = useRef<HTMLAnchorElement>(null)
+
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    loadingStateRef.current = isLoading
+    document.title = `${t('dataset.knowledge')} - Dify`
+  }, [isLoading])
+
+  useEffect(() => {
+    const onScroll = debounce(() => {
+      if (!loadingStateRef.current) {
+        const { scrollTop, clientHeight } = containerRef.current!
+        const anchorOffset = anchorRef.current!.offsetTop
+        if (anchorOffset - scrollTop - clientHeight < 100)
+          setSize(size => size + 1)
+      }
+    }, 50)
+
+    containerRef.current?.addEventListener('scroll', onScroll)
+    return () => containerRef.current?.removeEventListener('scroll', onScroll)
+  }, [])
+
+  return (
+    <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
+      { isCurrentWorkspaceEditor && <NewDatabaseCard ref={anchorRef} /> }
+      {data?.map(({ data: datasets }) => datasets.map(dataset => (
+        <DatabaseCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
+      ))}
+    </nav>
+    
+  )
+}
+
+export default Database

+ 240 - 0
app/(commonLayout)/database/datasets/DatabaseCard.tsx

@@ -0,0 +1,240 @@
+'use client'
+
+import { useContext } from 'use-context-selector'
+import { useRouter } from 'next/navigation'
+import { useCallback, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { RiMoreFill } from '@remixicon/react'
+import cn from '@/utils/classnames'
+import Confirm from '@/app/components/base/confirm'
+import { ToastContext } from '@/app/components/base/toast'
+import { checkIsUsedInApp, deleteDataset } from '@/service/datasets'
+import type { DataSet } from '@/models/datasets'
+import Tooltip from '@/app/components/base/tooltip'
+import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
+import type { HtmlContentProps } from '@/app/components/base/popover'
+import CustomPopover from '@/app/components/base/popover'
+import Divider from '@/app/components/base/divider'
+import RenameDatasetModal from '@/app/components/datasets/rename-modal'
+import type { Tag } from '@/app/components/base/tag-management/constant'
+import TagSelector from '@/app/components/base/tag-management/selector'
+import CornerLabel from '@/app/components/base/corner-label'
+import { useAppContext } from '@/context/app-context'
+
+export type DatasetCardProps = {
+  dataset: DataSet
+  onSuccess?: () => void
+}
+
+const DatasetCard = ({
+  dataset,
+  onSuccess,
+}: DatasetCardProps) => {
+  const { t } = useTranslation()
+  const { notify } = useContext(ToastContext)
+  const { push } = useRouter()
+  const EXTERNAL_PROVIDER = 'external' as const
+
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
+  const [tags, setTags] = useState<Tag[]>(dataset.tags)
+
+  const [showRenameModal, setShowRenameModal] = useState(false)
+  const [showConfirmDelete, setShowConfirmDelete] = useState(false)
+  const [confirmMessage, setConfirmMessage] = useState<string>('')
+  const isExternalProvider = (provider: string): boolean => provider === EXTERNAL_PROVIDER
+  const detectIsUsedByApp = useCallback(async () => {
+    try {
+      const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id)
+      setConfirmMessage(isUsedByApp ? t('dataset.datasetUsedByApp')! : t('dataset.deleteDatasetConfirmContent')!)
+    }
+    catch (e: any) {
+      const res = await e.json()
+      notify({ type: 'error', message: res?.message || 'Unknown error' })
+    }
+
+    setShowConfirmDelete(true)
+  }, [dataset.id, notify, t])
+  const onConfirmDelete = useCallback(async () => {
+    try {
+      await deleteDataset(dataset.id)
+      notify({ type: 'success', message: t('dataset.datasetDeleted') })
+      if (onSuccess)
+        onSuccess()
+    }
+    catch (e: any) {
+    }
+    setShowConfirmDelete(false)
+  }, [dataset.id, notify, onSuccess, t])
+
+  const Operations = (props: HtmlContentProps & { showDelete: boolean }) => {
+    const onMouseLeave = async () => {
+      props.onClose?.()
+    }
+    const onClickRename = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      setShowRenameModal(true)
+    }
+    const onClickDelete = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      detectIsUsedByApp()
+    }
+    return (
+      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
+        <div className='h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer' onClick={onClickRename}>
+          <span className='text-gray-700 text-sm'>{t('common.operation.settings')}</span>
+        </div>
+        {props.showDelete && (
+          <>
+            <Divider className="!my-1" />
+            <div
+              className='group h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-red-50 rounded-lg cursor-pointer'
+              onClick={onClickDelete}
+            >
+              <span className={cn('text-gray-700 text-sm', 'group-hover:text-red-500')}>
+                {t('common.operation.delete')}
+              </span>
+            </div>
+          </>
+        )}
+      </div>
+    )
+  }
+
+  useEffect(() => {
+    setTags(dataset.tags)
+  }, [dataset])
+
+  return (
+    <>
+      <div
+        className='group relative col-span-1 bg-white border-[0.5px] border-solid border-transparent rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
+        data-disable-nprogress={true}
+        onClick={(e) => {
+          e.preventDefault()
+          isExternalProvider(dataset.provider)
+            ? push(`/datasets/${dataset.id}/hitTesting`)
+            : push(`/datasets/${dataset.id}/documents`)
+        }}
+      >
+        {isExternalProvider(dataset.provider) && <CornerLabel label='External' className='absolute right-0' labelClassName='rounded-tr-xl' />}
+        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
+          <div className={cn(
+            'shrink-0 flex items-center justify-center p-2.5 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}>
+            <Folder className='w-5 h-5 text-[#444CE7]' />
+          </div>
+          <div className='grow w-0 py-[1px]'>
+            <div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
+              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
+              {!dataset.embedding_available && (
+                <Tooltip
+                  popupContent={t('dataset.unavailableTip')}
+                >
+                  <span className='shrink-0 inline-flex w-max ml-1 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
+                </Tooltip>
+              )}
+            </div>
+            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
+              <div
+                className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
+                title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
+              >
+                {dataset.provider === 'external'
+                  ? <>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                  : <>
+                    <span>{dataset.document_count}{t('dataset.documentCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{Math.round(dataset.word_count / 1000)}{t('dataset.wordCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                }
+              </div>
+            </div>
+          </div>
+        </div>
+        <div
+          className={cn(
+            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-gray-500 group-hover:line-clamp-2 group-hover:max-h-[36px]',
+            tags.length ? 'line-clamp-2' : 'line-clamp-4',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}
+          title={dataset.description}>
+          {dataset.description}
+        </div>
+        <div className={cn(
+          'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
+          tags.length ? 'flex' : '!hidden group-hover:!flex',
+        )}>
+          <div className={cn('grow flex items-center gap-1 w-0', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
+            e.stopPropagation()
+            e.preventDefault()
+          }}>
+            <div className={cn(
+              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
+              tags.length ? '!block' : '!hidden',
+            )}>
+              <TagSelector
+                position='bl'
+                type='knowledge'
+                targetID={dataset.id}
+                value={tags.map(tag => tag.id)}
+                selectedTags={tags}
+                onCacheUpdate={setTags}
+                onChange={onSuccess}
+              />
+            </div>
+          </div>
+          <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
+          <div className='!hidden group-hover:!flex shrink-0'>
+            <CustomPopover
+              htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
+              position="br"
+              trigger="click"
+              btnElement={
+                <div
+                  className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
+                >
+                  <RiMoreFill className='w-4 h-4 text-gray-700' />
+                </div>
+              }
+              btnClassName={open =>
+                cn(
+                  open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
+                  'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
+                )
+              }
+              className={'!w-[128px] h-fit !z-20'}
+            />
+          </div>
+        </div>
+      </div>
+      {showRenameModal && (
+        <RenameDatasetModal
+          show={showRenameModal}
+          dataset={dataset}
+          onClose={() => setShowRenameModal(false)}
+          onSuccess={onSuccess}
+        />
+      )}
+      {showConfirmDelete && (
+        <Confirm
+          title={t('dataset.deleteDatasetConfirmTitle')}
+          content={confirmMessage}
+          isShow={showConfirmDelete}
+          onConfirm={onConfirmDelete}
+          onCancel={() => setShowConfirmDelete(false)}
+        />
+      )}
+    </>
+  )
+}
+
+export default DatasetCard

+ 240 - 0
app/(commonLayout)/database/datasets/DatasetCard.tsx

@@ -0,0 +1,240 @@
+'use client'
+
+import { useContext } from 'use-context-selector'
+import { useRouter } from 'next/navigation'
+import { useCallback, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { RiMoreFill } from '@remixicon/react'
+import cn from '@/utils/classnames'
+import Confirm from '@/app/components/base/confirm'
+import { ToastContext } from '@/app/components/base/toast'
+import { checkIsUsedInApp, deleteDataset } from '@/service/datasets'
+import type { DataSet } from '@/models/datasets'
+import Tooltip from '@/app/components/base/tooltip'
+import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
+import type { HtmlContentProps } from '@/app/components/base/popover'
+import CustomPopover from '@/app/components/base/popover'
+import Divider from '@/app/components/base/divider'
+import RenameDatasetModal from '@/app/components/datasets/rename-modal'
+import type { Tag } from '@/app/components/base/tag-management/constant'
+import TagSelector from '@/app/components/base/tag-management/selector'
+import CornerLabel from '@/app/components/base/corner-label'
+import { useAppContext } from '@/context/app-context'
+
+export type DatasetCardProps = {
+  dataset: DataSet
+  onSuccess?: () => void
+}
+
+const DatasetCard = ({
+  dataset,
+  onSuccess,
+}: DatasetCardProps) => {
+  const { t } = useTranslation()
+  const { notify } = useContext(ToastContext)
+  const { push } = useRouter()
+  const EXTERNAL_PROVIDER = 'external' as const
+
+  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
+  const [tags, setTags] = useState<Tag[]>(dataset.tags)
+
+  const [showRenameModal, setShowRenameModal] = useState(false)
+  const [showConfirmDelete, setShowConfirmDelete] = useState(false)
+  const [confirmMessage, setConfirmMessage] = useState<string>('')
+  const isExternalProvider = (provider: string): boolean => provider === EXTERNAL_PROVIDER
+  const detectIsUsedByApp = useCallback(async () => {
+    try {
+      const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id)
+      setConfirmMessage(isUsedByApp ? t('dataset.datasetUsedByApp')! : t('dataset.deleteDatasetConfirmContent')!)
+    }
+    catch (e: any) {
+      const res = await e.json()
+      notify({ type: 'error', message: res?.message || 'Unknown error' })
+    }
+
+    setShowConfirmDelete(true)
+  }, [dataset.id, notify, t])
+  const onConfirmDelete = useCallback(async () => {
+    try {
+      await deleteDataset(dataset.id)
+      notify({ type: 'success', message: t('dataset.datasetDeleted') })
+      if (onSuccess)
+        onSuccess()
+    }
+    catch (e: any) {
+    }
+    setShowConfirmDelete(false)
+  }, [dataset.id, notify, onSuccess, t])
+
+  const Operations = (props: HtmlContentProps & { showDelete: boolean }) => {
+    const onMouseLeave = async () => {
+      props.onClose?.()
+    }
+    const onClickRename = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      setShowRenameModal(true)
+    }
+    const onClickDelete = async (e: React.MouseEvent<HTMLDivElement>) => {
+      e.stopPropagation()
+      props.onClick?.()
+      e.preventDefault()
+      detectIsUsedByApp()
+    }
+    return (
+      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
+        <div className='h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer' onClick={onClickRename}>
+          <span className='text-gray-700 text-sm'>{t('common.operation.settings')}</span>
+        </div>
+        {props.showDelete && (
+          <>
+            <Divider className="!my-1" />
+            <div
+              className='group h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-red-50 rounded-lg cursor-pointer'
+              onClick={onClickDelete}
+            >
+              <span className={cn('text-gray-700 text-sm', 'group-hover:text-red-500')}>
+                {t('common.operation.delete')}
+              </span>
+            </div>
+          </>
+        )}
+      </div>
+    )
+  }
+
+  useEffect(() => {
+    setTags(dataset.tags)
+  }, [dataset])
+
+  return (
+    <>
+      <div
+        className='group relative col-span-1 bg-white border-[0.5px] border-solid border-transparent rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
+        data-disable-nprogress={true}
+        onClick={(e) => {
+          e.preventDefault()
+          isExternalProvider(dataset.provider)
+            ? push(`/datasets/${dataset.id}/hitTesting`)
+            : push(`/datasets/${dataset.id}/documents`)
+        }}
+      >
+        {isExternalProvider(dataset.provider) && <CornerLabel label='External' className='absolute right-0' labelClassName='rounded-tr-xl' />}
+        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
+          <div className={cn(
+            'shrink-0 flex items-center justify-center p-2.5 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}>
+            <Folder className='w-5 h-5 text-[#444CE7]' />
+          </div>
+          <div className='grow w-0 py-[1px]'>
+            <div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
+              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
+              {!dataset.embedding_available && (
+                <Tooltip
+                  popupContent={t('dataset.unavailableTip')}
+                >
+                  <span className='shrink-0 inline-flex w-max ml-1 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
+                </Tooltip>
+              )}
+            </div>
+            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
+              <div
+                className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
+                title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
+              >
+                {dataset.provider === 'external'
+                  ? <>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                  : <>
+                    <span>{dataset.document_count}{t('dataset.documentCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{Math.round(dataset.word_count / 1000)}{t('dataset.wordCount')}</span>
+                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
+                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
+                  </>
+                }
+              </div>
+            </div>
+          </div>
+        </div>
+        <div
+          className={cn(
+            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-gray-500 group-hover:line-clamp-2 group-hover:max-h-[36px]',
+            tags.length ? 'line-clamp-2' : 'line-clamp-4',
+            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
+          )}
+          title={dataset.description}>
+          {dataset.description}
+        </div>
+        <div className={cn(
+          'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
+          tags.length ? 'flex' : '!hidden group-hover:!flex',
+        )}>
+          <div className={cn('grow flex items-center gap-1 w-0', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
+            e.stopPropagation()
+            e.preventDefault()
+          }}>
+            <div className={cn(
+              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
+              tags.length ? '!block' : '!hidden',
+            )}>
+              <TagSelector
+                position='bl'
+                type='knowledge'
+                targetID={dataset.id}
+                value={tags.map(tag => tag.id)}
+                selectedTags={tags}
+                onCacheUpdate={setTags}
+                onChange={onSuccess}
+              />
+            </div>
+          </div>
+          <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
+          <div className='!hidden group-hover:!flex shrink-0'>
+            <CustomPopover
+              htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
+              position="br"
+              trigger="click"
+              btnElement={
+                <div
+                  className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
+                >
+                  <RiMoreFill className='w-4 h-4 text-gray-700' />
+                </div>
+              }
+              btnClassName={open =>
+                cn(
+                  open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
+                  'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
+                )
+              }
+              className={'!w-[128px] h-fit !z-20'}
+            />
+          </div>
+        </div>
+      </div>
+      {showRenameModal && (
+        <RenameDatasetModal
+          show={showRenameModal}
+          dataset={dataset}
+          onClose={() => setShowRenameModal(false)}
+          onSuccess={onSuccess}
+        />
+      )}
+      {showConfirmDelete && (
+        <Confirm
+          title={t('dataset.deleteDatasetConfirmTitle')}
+          content={confirmMessage}
+          isShow={showConfirmDelete}
+          onConfirm={onConfirmDelete}
+          onCancel={() => setShowConfirmDelete(false)}
+        />
+      )}
+    </>
+  )
+}
+
+export default DatasetCard

+ 19 - 0
app/(commonLayout)/database/datasets/DatasetFooter.tsx

@@ -0,0 +1,19 @@
+'use client'
+
+import { useTranslation } from 'react-i18next'
+
+const DatasetFooter = () => {
+  const { t } = useTranslation()
+
+  return (
+    <footer className='px-12 py-6 grow-0 shrink-0'>
+      <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('dataset.didYouKnow')}</h3>
+      <p className='mt-1 text-sm font-normal leading-tight text-gray-700'>
+        {t('dataset.intro1')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro2')}</span>{t('dataset.intro3')}<br />
+        {t('dataset.intro4')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro5')}</span>{t('dataset.intro6')}
+      </p>
+    </footer>
+  )
+}
+
+export default DatasetFooter

+ 88 - 0
app/(commonLayout)/database/datasets/Datasets.tsx

@@ -0,0 +1,88 @@
+'use client'
+
+import { useEffect, useRef } from 'react'
+import useSWRInfinite from 'swr/infinite'
+import { debounce } from 'lodash-es'
+import { useTranslation } from 'react-i18next'
+import NewDatasetCard from './NewDatasetCard'
+import DatasetCard from './DatasetCard'
+import type { DataSetListResponse } from '@/models/datasets'
+import { fetchDatasets } from '@/service/datasets'
+import { useAppContext } from '@/context/app-context'
+
+const getKey = (
+  pageIndex: number,
+  previousPageData: DataSetListResponse,
+  tags: string[],
+  keyword: string,
+) => {
+  if (!pageIndex || previousPageData.has_more) {
+    const params: any = {
+      url: 'datasets',
+      params: {
+        page: pageIndex + 1,
+        limit: 30,
+      },
+    }
+    if (tags.length)
+      params.params.tag_ids = tags
+    if (keyword)
+      params.params.keyword = keyword
+    return params
+  }
+  return null
+}
+
+type Props = {
+  containerRef: React.RefObject<HTMLDivElement>
+  tags: string[]
+  keywords: string
+}
+
+const Datasets = ({
+  containerRef,
+  tags,
+  keywords,
+}: Props) => {
+  const { isCurrentWorkspaceEditor } = useAppContext()
+  const { data, isLoading, setSize, mutate } = useSWRInfinite(
+    (pageIndex: number, previousPageData: DataSetListResponse) => getKey(pageIndex, previousPageData, tags, keywords),
+    fetchDatasets,
+    { revalidateFirstPage: false, revalidateAll: true },
+  )
+  const loadingStateRef = useRef(false)
+  const anchorRef = useRef<HTMLAnchorElement>(null)
+
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    loadingStateRef.current = isLoading
+    document.title = `${t('dataset.knowledge')} - Dify`
+  }, [isLoading])
+
+  useEffect(() => {
+    const onScroll = debounce(() => {
+      if (!loadingStateRef.current) {
+        const { scrollTop, clientHeight } = containerRef.current!
+        const anchorOffset = anchorRef.current!.offsetTop
+        if (anchorOffset - scrollTop - clientHeight < 100)
+          setSize(size => size + 1)
+      }
+    }, 50)
+
+    containerRef.current?.addEventListener('scroll', onScroll)
+    return () => containerRef.current?.removeEventListener('scroll', onScroll)
+  }, [])
+
+  return (
+    <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
+      { isCurrentWorkspaceEditor && <NewDatasetCard ref={anchorRef} /> }
+      {data?.map(({ data: datasets }) => datasets.map(dataset => (
+        <DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
+      ))}
+    </nav>
+    
+  )
+}
+
+export default Datasets

+ 35 - 0
app/(commonLayout)/database/datasets/Doc.tsx

@@ -0,0 +1,35 @@
+'use client'
+
+import { type FC, useEffect } from 'react'
+import { useContext } from 'use-context-selector'
+import TemplateEn from './template/template.en.mdx'
+import TemplateZh from './template/template.zh.mdx'
+import I18n from '@/context/i18n'
+import { LanguagesSupported } from '@/i18n/language'
+
+type DocProps = {
+  apiBaseUrl: string
+}
+const Doc: FC<DocProps> = ({
+  apiBaseUrl,
+}) => {
+  const { locale } = useContext(I18n)
+
+  useEffect(() => {
+    const hash = location.hash
+    if (hash)
+      document.querySelector(hash)?.scrollIntoView()
+  }, [])
+
+  return (
+    <article className='mx-1 px-4 sm:mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
+      {
+        locale !== LanguagesSupported[1]
+          ? <TemplateEn apiBaseUrl={apiBaseUrl} />
+          : <TemplateZh apiBaseUrl={apiBaseUrl} />
+      }
+    </article>
+  )
+}
+
+export default Doc

+ 38 - 0
app/(commonLayout)/database/datasets/NewDatabaseCard.tsx

@@ -0,0 +1,38 @@
+'use client'
+
+import { forwardRef } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+  RiAddLine,
+  RiArrowRightLine,
+} from '@remixicon/react'
+
+const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
+      min-h-[160px] transition-all duration-200 ease-in-out'
+    >
+      <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>
+        <div className='flex items-center gap-3'>
+          <div className='w-10 h-10 p-2 flex items-center justify-center border border-dashed border-divider-regular rounded-lg
+            bg-background-default-lighter group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
+          >
+            <RiAddLine className='w-4 h-4 text-text-tertiary group-hover:text-text-accent'/>
+          </div>
+          <div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDatabase')}</div>
+        </div>
+      </a>
+      <div className='p-4 pt-0 text-text-tertiary system-xs-regular'>{t('dataset.createDatasetIntro')}</div>
+      <a className='group flex p-4 items-center gap-1 border-t-[0.5px] border-divider-subtle rounded-b-xl cursor-pointer' href='/datasets/connect'>
+        <div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDatabase')}</div>
+        <RiArrowRightLine className='w-3.5 h-3.5 text-text-tertiary group-hover:text-text-accent' />
+      </a>
+    </div>
+  )
+})
+
+CreateAppCard.displayName = 'CreateAppCard'
+
+export default CreateAppCard

+ 38 - 0
app/(commonLayout)/database/datasets/NewDatasetCard.tsx

@@ -0,0 +1,38 @@
+'use client'
+
+import { forwardRef } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+  RiAddLine,
+  RiArrowRightLine,
+} from '@remixicon/react'
+
+const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
+  const { t } = useTranslation()
+
+  return (
+    <div className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
+      min-h-[160px] transition-all duration-200 ease-in-out'
+    >
+      <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>
+        <div className='flex items-center gap-3'>
+          <div className='w-10 h-10 p-2 flex items-center justify-center border border-dashed border-divider-regular rounded-lg
+            bg-background-default-lighter group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
+          >
+            <RiAddLine className='w-4 h-4 text-text-tertiary group-hover:text-text-accent'/>
+          </div>
+          <div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDataset')}</div>
+        </div>
+      </a>
+      <div className='p-4 pt-0 text-text-tertiary system-xs-regular'>{t('dataset.createDatasetIntro')}</div>
+      <a className='group flex p-4 items-center gap-1 border-t-[0.5px] border-divider-subtle rounded-b-xl cursor-pointer' href='/datasets/connect'>
+        <div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDataset')}</div>
+        <RiArrowRightLine className='w-3.5 h-3.5 text-text-tertiary group-hover:text-text-accent' />
+      </a>
+    </div>
+  )
+})
+
+CreateAppCard.displayName = 'CreateAppCard'
+
+export default CreateAppCard

+ 8 - 0
app/(commonLayout)/database/datasets/connect/page.tsx

@@ -0,0 +1,8 @@
+import React from 'react'
+import ExternalKnowledgeBaseConnector from '@/app/components/datasets/external-knowledge-base/connector'
+
+const ExternalKnowledgeBaseCreation = () => {
+  return <ExternalKnowledgeBaseConnector />
+}
+
+export default ExternalKnowledgeBaseCreation

+ 12 - 0
app/(commonLayout)/database/datasets/create/page.tsx

@@ -0,0 +1,12 @@
+import React from 'react'
+import DatasetUpdateForm from '@/app/components/datasets/create'
+
+type Props = {}
+
+const DatasetCreation = async (props: Props) => {
+  return (
+    <DatasetUpdateForm />
+  )
+}
+
+export default DatasetCreation

+ 14 - 0
app/(commonLayout)/database/datasets/layout.tsx

@@ -0,0 +1,14 @@
+'use client'
+
+import { ExternalApiPanelProvider } from '@/context/external-api-panel-context'
+import { ExternalKnowledgeApiProvider } from '@/context/external-knowledge-api-context'
+
+export default function DatasetsLayout({ children }: { children: React.ReactNode }) {
+  return (
+    <ExternalKnowledgeApiProvider>
+      <ExternalApiPanelProvider>
+        {children}
+      </ExternalApiPanelProvider>
+    </ExternalKnowledgeApiProvider>
+  )
+}

+ 11 - 0
app/(commonLayout)/database/datasets/page.tsx

@@ -0,0 +1,11 @@
+import Container from './Container'
+
+const AppList = async () => {
+  return <Container />
+}
+
+export const metadata = {
+  title: 'Datasets - Dify',
+}
+
+export default AppList

+ 11 - 0
app/(commonLayout)/database/datasets/store.ts

@@ -0,0 +1,11 @@
+import { create } from 'zustand'
+
+type DatasetStore = {
+  showExternalApiPanel: boolean
+  setShowExternalApiPanel: (show: boolean) => void
+}
+
+export const useDatasetStore = create<DatasetStore>(set => ({
+  showExternalApiPanel: false,
+  setShowExternalApiPanel: show => set({ showExternalApiPanel: show }),
+}))

+ 1319 - 0
app/(commonLayout)/database/datasets/template/template.en.mdx

@@ -0,0 +1,1319 @@
+import { CodeGroup } from '@/app/components/develop/code.tsx'
+import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '@/app/components/develop/md.tsx'
+
+# Knowledge API
+
+<div>
+  ### Authentication
+
+  Service API of Dify authenticates using an `API-Key`.
+
+  It is suggested that developers store the `API-Key` in the backend instead of sharing or storing it in the client side to avoid the leakage of the `API-Key`, which may lead to property loss.
+
+  All API requests should include your `API-Key` in the **`Authorization`** HTTP Header, as shown below:
+
+  <CodeGroup title="Code">
+    ```javascript
+      Authorization: Bearer {API_KEY}
+
+    ```
+  </CodeGroup>
+</div>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-text'
+  method='POST'
+  title='Create a Document from Text'
+  name='#create-by-text'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and creates a new document through text based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name
+      </Property>
+      <Property name='text' type='string' key='text'>
+        Document content
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        Index mode
+          - <code>high_quality</code> High quality: embedding using embedding model, built as vector database index
+          - <code>economy</code> Economy: Build using inverted index of keyword table index
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "text","text": "text","indexing_technique": "high_quality","process_rule": {"mode": "automatic"}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "text",
+        "text": "text",
+        "indexing_technique": "high_quality",
+        "process_rule": {
+            "mode": "automatic"
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+            "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "text.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695690280,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-file'
+  method='POST'
+  title='Create a Document from a File'
+  name='#create-by-file'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and creates a new document through a file based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='data' type='multipart/form-data json string' key='data'>
+        - <code>original_document_id</code> Source document ID (optional)
+          - Used to re-upload the document or modify the document cleaning and segmentation configuration. The missing information is copied from the source document
+          - The source document cannot be an archived document
+          - When original_document_id is passed in, the update operation is performed on behalf of the document. process_rule is a fillable item. If not filled in, the segmentation method of the source document will be used by default
+          - When original_document_id is not passed in, the new operation is performed on behalf of the document, and process_rule is required
+
+        - <code>indexing_technique</code> Index mode
+          - <code>high_quality</code> High quality: embedding using embedding model, built as vector database index
+          - <code>economy</code> Economy: Build using inverted index of keyword table index
+
+        - <code>process_rule</code> Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        Files that need to be uploaded.
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='POST'
+  title='Create an Empty Knowledge Base'
+  name='#create_empty_dataset'
+/>
+<Row>
+  <Col>
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Knowledge name
+      </Property>
+      <Property name='description' type='string' key='description'>
+        Knowledge description (optional)
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        Index technique (optional)
+          - <code>high_quality</code> High quality
+          - <code>economy</code> Economy
+      </Property>
+      <Property name='permission' type='string' key='permission'>
+        Permission
+          - <code>only_me</code> Only me
+          - <code>all_team_members</code> All team members
+          - <code>partial_members</code> Partial members
+      </Property>
+      <Property name='provider' type='string' key='provider'>
+        Provider (optional, default: vendor)
+          - <code>vendor</code> Vendor
+          - <code>external</code> External knowledge
+      </Property>
+      <Property name='external_knowledge_api_id' type='str' key='external_knowledge_api_id'>
+        External knowledge API ID (optional)
+      </Property>
+      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
+        External knowledge ID (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name", "permission": "only_me"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${apiBaseUrl}/v1/datasets' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "name": "name",
+      "permission": "only_me"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "id": "",
+      "name": "name",
+      "description": null,
+      "provider": "vendor",
+      "permission": "only_me",
+      "data_source_type": null,
+      "indexing_technique": null,
+      "app_count": 0,
+      "document_count": 0,
+      "word_count": 0,
+      "created_by": "",
+      "created_at": 1695636173,
+      "updated_by": "",
+      "updated_at": 1695636173,
+      "embedding_model": null,
+      "embedding_model_provider": null,
+      "embedding_available": null
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='GET'
+  title='Get Knowledge Base List'
+  name='#dataset_list'
+/>
+<Row>
+  <Col>
+    ### Query
+    <Properties>
+      <Property name='page' type='string' key='page'>
+        Page number
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        Number of items returned, default 20, range 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "name": "name",
+          "description": "desc",
+          "permission": "only_me",
+          "data_source_type": "upload_file",
+          "indexing_technique": "",
+          "app_count": 2,
+          "document_count": 10,
+          "word_count": 1200,
+          "created_by": "",
+          "created_at": "",
+          "updated_by": "",
+          "updated_at": ""
+        },
+        ...
+      ],
+      "has_more": true,
+      "limit": 20,
+      "total": 50,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}'
+  method='DELETE'
+  title='Delete a Knowledge Base'
+  name='#delete_dataset'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```text {{ title: 'Response' }}
+    204 No Content
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-text'
+  method='POST'
+  title='Update a Document with Text'
+  name='#update-by-text'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and updates the document through text based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name (optional)
+      </Property>
+      <Property name='text' type='string' key='text'>
+        Document content (optional)
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name","text": "text"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "name",
+        "text": "text"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "name.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-file'
+  method='POST'
+  title='Update a Document with a File'
+  name='#update-by-file'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge, and updates documents through files based on this knowledge
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name (optional)
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        Files to be uploaded
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"name":"Dify","indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": "20230921150427533684"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{batch}/indexing-status'
+  method='GET'
+  title='Get Document Embedding Status (Progress)'
+  name='#indexing_status'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='batch' type='string' key='batch'>
+        Batch number of uploaded documents
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{batch}/indexing-status"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data":[{
+        "id": "",
+        "indexing_status": "indexing",
+        "processing_started_at": 1681623462.0,
+        "parsing_completed_at": 1681623462.0,
+        "cleaning_completed_at": 1681623462.0,
+        "splitting_completed_at": 1681623462.0,
+        "completed_at": null,
+        "paused_at": null,
+        "error": null,
+        "stopped_at": null,
+        "completed_segments": 24,
+        "total_segments": 100
+      }]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}'
+  method='DELETE'
+  title='Delete a Document'
+  name='#delete_document'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents'
+  method='GET'
+  title='Get the Document List of a Knowledge Base'
+  name='#dataset_document_list'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        Search keywords, currently only search document names (optional)
+      </Property>
+      <Property name='page' type='string' key='page'>
+        Page number (optional)
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        Number of items returned, default 20, range 1-100 (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "position": 1,
+          "data_source_type": "file_upload",
+          "data_source_info": null,
+          "dataset_process_rule_id": null,
+          "name": "dify",
+          "created_from": "",
+          "created_by": "",
+          "created_at": 1681623639,
+          "tokens": 0,
+          "indexing_status": "waiting",
+          "error": null,
+          "enabled": true,
+          "disabled_at": null,
+          "disabled_by": null,
+          "archived": false
+        },
+      ],
+      "has_more": false,
+      "limit": 20,
+      "total": 9,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='POST'
+  title='Add Chunks to a Document'
+  name='#create_new_segment'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segments' type='object list' key='segments'>
+        - <code>content</code> (text) Text content / question content, required
+        - <code>answer</code> (text) Answer content, if the mode of the knowledge is Q&A mode, pass the value (optional)
+        - <code>keywords</code> (list) Keywords (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"segments": [{"content": "1","answer": "1","keywords": ["a"]}]}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segments": [
+        {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"]
+        }
+      ]
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+          "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='GET'
+  title='Get Chunks from a Document'
+  name='#get_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+     ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        Keyword (optional)
+      </Property>
+      <Property name='status' type='string' key='status'>
+        Search status, completed
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='DELETE'
+  title='Delete a Chunk in a Document'
+  name='#delete_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        Document Segment ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='POST'
+  title='Update a Chunk in a Document '
+  name='#update_segment'
+/>
+<Row>
+  <Col>
+    ### POST
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        Document Segment ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segment' type='object' key='segment'>
+        - <code>content</code> (text) Text content / question content, required
+        - <code>answer</code> (text) Answer content, passed if the knowledge is in Q&A mode (optional)
+        - <code>keywords</code> (list) Keyword (optional)
+        - <code>enabled</code> (bool) False / true (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{\"segment\": {\"content\": \"1\",\"answer\": \"1\", \"keywords\": [\"a\"], \"enabled\": false}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segment": {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"],
+          "enabled": false
+      }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/retrieve'
+  method='POST'
+  title='Retrieve Chunks from a Knowledge Base'
+  name='#dataset_retrieval'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='query' type='string' key='query'>
+        Query keyword
+      </Property>
+      <Property name='retrieval_model' type='object' key='retrieval_model'>
+        Retrieval model (optional, if not filled, it will be recalled according to the default method)
+        - <code>search_method</code> (text) Search method: One of the following four keywords is required
+          - <code>keyword_search</code> Keyword search
+          - <code>semantic_search</code> Semantic search
+          - <code>full_text_search</code> Full-text search
+          - <code>hybrid_search</code> Hybrid search
+        - <code>reranking_enable</code> (bool) Whether to enable reranking, required if the search mode is semantic_search or hybrid_search (optional)
+        - <code>reranking_mode</code> (object) Rerank model configuration, required if reranking is enabled
+            - <code>reranking_provider_name</code> (string) Rerank model provider
+            - <code>reranking_model_name</code> (string) Rerank model name
+        - <code>weights</code> (double) Semantic search weight setting in hybrid search mode
+        - <code>top_k</code> (integer) Number of results to return (optional)
+        - <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
+        - <code>score_threshold</code> (double) Score threshold
+      </Property>
+      <Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
+          Unused field
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/retrieve"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \\\n--header 'Authorization: Bearer {api_key}'\\\n--header 'Content-Type: application/json'\\\n--data-raw '{
+    "query": "test",
+    "retrieval_model": {
+        "search_method": "keyword_search",
+        "reranking_enable": false,
+        "reranking_mode": null,
+        "reranking_model": {
+            "reranking_provider_name": "",
+            "reranking_model_name": ""
+        },
+        "weights": null,
+        "top_k": 1,
+        "score_threshold_enabled": false,
+        "score_threshold": null
+    }
+}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "query": "test",
+        "retrieval_model": {
+            "search_method": "keyword_search",
+            "reranking_enable": false,
+            "reranking_mode": null,
+            "reranking_model": {
+                "reranking_provider_name": "",
+                "reranking_model_name": ""
+            },
+            "weights": null,
+            "top_k": 2,
+            "score_threshold_enabled": false,
+            "score_threshold": null
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "query": {
+        "content": "test"
+      },
+      "records": [
+        {
+          "segment": {
+            "id": "7fa6f24f-8679-48b3-bc9d-bdf28d73f218",
+            "position": 1,
+            "document_id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+            "content": "Operation guide",
+            "answer": null,
+            "word_count": 847,
+            "tokens": 280,
+            "keywords": [
+              "install",
+              "java",
+              "base",
+              "scripts",
+              "jdk",
+              "manual",
+              "internal",
+              "opens",
+              "add",
+              "vmoptions"
+            ],
+            "index_node_id": "39dd8443-d960-45a8-bb46-7275ad7fbc8e",
+            "index_node_hash": "0189157697b3c6a418ccf8264a09699f25858975578f3467c76d6bfc94df1d73",
+            "hit_count": 0,
+            "enabled": true,
+            "disabled_at": null,
+            "disabled_by": null,
+            "status": "completed",
+            "created_by": "dbcb1ab5-90c8-41a7-8b78-73b235eb6f6f",
+            "created_at": 1728734540,
+            "indexing_at": 1728734552,
+            "completed_at": 1728734584,
+            "error": null,
+            "stopped_at": null,
+            "document": {
+              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+              "data_source_type": "upload_file",
+              "name": "readme.txt",
+              "doc_type": null
+            }
+          },
+          "score": 3.730463140527718e-05,
+          "tsne_position": null
+        }
+      ]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Row>
+  <Col>
+    ### Error message
+    <Properties>
+      <Property name='code' type='string' key='code'>
+        Error code
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='status' type='number' key='status'>
+        Error status
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='message' type='string' key='message'>
+        Error message
+      </Property>
+    </Properties>
+  </Col>
+  <Col>
+    <CodeGroup title="Example">
+    ```json {{ title: 'Response' }}
+      {
+        "code": "no_file_uploaded",
+        "message": "Please upload your file.",
+        "status": 400
+      }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+<table className="max-w-auto border-collapse border border-slate-400" style={{ maxWidth: 'none', width: 'auto' }}>
+  <thead style={{ background: '#f9fafc' }}>
+    <tr>
+      <th className="p-2 border border-slate-300">code</th>
+      <th className="p-2 border border-slate-300">status</th>
+      <th className="p-2 border border-slate-300">message</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td className="p-2 border border-slate-300">no_file_uploaded</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Please upload your file.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">too_many_files</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Only one file is allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">file_too_large</td>
+      <td className="p-2 border border-slate-300">413</td>
+      <td className="p-2 border border-slate-300">File size exceeded.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">unsupported_file_type</td>
+      <td className="p-2 border border-slate-300">415</td>
+      <td className="p-2 border border-slate-300">File type not allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">high_quality_dataset_only</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Current operation only supports 'high-quality' datasets.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_not_initialized</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The dataset is still being initialized or indexing. Please wait a moment.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">archived_document_immutable</td>
+      <td className="p-2 border border-slate-300">403</td>
+      <td className="p-2 border border-slate-300">The archived document is not editable.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_name_duplicate</td>
+      <td className="p-2 border border-slate-300">409</td>
+      <td className="p-2 border border-slate-300">The dataset name already exists. Please modify your dataset name.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_action</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Invalid action.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_already_finished</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document has been processed. Please refresh the page or go to the document details.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_indexing</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document is being processed and cannot be edited.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_metadata</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The metadata content is incorrect. Please check and verify.</td>
+    </tr>
+  </tbody>
+</table>
+<div className="pb-4" />

+ 1321 - 0
app/(commonLayout)/database/datasets/template/template.zh.mdx

@@ -0,0 +1,1321 @@
+import { CodeGroup } from '@/app/components/develop/code.tsx'
+import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '@/app/components/develop/md.tsx'
+
+# 知识库 API
+
+<div>
+  ### 鉴权
+
+  Dify Service API 使用 `API-Key` 进行鉴权。
+
+  建议开发者把 `API-Key` 放在后端存储,而非分享或者放在客户端存储,以免 `API-Key` 泄露,导致财产损失。
+
+  所有 API 请求都应在 **`Authorization`** HTTP Header 中包含您的 `API-Key`,如下所示:
+
+  <CodeGroup title="Code">
+    ```javascript
+      Authorization: Bearer {API_KEY}
+
+    ```
+  </CodeGroup>
+</div>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-text'
+  method='POST'
+  title='通过文本创建文档'
+  name='#create-by-text'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文本创建新的文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称
+      </Property>
+      <Property name='text' type='string' key='text'>
+        文档内容
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        索引方式
+          - <code>high_quality</code> 高质量:使用  embedding 模型进行嵌入,构建为向量数据库索引
+          - <code>economy</code> 经济:使用 keyword table index 的倒排索引进行构建
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "text","text": "text","indexing_technique": "high_quality","process_rule": {"mode": "automatic"}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "text",
+        "text": "text",
+        "indexing_technique": "high_quality",
+        "process_rule": {
+            "mode": "automatic"
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+            "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "text.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695690280,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-file'
+  method='POST'
+  title='通过文件创建文档 '
+  name='#create-by-file'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文件创建新的文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='data' type='multipart/form-data json string' key='data'>
+        - <code>original_document_id</code> 源文档 ID(选填)
+          - 用于重新上传文档或修改文档清洗、分段配置,缺失的信息从源文档复制
+          - 源文档不可为归档的文档
+          - 当传入 <code>original_document_id</code> 时,代表文档进行更新操作,<code>process_rule</code> 为可填项目,不填默认使用源文档的分段方式
+          - 未传入 <code>original_document_id</code> 时,代表文档进行新增操作,<code>process_rule</code> 为必填
+
+        - <code>indexing_technique</code> 索引方式
+          - <code>high_quality</code> 高质量:使用  embedding 模型进行嵌入,构建为向量数据库索引
+          - <code>economy</code> 经济:使用 keyword table index 的倒排索引进行构建
+
+        - <code>process_rule</code> 处理规则
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        需要上传的文件。
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='POST'
+  title='创建空知识库'
+  name='#create_empty_dataset'
+/>
+<Row>
+  <Col>
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        知识库名称(必填)
+      </Property>
+      <Property name='description' type='string' key='description'>
+        知识库描述(选填)
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        索引模式(选填,建议填写)
+          - <code>high_quality</code> 高质量
+          - <code>economy</code> 经济
+      </Property>
+      <Property name='permission' type='string' key='permission'>
+        权限(选填,默认 only_me)
+          - <code>only_me</code> 仅自己
+          - <code>all_team_members</code> 所有团队成员
+          - <code>partial_members</code> 部分团队成员
+      </Property>
+      <Property name='provider' type='string' key='provider'>
+        Provider(选填,默认 vendor)
+          - <code>vendor</code> 上传文件
+          - <code>external</code> 外部知识库
+      </Property>
+      <Property name='external_knowledge_api_id' type='str' key='external_knowledge_api_id'>
+        外部知识库 API_ID(选填)
+      </Property>
+      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
+        外部知识库 ID(选填)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name", "permission": "only_me"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "name": "name",
+      "permission": "only_me"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "id": "",
+      "name": "name",
+      "description": null,
+      "provider": "vendor",
+      "permission": "only_me",
+      "data_source_type": null,
+      "indexing_technique": null,
+      "app_count": 0,
+      "document_count": 0,
+      "word_count": 0,
+      "created_by": "",
+      "created_at": 1695636173,
+      "updated_by": "",
+      "updated_at": 1695636173,
+      "embedding_model": null,
+      "embedding_model_provider": null,
+      "embedding_available": null
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='GET'
+  title='知识库列表'
+  name='#dataset_list'
+/>
+<Row>
+  <Col>
+    ### Query
+    <Properties>
+      <Property name='page' type='string' key='page'>
+        页码
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        返回条数,默认 20,范围 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "name": "知识库名称",
+          "description": "描述信息",
+          "permission": "only_me",
+          "data_source_type": "upload_file",
+          "indexing_technique": "",
+          "app_count": 2,
+          "document_count": 10,
+          "word_count": 1200,
+          "created_by": "",
+          "created_at": "",
+          "updated_by": "",
+          "updated_at": ""
+        },
+        ...
+      ],
+      "has_more": true,
+      "limit": 20,
+      "total": 50,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}'
+  method='DELETE'
+  title='删除知识库'
+  name='#delete_dataset'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```text {{ title: 'Response' }}
+    204 No Content
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-text'
+  method='POST'
+  title='通过文本更新文档 '
+  name='#update-by-text'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文本更新文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称(选填)
+      </Property>
+      <Property name='text' type='string' key='text'>
+        文档内容(选填)
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则(选填)
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name","text": "text"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "name",
+        "text": "text"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "name.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-file'
+  method='POST'
+  title='通过文件更新文档  '
+  name='#update-by-file'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文件更新文档的操作。
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称(选填)
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        需要上传的文件
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则(选填)
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"name":"Dify","indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": "20230921150427533684"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{batch}/indexing-status'
+  method='GET'
+  title='获取文档嵌入状态(进度)'
+  name='#indexing_status'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='batch' type='string' key='batch'>
+        上传文档的批次号
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{batch}/indexing-status"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data":[{
+        "id": "",
+        "indexing_status": "indexing",
+        "processing_started_at": 1681623462.0,
+        "parsing_completed_at": 1681623462.0,
+        "cleaning_completed_at": 1681623462.0,
+        "splitting_completed_at": 1681623462.0,
+        "completed_at": null,
+        "paused_at": null,
+        "error": null,
+        "stopped_at": null,
+        "completed_segments": 24,
+        "total_segments": 100
+      }]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}'
+  method='DELETE'
+  title='删除文档'
+  name='#delete_document'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents'
+  method='GET'
+  title='知识库文档列表'
+  name='#dataset_document_list'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        搜索关键词,可选,目前仅搜索文档名称
+      </Property>
+      <Property name='page' type='string' key='page'>
+        页码,可选
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        返回条数,可选,默认 20,范围 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "position": 1,
+          "data_source_type": "file_upload",
+          "data_source_info": null,
+          "dataset_process_rule_id": null,
+          "name": "dify",
+          "created_from": "",
+          "created_by": "",
+          "created_at": 1681623639,
+          "tokens": 0,
+          "indexing_status": "waiting",
+          "error": null,
+          "enabled": true,
+          "disabled_at": null,
+          "disabled_by": null,
+          "archived": false
+        },
+      ],
+      "has_more": false,
+      "limit": 20,
+      "total": 9,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='POST'
+  title='新增分段'
+  name='#create_new_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segments' type='object list' key='segments'>
+        - <code>content</code> (text) 文本内容/问题内容,必填
+        - <code>answer</code> (text) 答案内容,非必填,如果知识库的模式为 Q&A 模式则传值
+        - <code>keywords</code> (list) 关键字,非必填
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"segments": [{"content": "1","answer": "1","keywords": ["a"]}]}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segments": [
+        {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"]
+        }
+      ]
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='GET'
+  title='查询文档分段'
+  name='#get_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+     ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        搜索关键词,可选
+      </Property>
+      <Property name='status' type='string' key='status'>
+        搜索状态,completed
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='DELETE'
+  title='删除文档分段'
+  name='#delete_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        文档分段ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='POST'
+  title='更新文档分段'
+  name='#update_segment'
+/>
+<Row>
+  <Col>
+    ### POST
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        文档分段ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segment' type='object' key='segment'>
+        - <code>content</code> (text) 文本内容/问题内容,必填
+        - <code>answer</code> (text) 答案内容,非必填,如果知识库的模式为 Q&A 模式则传值
+        - <code>keywords</code> (list) 关键字,非必填
+        - <code>enabled</code> (bool) false/true,非必填
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{\"segment\": {\"content\": \"1\",\"answer\": \"1\", \"keywords\": [\"a\"], \"enabled\": false}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segment": {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"],
+          "enabled": false
+      }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/retrieve'
+  method='POST'
+  title='检索知识库'
+  name='#dataset_retrieval'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='query' type='string' key='query'>
+        检索关键词
+      </Property>
+      <Property name='retrieval_model' type='object' key='retrieval_model'>
+        检索参数(选填,如不填,按照默认方式召回)
+        - <code>search_method</code> (text) 检索方法:以下三个关键字之一,必填
+          - <code>keyword_search</code> 关键字检索
+          - <code>semantic_search</code> 语义检索
+          - <code>full_text_search</code> 全文检索
+          - <code>hybrid_search</code> 混合检索
+        - <code>reranking_enable</code> (bool) 是否启用 Reranking,非必填,如果检索模式为 semantic_search 模式或者 hybrid_search 则传值
+        - <code>reranking_mode</code> (object) Rerank模型配置,非必填,如果启用了 reranking 则传值
+            - <code>reranking_provider_name</code> (string) Rerank 模型提供商
+            - <code>reranking_model_name</code> (string) Rerank 模型名称
+        - <code>weights</code> (double) 混合检索模式下语意检索的权重设置
+        - <code>top_k</code> (integer) 返回结果数量,非必填
+        - <code>score_threshold_enabled</code> (bool) 是否开启 score 阈值
+        - <code>score_threshold</code> (double) Score 阈值
+      </Property>
+      <Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
+          未启用字段
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/retrieve"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \\\n--header 'Authorization: Bearer {api_key}'\\\n--header 'Content-Type: application/json'\\\n--data-raw '{
+    "query": "test",
+    "retrieval_model": {
+        "search_method": "keyword_search",
+        "reranking_enable": false,
+        "reranking_mode": null,
+        "reranking_model": {
+            "reranking_provider_name": "",
+            "reranking_model_name": ""
+        },
+        "weights": null,
+        "top_k": 1,
+        "score_threshold_enabled": false,
+        "score_threshold": null
+    }
+}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "query": "test",
+        "retrieval_model": {
+            "search_method": "keyword_search",
+            "reranking_enable": false,
+            "reranking_mode": null,
+            "reranking_model": {
+                "reranking_provider_name": "",
+                "reranking_model_name": ""
+            },
+            "weights": null,
+            "top_k": 2,
+            "score_threshold_enabled": false,
+            "score_threshold": null
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "query": {
+        "content": "test"
+      },
+      "records": [
+        {
+          "segment": {
+            "id": "7fa6f24f-8679-48b3-bc9d-bdf28d73f218",
+            "position": 1,
+            "document_id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+            "content": "Operation guide",
+            "answer": null,
+            "word_count": 847,
+            "tokens": 280,
+            "keywords": [
+              "install",
+              "java",
+              "base",
+              "scripts",
+              "jdk",
+              "manual",
+              "internal",
+              "opens",
+              "add",
+              "vmoptions"
+            ],
+            "index_node_id": "39dd8443-d960-45a8-bb46-7275ad7fbc8e",
+            "index_node_hash": "0189157697b3c6a418ccf8264a09699f25858975578f3467c76d6bfc94df1d73",
+            "hit_count": 0,
+            "enabled": true,
+            "disabled_at": null,
+            "disabled_by": null,
+            "status": "completed",
+            "created_by": "dbcb1ab5-90c8-41a7-8b78-73b235eb6f6f",
+            "created_at": 1728734540,
+            "indexing_at": 1728734552,
+            "completed_at": 1728734584,
+            "error": null,
+            "stopped_at": null,
+            "document": {
+              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+              "data_source_type": "upload_file",
+              "name": "readme.txt",
+              "doc_type": null
+            }
+          },
+          "score": 3.730463140527718e-05,
+          "tsne_position": null
+        }
+      ]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+
+<hr className='ml-0 mr-0' />
+
+<Row>
+  <Col>
+    ### 错误信息
+    <Properties>
+      <Property name='code' type='string' key='code'>
+        返回的错误代码
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='status' type='number' key='status'>
+        返回的错误状态
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='message' type='string' key='message'>
+        返回的错误信息
+      </Property>
+    </Properties>
+  </Col>
+  <Col>
+    <CodeGroup title="Example">
+    ```json {{ title: 'Response' }}
+      {
+        "code": "no_file_uploaded",
+        "message": "Please upload your file.",
+        "status": 400
+      }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+<table className="max-w-auto border-collapse border border-slate-400" style={{ maxWidth: 'none', width: 'auto' }}>
+  <thead style={{ background: '#f9fafc' }}>
+    <tr>
+      <th className="p-2 border border-slate-300">code</th>
+      <th className="p-2 border border-slate-300">status</th>
+      <th className="p-2 border border-slate-300">message</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td className="p-2 border border-slate-300">no_file_uploaded</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Please upload your file.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">too_many_files</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Only one file is allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">file_too_large</td>
+      <td className="p-2 border border-slate-300">413</td>
+      <td className="p-2 border border-slate-300">File size exceeded.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">unsupported_file_type</td>
+      <td className="p-2 border border-slate-300">415</td>
+      <td className="p-2 border border-slate-300">File type not allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">high_quality_dataset_only</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Current operation only supports 'high-quality' datasets.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_not_initialized</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The dataset is still being initialized or indexing. Please wait a moment.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">archived_document_immutable</td>
+      <td className="p-2 border border-slate-300">403</td>
+      <td className="p-2 border border-slate-300">The archived document is not editable.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_name_duplicate</td>
+      <td className="p-2 border border-slate-300">409</td>
+      <td className="p-2 border border-slate-300">The dataset name already exists. Please modify your dataset name.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_action</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Invalid action.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_already_finished</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document has been processed. Please refresh the page or go to the document details.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_indexing</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document is being processed and cannot be edited.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_metadata</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The metadata content is incorrect. Please check and verify.</td>
+    </tr>
+  </tbody>
+</table>
+<div className="pb-4" />

+ 14 - 0
app/(commonLayout)/database/layout.tsx

@@ -0,0 +1,14 @@
+'use client'
+
+import { ExternalApiPanelProvider } from '@/context/external-api-panel-context'
+import { ExternalKnowledgeApiProvider } from '@/context/external-knowledge-api-context'
+
+export default function DatasetsLayout({ children }: { children: React.ReactNode }) {
+  return (
+    <ExternalKnowledgeApiProvider>
+      <ExternalApiPanelProvider>
+        {children}
+      </ExternalApiPanelProvider>
+    </ExternalKnowledgeApiProvider>
+  )
+}

+ 11 - 0
app/(commonLayout)/database/page.tsx

@@ -0,0 +1,11 @@
+import Container from './Container'
+
+const AppList = async () => {
+  return <Container />
+}
+
+export const metadata = {
+  title: 'Datasets - Dify',
+}
+
+export default AppList

+ 11 - 0
app/(commonLayout)/database/store.ts

@@ -0,0 +1,11 @@
+import { create } from 'zustand'
+
+type DatasetStore = {
+  showExternalApiPanel: boolean
+  setShowExternalApiPanel: (show: boolean) => void
+}
+
+export const useDatasetStore = create<DatasetStore>(set => ({
+  showExternalApiPanel: false,
+  setShowExternalApiPanel: show => set({ showExternalApiPanel: show }),
+}))

+ 1319 - 0
app/(commonLayout)/database/template/template.en.mdx

@@ -0,0 +1,1319 @@
+import { CodeGroup } from '@/app/components/develop/code.tsx'
+import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '@/app/components/develop/md.tsx'
+
+# Knowledge API
+
+<div>
+  ### Authentication
+
+  Service API of Dify authenticates using an `API-Key`.
+
+  It is suggested that developers store the `API-Key` in the backend instead of sharing or storing it in the client side to avoid the leakage of the `API-Key`, which may lead to property loss.
+
+  All API requests should include your `API-Key` in the **`Authorization`** HTTP Header, as shown below:
+
+  <CodeGroup title="Code">
+    ```javascript
+      Authorization: Bearer {API_KEY}
+
+    ```
+  </CodeGroup>
+</div>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-text'
+  method='POST'
+  title='Create a Document from Text'
+  name='#create-by-text'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and creates a new document through text based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name
+      </Property>
+      <Property name='text' type='string' key='text'>
+        Document content
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        Index mode
+          - <code>high_quality</code> High quality: embedding using embedding model, built as vector database index
+          - <code>economy</code> Economy: Build using inverted index of keyword table index
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "text","text": "text","indexing_technique": "high_quality","process_rule": {"mode": "automatic"}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "text",
+        "text": "text",
+        "indexing_technique": "high_quality",
+        "process_rule": {
+            "mode": "automatic"
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+            "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "text.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695690280,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-file'
+  method='POST'
+  title='Create a Document from a File'
+  name='#create-by-file'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and creates a new document through a file based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='data' type='multipart/form-data json string' key='data'>
+        - <code>original_document_id</code> Source document ID (optional)
+          - Used to re-upload the document or modify the document cleaning and segmentation configuration. The missing information is copied from the source document
+          - The source document cannot be an archived document
+          - When original_document_id is passed in, the update operation is performed on behalf of the document. process_rule is a fillable item. If not filled in, the segmentation method of the source document will be used by default
+          - When original_document_id is not passed in, the new operation is performed on behalf of the document, and process_rule is required
+
+        - <code>indexing_technique</code> Index mode
+          - <code>high_quality</code> High quality: embedding using embedding model, built as vector database index
+          - <code>economy</code> Economy: Build using inverted index of keyword table index
+
+        - <code>process_rule</code> Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        Files that need to be uploaded.
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='POST'
+  title='Create an Empty Knowledge Base'
+  name='#create_empty_dataset'
+/>
+<Row>
+  <Col>
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Knowledge name
+      </Property>
+      <Property name='description' type='string' key='description'>
+        Knowledge description (optional)
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        Index technique (optional)
+          - <code>high_quality</code> High quality
+          - <code>economy</code> Economy
+      </Property>
+      <Property name='permission' type='string' key='permission'>
+        Permission
+          - <code>only_me</code> Only me
+          - <code>all_team_members</code> All team members
+          - <code>partial_members</code> Partial members
+      </Property>
+      <Property name='provider' type='string' key='provider'>
+        Provider (optional, default: vendor)
+          - <code>vendor</code> Vendor
+          - <code>external</code> External knowledge
+      </Property>
+      <Property name='external_knowledge_api_id' type='str' key='external_knowledge_api_id'>
+        External knowledge API ID (optional)
+      </Property>
+      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
+        External knowledge ID (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name", "permission": "only_me"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${apiBaseUrl}/v1/datasets' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "name": "name",
+      "permission": "only_me"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "id": "",
+      "name": "name",
+      "description": null,
+      "provider": "vendor",
+      "permission": "only_me",
+      "data_source_type": null,
+      "indexing_technique": null,
+      "app_count": 0,
+      "document_count": 0,
+      "word_count": 0,
+      "created_by": "",
+      "created_at": 1695636173,
+      "updated_by": "",
+      "updated_at": 1695636173,
+      "embedding_model": null,
+      "embedding_model_provider": null,
+      "embedding_available": null
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='GET'
+  title='Get Knowledge Base List'
+  name='#dataset_list'
+/>
+<Row>
+  <Col>
+    ### Query
+    <Properties>
+      <Property name='page' type='string' key='page'>
+        Page number
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        Number of items returned, default 20, range 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "name": "name",
+          "description": "desc",
+          "permission": "only_me",
+          "data_source_type": "upload_file",
+          "indexing_technique": "",
+          "app_count": 2,
+          "document_count": 10,
+          "word_count": 1200,
+          "created_by": "",
+          "created_at": "",
+          "updated_by": "",
+          "updated_at": ""
+        },
+        ...
+      ],
+      "has_more": true,
+      "limit": 20,
+      "total": 50,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}'
+  method='DELETE'
+  title='Delete a Knowledge Base'
+  name='#delete_dataset'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```text {{ title: 'Response' }}
+    204 No Content
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-text'
+  method='POST'
+  title='Update a Document with Text'
+  name='#update-by-text'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge and updates the document through text based on this knowledge.
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name (optional)
+      </Property>
+      <Property name='text' type='string' key='text'>
+        Document content (optional)
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name","text": "text"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "name",
+        "text": "text"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "name.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-file'
+  method='POST'
+  title='Update a Document with a File'
+  name='#update-by-file'
+/>
+<Row>
+  <Col>
+    This API is based on an existing knowledge, and updates documents through files based on this knowledge
+
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        Document name (optional)
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        Files to be uploaded
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        Processing rules
+          - <code>mode</code> (string) Cleaning, segmentation mode, automatic / custom
+          - <code>rules</code> (object) Custom rules (in automatic mode, this field is empty)
+            - <code>pre_processing_rules</code> (array[object]) Preprocessing rules
+              - <code>id</code> (string) Unique identifier for the preprocessing rule
+                - enumerate
+                  - <code>remove_extra_spaces</code> Replace consecutive spaces, newlines, tabs
+                  - <code>remove_urls_emails</code> Delete URL, email address
+              - <code>enabled</code> (bool) Whether to select this rule or not. If no document ID is passed in, it represents the default value.
+            - <code>segmentation</code> (object) Segmentation rules
+              - <code>separator</code> Custom segment identifier, currently only allows one delimiter to be set. Default is \n
+              - <code>max_tokens</code> Maximum length (token) defaults to 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"name":"Dify","indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": "20230921150427533684"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{batch}/indexing-status'
+  method='GET'
+  title='Get Document Embedding Status (Progress)'
+  name='#indexing_status'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='batch' type='string' key='batch'>
+        Batch number of uploaded documents
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{batch}/indexing-status"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data":[{
+        "id": "",
+        "indexing_status": "indexing",
+        "processing_started_at": 1681623462.0,
+        "parsing_completed_at": 1681623462.0,
+        "cleaning_completed_at": 1681623462.0,
+        "splitting_completed_at": 1681623462.0,
+        "completed_at": null,
+        "paused_at": null,
+        "error": null,
+        "stopped_at": null,
+        "completed_segments": 24,
+        "total_segments": 100
+      }]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}'
+  method='DELETE'
+  title='Delete a Document'
+  name='#delete_document'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents'
+  method='GET'
+  title='Get the Document List of a Knowledge Base'
+  name='#dataset_document_list'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        Search keywords, currently only search document names (optional)
+      </Property>
+      <Property name='page' type='string' key='page'>
+        Page number (optional)
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        Number of items returned, default 20, range 1-100 (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "position": 1,
+          "data_source_type": "file_upload",
+          "data_source_info": null,
+          "dataset_process_rule_id": null,
+          "name": "dify",
+          "created_from": "",
+          "created_by": "",
+          "created_at": 1681623639,
+          "tokens": 0,
+          "indexing_status": "waiting",
+          "error": null,
+          "enabled": true,
+          "disabled_at": null,
+          "disabled_by": null,
+          "archived": false
+        },
+      ],
+      "has_more": false,
+      "limit": 20,
+      "total": 9,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='POST'
+  title='Add Chunks to a Document'
+  name='#create_new_segment'
+/>
+<Row>
+  <Col>
+    ### Params
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segments' type='object list' key='segments'>
+        - <code>content</code> (text) Text content / question content, required
+        - <code>answer</code> (text) Answer content, if the mode of the knowledge is Q&A mode, pass the value (optional)
+        - <code>keywords</code> (list) Keywords (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"segments": [{"content": "1","answer": "1","keywords": ["a"]}]}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segments": [
+        {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"]
+        }
+      ]
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+          "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='GET'
+  title='Get Chunks from a Document'
+  name='#get_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+    </Properties>
+
+     ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        Keyword (optional)
+      </Property>
+      <Property name='status' type='string' key='status'>
+        Search status, completed
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='DELETE'
+  title='Delete a Chunk in a Document'
+  name='#delete_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        Document Segment ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='POST'
+  title='Update a Chunk in a Document '
+  name='#update_segment'
+/>
+<Row>
+  <Col>
+    ### POST
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        Document ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        Document Segment ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segment' type='object' key='segment'>
+        - <code>content</code> (text) Text content / question content, required
+        - <code>answer</code> (text) Answer content, passed if the knowledge is in Q&A mode (optional)
+        - <code>keywords</code> (list) Keyword (optional)
+        - <code>enabled</code> (bool) False / true (optional)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{\"segment\": {\"content\": \"1\",\"answer\": \"1\", \"keywords\": [\"a\"], \"enabled\": false}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segment": {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"],
+          "enabled": false
+      }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/retrieve'
+  method='POST'
+  title='Retrieve Chunks from a Knowledge Base'
+  name='#dataset_retrieval'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        Knowledge ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='query' type='string' key='query'>
+        Query keyword
+      </Property>
+      <Property name='retrieval_model' type='object' key='retrieval_model'>
+        Retrieval model (optional, if not filled, it will be recalled according to the default method)
+        - <code>search_method</code> (text) Search method: One of the following four keywords is required
+          - <code>keyword_search</code> Keyword search
+          - <code>semantic_search</code> Semantic search
+          - <code>full_text_search</code> Full-text search
+          - <code>hybrid_search</code> Hybrid search
+        - <code>reranking_enable</code> (bool) Whether to enable reranking, required if the search mode is semantic_search or hybrid_search (optional)
+        - <code>reranking_mode</code> (object) Rerank model configuration, required if reranking is enabled
+            - <code>reranking_provider_name</code> (string) Rerank model provider
+            - <code>reranking_model_name</code> (string) Rerank model name
+        - <code>weights</code> (double) Semantic search weight setting in hybrid search mode
+        - <code>top_k</code> (integer) Number of results to return (optional)
+        - <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
+        - <code>score_threshold</code> (double) Score threshold
+      </Property>
+      <Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
+          Unused field
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/retrieve"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \\\n--header 'Authorization: Bearer {api_key}'\\\n--header 'Content-Type: application/json'\\\n--data-raw '{
+    "query": "test",
+    "retrieval_model": {
+        "search_method": "keyword_search",
+        "reranking_enable": false,
+        "reranking_mode": null,
+        "reranking_model": {
+            "reranking_provider_name": "",
+            "reranking_model_name": ""
+        },
+        "weights": null,
+        "top_k": 1,
+        "score_threshold_enabled": false,
+        "score_threshold": null
+    }
+}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "query": "test",
+        "retrieval_model": {
+            "search_method": "keyword_search",
+            "reranking_enable": false,
+            "reranking_mode": null,
+            "reranking_model": {
+                "reranking_provider_name": "",
+                "reranking_model_name": ""
+            },
+            "weights": null,
+            "top_k": 2,
+            "score_threshold_enabled": false,
+            "score_threshold": null
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "query": {
+        "content": "test"
+      },
+      "records": [
+        {
+          "segment": {
+            "id": "7fa6f24f-8679-48b3-bc9d-bdf28d73f218",
+            "position": 1,
+            "document_id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+            "content": "Operation guide",
+            "answer": null,
+            "word_count": 847,
+            "tokens": 280,
+            "keywords": [
+              "install",
+              "java",
+              "base",
+              "scripts",
+              "jdk",
+              "manual",
+              "internal",
+              "opens",
+              "add",
+              "vmoptions"
+            ],
+            "index_node_id": "39dd8443-d960-45a8-bb46-7275ad7fbc8e",
+            "index_node_hash": "0189157697b3c6a418ccf8264a09699f25858975578f3467c76d6bfc94df1d73",
+            "hit_count": 0,
+            "enabled": true,
+            "disabled_at": null,
+            "disabled_by": null,
+            "status": "completed",
+            "created_by": "dbcb1ab5-90c8-41a7-8b78-73b235eb6f6f",
+            "created_at": 1728734540,
+            "indexing_at": 1728734552,
+            "completed_at": 1728734584,
+            "error": null,
+            "stopped_at": null,
+            "document": {
+              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+              "data_source_type": "upload_file",
+              "name": "readme.txt",
+              "doc_type": null
+            }
+          },
+          "score": 3.730463140527718e-05,
+          "tsne_position": null
+        }
+      ]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Row>
+  <Col>
+    ### Error message
+    <Properties>
+      <Property name='code' type='string' key='code'>
+        Error code
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='status' type='number' key='status'>
+        Error status
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='message' type='string' key='message'>
+        Error message
+      </Property>
+    </Properties>
+  </Col>
+  <Col>
+    <CodeGroup title="Example">
+    ```json {{ title: 'Response' }}
+      {
+        "code": "no_file_uploaded",
+        "message": "Please upload your file.",
+        "status": 400
+      }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+<table className="max-w-auto border-collapse border border-slate-400" style={{ maxWidth: 'none', width: 'auto' }}>
+  <thead style={{ background: '#f9fafc' }}>
+    <tr>
+      <th className="p-2 border border-slate-300">code</th>
+      <th className="p-2 border border-slate-300">status</th>
+      <th className="p-2 border border-slate-300">message</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td className="p-2 border border-slate-300">no_file_uploaded</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Please upload your file.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">too_many_files</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Only one file is allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">file_too_large</td>
+      <td className="p-2 border border-slate-300">413</td>
+      <td className="p-2 border border-slate-300">File size exceeded.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">unsupported_file_type</td>
+      <td className="p-2 border border-slate-300">415</td>
+      <td className="p-2 border border-slate-300">File type not allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">high_quality_dataset_only</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Current operation only supports 'high-quality' datasets.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_not_initialized</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The dataset is still being initialized or indexing. Please wait a moment.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">archived_document_immutable</td>
+      <td className="p-2 border border-slate-300">403</td>
+      <td className="p-2 border border-slate-300">The archived document is not editable.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_name_duplicate</td>
+      <td className="p-2 border border-slate-300">409</td>
+      <td className="p-2 border border-slate-300">The dataset name already exists. Please modify your dataset name.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_action</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Invalid action.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_already_finished</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document has been processed. Please refresh the page or go to the document details.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_indexing</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document is being processed and cannot be edited.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_metadata</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The metadata content is incorrect. Please check and verify.</td>
+    </tr>
+  </tbody>
+</table>
+<div className="pb-4" />

+ 1321 - 0
app/(commonLayout)/database/template/template.zh.mdx

@@ -0,0 +1,1321 @@
+import { CodeGroup } from '@/app/components/develop/code.tsx'
+import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '@/app/components/develop/md.tsx'
+
+# 知识库 API
+
+<div>
+  ### 鉴权
+
+  Dify Service API 使用 `API-Key` 进行鉴权。
+
+  建议开发者把 `API-Key` 放在后端存储,而非分享或者放在客户端存储,以免 `API-Key` 泄露,导致财产损失。
+
+  所有 API 请求都应在 **`Authorization`** HTTP Header 中包含您的 `API-Key`,如下所示:
+
+  <CodeGroup title="Code">
+    ```javascript
+      Authorization: Bearer {API_KEY}
+
+    ```
+  </CodeGroup>
+</div>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-text'
+  method='POST'
+  title='通过文本创建文档'
+  name='#create-by-text'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文本创建新的文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称
+      </Property>
+      <Property name='text' type='string' key='text'>
+        文档内容
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        索引方式
+          - <code>high_quality</code> 高质量:使用  embedding 模型进行嵌入,构建为向量数据库索引
+          - <code>economy</code> 经济:使用 keyword table index 的倒排索引进行构建
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "text","text": "text","indexing_technique": "high_quality","process_rule": {"mode": "automatic"}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "text",
+        "text": "text",
+        "indexing_technique": "high_quality",
+        "process_rule": {
+            "mode": "automatic"
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+            "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "text.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695690280,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/document/create-by-file'
+  method='POST'
+  title='通过文件创建文档 '
+  name='#create-by-file'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文件创建新的文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='data' type='multipart/form-data json string' key='data'>
+        - <code>original_document_id</code> 源文档 ID(选填)
+          - 用于重新上传文档或修改文档清洗、分段配置,缺失的信息从源文档复制
+          - 源文档不可为归档的文档
+          - 当传入 <code>original_document_id</code> 时,代表文档进行更新操作,<code>process_rule</code> 为可填项目,不填默认使用源文档的分段方式
+          - 未传入 <code>original_document_id</code> 时,代表文档进行新增操作,<code>process_rule</code> 为必填
+
+        - <code>indexing_technique</code> 索引方式
+          - <code>high_quality</code> 高质量:使用  embedding 模型进行嵌入,构建为向量数据库索引
+          - <code>economy</code> 经济:使用 keyword table index 的倒排索引进行构建
+
+        - <code>process_rule</code> 处理规则
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        需要上传的文件。
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/document/create-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/document/create-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='POST'
+  title='创建空知识库'
+  name='#create_empty_dataset'
+/>
+<Row>
+  <Col>
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        知识库名称(必填)
+      </Property>
+      <Property name='description' type='string' key='description'>
+        知识库描述(选填)
+      </Property>
+      <Property name='indexing_technique' type='string' key='indexing_technique'>
+        索引模式(选填,建议填写)
+          - <code>high_quality</code> 高质量
+          - <code>economy</code> 经济
+      </Property>
+      <Property name='permission' type='string' key='permission'>
+        权限(选填,默认 only_me)
+          - <code>only_me</code> 仅自己
+          - <code>all_team_members</code> 所有团队成员
+          - <code>partial_members</code> 部分团队成员
+      </Property>
+      <Property name='provider' type='string' key='provider'>
+        Provider(选填,默认 vendor)
+          - <code>vendor</code> 上传文件
+          - <code>external</code> 外部知识库
+      </Property>
+      <Property name='external_knowledge_api_id' type='str' key='external_knowledge_api_id'>
+        外部知识库 API_ID(选填)
+      </Property>
+      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
+        外部知识库 ID(选填)
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name", "permission": "only_me"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "name": "name",
+      "permission": "only_me"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "id": "",
+      "name": "name",
+      "description": null,
+      "provider": "vendor",
+      "permission": "only_me",
+      "data_source_type": null,
+      "indexing_technique": null,
+      "app_count": 0,
+      "document_count": 0,
+      "word_count": 0,
+      "created_by": "",
+      "created_at": 1695636173,
+      "updated_by": "",
+      "updated_at": 1695636173,
+      "embedding_model": null,
+      "embedding_model_provider": null,
+      "embedding_available": null
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets'
+  method='GET'
+  title='知识库列表'
+  name='#dataset_list'
+/>
+<Row>
+  <Col>
+    ### Query
+    <Properties>
+      <Property name='page' type='string' key='page'>
+        页码
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        返回条数,默认 20,范围 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets?page=1&limit=20' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "name": "知识库名称",
+          "description": "描述信息",
+          "permission": "only_me",
+          "data_source_type": "upload_file",
+          "indexing_technique": "",
+          "app_count": 2,
+          "document_count": 10,
+          "word_count": 1200,
+          "created_by": "",
+          "created_at": "",
+          "updated_by": "",
+          "updated_at": ""
+        },
+        ...
+      ],
+      "has_more": true,
+      "limit": 20,
+      "total": 50,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}'
+  method='DELETE'
+  title='删除知识库'
+  name='#delete_dataset'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}' \
+    --header 'Authorization: Bearer {api_key}'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```text {{ title: 'Response' }}
+    204 No Content
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-text'
+  method='POST'
+  title='通过文本更新文档 '
+  name='#update-by-text'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文本更新文档
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称(选填)
+      </Property>
+      <Property name='text' type='string' key='text'>
+        文档内容(选填)
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则(选填)
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-text"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"name": "name","text": "text"}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-text' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "name": "name",
+        "text": "text"
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "name.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": ""
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/update-by-file'
+  method='POST'
+  title='通过文件更新文档  '
+  name='#update-by-file'
+/>
+<Row>
+  <Col>
+    此接口基于已存在知识库,在此知识库的基础上通过文件更新文档的操作。
+
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='name' type='string' key='name'>
+        文档名称(选填)
+      </Property>
+      <Property name='file' type='multipart/form-data' key='file'>
+        需要上传的文件
+      </Property>
+      <Property name='process_rule' type='object' key='process_rule'>
+        处理规则(选填)
+          - <code>mode</code> (string) 清洗、分段模式 ,automatic 自动 / custom 自定义
+          - <code>rules</code> (object) 自定义规则(自动模式下,该字段为空)
+            - <code>pre_processing_rules</code> (array[object]) 预处理规则
+              - <code>id</code> (string) 预处理规则的唯一标识符
+                - 枚举:
+                  - <code>remove_extra_spaces</code> 替换连续空格、换行符、制表符
+                  - <code>remove_urls_emails</code> 删除 URL、电子邮件地址
+              - <code>enabled</code> (bool) 是否选中该规则,不传入文档 ID 时代表默认值
+            - <code>segmentation</code> (object) 分段规则
+              - <code>separator</code> 自定义分段标识符,目前仅允许设置一个分隔符。默认为 \n
+              - <code>max_tokens</code> 最大长度(token)默认为 1000
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/update-by-file"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'data="{"name":"Dify","indexing_technique":"high_quality","process_rule":{"rules":{"pre_processing_rules":[{"id":"remove_extra_spaces","enabled":true},{"id":"remove_urls_emails","enabled":true}],"segmentation":{"separator":"###","max_tokens":500}},"mode":"custom"}}";type=text/plain' \\\n--form 'file=@"/path/to/file"'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/update-by-file' \
+    --header 'Authorization: Bearer {api_key}' \
+    --form 'data="{\"name\":\"Dify\",\"indexing_technique\":\"high_quality\",\"process_rule\":{\"rules\":{\"pre_processing_rules\":[{\"id\":\"remove_extra_spaces\",\"enabled\":true},{\"id\":\"remove_urls_emails\",\"enabled\":true}],\"segmentation\":{\"separator\":\"###\",\"max_tokens\":500}},\"mode\":\"custom\"}}";type=text/plain' \
+    --form 'file=@"/path/to/file"'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "document": {
+        "id": "",
+        "position": 1,
+        "data_source_type": "upload_file",
+        "data_source_info": {
+          "upload_file_id": ""
+        },
+        "dataset_process_rule_id": "",
+        "name": "Dify.txt",
+        "created_from": "api",
+        "created_by": "",
+        "created_at": 1695308667,
+        "tokens": 0,
+        "indexing_status": "waiting",
+        "error": null,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "archived": false,
+        "display_status": "queuing",
+        "word_count": 0,
+        "hit_count": 0,
+        "doc_form": "text_model"
+      },
+      "batch": "20230921150427533684"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{batch}/indexing-status'
+  method='GET'
+  title='获取文档嵌入状态(进度)'
+  name='#indexing_status'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='batch' type='string' key='batch'>
+        上传文档的批次号
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{batch}/indexing-status"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{batch}/indexing-status' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data":[{
+        "id": "",
+        "indexing_status": "indexing",
+        "processing_started_at": 1681623462.0,
+        "parsing_completed_at": 1681623462.0,
+        "cleaning_completed_at": 1681623462.0,
+        "splitting_completed_at": 1681623462.0,
+        "completed_at": null,
+        "paused_at": null,
+        "error": null,
+        "stopped_at": null,
+        "completed_segments": 24,
+        "total_segments": 100
+      }]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}'
+  method='DELETE'
+  title='删除文档'
+  name='#delete_document'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents'
+  method='GET'
+  title='知识库文档列表'
+  name='#dataset_document_list'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        搜索关键词,可选,目前仅搜索文档名称
+      </Property>
+      <Property name='page' type='string' key='page'>
+        页码,可选
+      </Property>
+      <Property name='limit' type='string' key='limit'>
+        返回条数,可选,默认 20,范围 1-100
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \\\n--header 'Authorization: Bearer {api_key}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents' \
+    --header 'Authorization: Bearer {api_key}' \
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [
+        {
+          "id": "",
+          "position": 1,
+          "data_source_type": "file_upload",
+          "data_source_info": null,
+          "dataset_process_rule_id": null,
+          "name": "dify",
+          "created_from": "",
+          "created_by": "",
+          "created_at": 1681623639,
+          "tokens": 0,
+          "indexing_status": "waiting",
+          "error": null,
+          "enabled": true,
+          "disabled_at": null,
+          "disabled_by": null,
+          "archived": false
+        },
+      ],
+      "has_more": false,
+      "limit": 20,
+      "total": 9,
+      "page": 1
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='POST'
+  title='新增分段'
+  name='#create_new_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segments' type='object list' key='segments'>
+        - <code>content</code> (text) 文本内容/问题内容,必填
+        - <code>answer</code> (text) 答案内容,非必填,如果知识库的模式为 Q&A 模式则传值
+        - <code>keywords</code> (list) 关键字,非必填
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"segments": [{"content": "1","answer": "1","keywords": ["a"]}]}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segments": [
+        {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"]
+        }
+      ]
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments'
+  method='GET'
+  title='查询文档分段'
+  name='#get_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+    </Properties>
+
+     ### Query
+    <Properties>
+      <Property name='keyword' type='string' key='keyword'>
+        搜索关键词,可选
+      </Property>
+      <Property name='status' type='string' key='status'>
+        搜索状态,completed
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="GET"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments"
+      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='DELETE'
+  title='删除文档分段'
+  name='#delete_segment'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        文档分段ID
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="DELETE"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "result": "success"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}'
+  method='POST'
+  title='更新文档分段'
+  name='#update_segment'
+/>
+<Row>
+  <Col>
+    ### POST
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+      <Property name='document_id' type='string' key='document_id'>
+        文档 ID
+      </Property>
+      <Property name='segment_id' type='string' key='segment_id'>
+        文档分段ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='segment' type='object' key='segment'>
+        - <code>content</code> (text) 文本内容/问题内容,必填
+        - <code>answer</code> (text) 答案内容,非必填,如果知识库的模式为 Q&A 模式则传值
+        - <code>keywords</code> (list) 关键字,非必填
+        - <code>enabled</code> (bool) false/true,非必填
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{\"segment\": {\"content\": \"1\",\"answer\": \"1\", \"keywords\": [\"a\"], \"enabled\": false}}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+      "segment": {
+          "content": "1",
+          "answer": "1",
+          "keywords": ["a"],
+          "enabled": false
+      }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "data": [{
+        "id": "",
+        "position": 1,
+        "document_id": "",
+        "content": "1",
+        "answer": "1",
+        "word_count": 25,
+        "tokens": 0,
+        "keywords": [
+            "a"
+        ],
+        "index_node_id": "",
+        "index_node_hash": "",
+        "hit_count": 0,
+        "enabled": true,
+        "disabled_at": null,
+        "disabled_by": null,
+        "status": "completed",
+        "created_by": "",
+        "created_at": 1695312007,
+        "indexing_at": 1695312007,
+        "completed_at": 1695312007,
+        "error": null,
+        "stopped_at": null
+      }],
+      "doc_form": "text_model"
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+<hr className='ml-0 mr-0' />
+
+<Heading
+  url='/datasets/{dataset_id}/retrieve'
+  method='POST'
+  title='检索知识库'
+  name='#dataset_retrieval'
+/>
+<Row>
+  <Col>
+    ### Path
+    <Properties>
+      <Property name='dataset_id' type='string' key='dataset_id'>
+        知识库 ID
+      </Property>
+    </Properties>
+
+    ### Request Body
+    <Properties>
+      <Property name='query' type='string' key='query'>
+        检索关键词
+      </Property>
+      <Property name='retrieval_model' type='object' key='retrieval_model'>
+        检索参数(选填,如不填,按照默认方式召回)
+        - <code>search_method</code> (text) 检索方法:以下三个关键字之一,必填
+          - <code>keyword_search</code> 关键字检索
+          - <code>semantic_search</code> 语义检索
+          - <code>full_text_search</code> 全文检索
+          - <code>hybrid_search</code> 混合检索
+        - <code>reranking_enable</code> (bool) 是否启用 Reranking,非必填,如果检索模式为 semantic_search 模式或者 hybrid_search 则传值
+        - <code>reranking_mode</code> (object) Rerank模型配置,非必填,如果启用了 reranking 则传值
+            - <code>reranking_provider_name</code> (string) Rerank 模型提供商
+            - <code>reranking_model_name</code> (string) Rerank 模型名称
+        - <code>weights</code> (double) 混合检索模式下语意检索的权重设置
+        - <code>top_k</code> (integer) 返回结果数量,非必填
+        - <code>score_threshold_enabled</code> (bool) 是否开启 score 阈值
+        - <code>score_threshold</code> (double) Score 阈值
+      </Property>
+      <Property name='external_retrieval_model' type='object' key='external_retrieval_model'>
+          未启用字段
+      </Property>
+    </Properties>
+  </Col>
+  <Col sticky>
+    <CodeGroup
+      title="Request"
+      tag="POST"
+      label="/datasets/{dataset_id}/retrieve"
+      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \\\n--header 'Authorization: Bearer {api_key}'\\\n--header 'Content-Type: application/json'\\\n--data-raw '{
+    "query": "test",
+    "retrieval_model": {
+        "search_method": "keyword_search",
+        "reranking_enable": false,
+        "reranking_mode": null,
+        "reranking_model": {
+            "reranking_provider_name": "",
+            "reranking_model_name": ""
+        },
+        "weights": null,
+        "top_k": 1,
+        "score_threshold_enabled": false,
+        "score_threshold": null
+    }
+}'`}
+    >
+    ```bash {{ title: 'cURL' }}
+    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/retrieve' \
+    --header 'Authorization: Bearer {api_key}' \
+    --header 'Content-Type: application/json' \
+    --data-raw '{
+        "query": "test",
+        "retrieval_model": {
+            "search_method": "keyword_search",
+            "reranking_enable": false,
+            "reranking_mode": null,
+            "reranking_model": {
+                "reranking_provider_name": "",
+                "reranking_model_name": ""
+            },
+            "weights": null,
+            "top_k": 2,
+            "score_threshold_enabled": false,
+            "score_threshold": null
+        }
+    }'
+    ```
+    </CodeGroup>
+    <CodeGroup title="Response">
+    ```json {{ title: 'Response' }}
+    {
+      "query": {
+        "content": "test"
+      },
+      "records": [
+        {
+          "segment": {
+            "id": "7fa6f24f-8679-48b3-bc9d-bdf28d73f218",
+            "position": 1,
+            "document_id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+            "content": "Operation guide",
+            "answer": null,
+            "word_count": 847,
+            "tokens": 280,
+            "keywords": [
+              "install",
+              "java",
+              "base",
+              "scripts",
+              "jdk",
+              "manual",
+              "internal",
+              "opens",
+              "add",
+              "vmoptions"
+            ],
+            "index_node_id": "39dd8443-d960-45a8-bb46-7275ad7fbc8e",
+            "index_node_hash": "0189157697b3c6a418ccf8264a09699f25858975578f3467c76d6bfc94df1d73",
+            "hit_count": 0,
+            "enabled": true,
+            "disabled_at": null,
+            "disabled_by": null,
+            "status": "completed",
+            "created_by": "dbcb1ab5-90c8-41a7-8b78-73b235eb6f6f",
+            "created_at": 1728734540,
+            "indexing_at": 1728734552,
+            "completed_at": 1728734584,
+            "error": null,
+            "stopped_at": null,
+            "document": {
+              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
+              "data_source_type": "upload_file",
+              "name": "readme.txt",
+              "doc_type": null
+            }
+          },
+          "score": 3.730463140527718e-05,
+          "tsne_position": null
+        }
+      ]
+    }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+
+
+<hr className='ml-0 mr-0' />
+
+<Row>
+  <Col>
+    ### 错误信息
+    <Properties>
+      <Property name='code' type='string' key='code'>
+        返回的错误代码
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='status' type='number' key='status'>
+        返回的错误状态
+      </Property>
+    </Properties>
+    <Properties>
+      <Property name='message' type='string' key='message'>
+        返回的错误信息
+      </Property>
+    </Properties>
+  </Col>
+  <Col>
+    <CodeGroup title="Example">
+    ```json {{ title: 'Response' }}
+      {
+        "code": "no_file_uploaded",
+        "message": "Please upload your file.",
+        "status": 400
+      }
+    ```
+    </CodeGroup>
+  </Col>
+</Row>
+<table className="max-w-auto border-collapse border border-slate-400" style={{ maxWidth: 'none', width: 'auto' }}>
+  <thead style={{ background: '#f9fafc' }}>
+    <tr>
+      <th className="p-2 border border-slate-300">code</th>
+      <th className="p-2 border border-slate-300">status</th>
+      <th className="p-2 border border-slate-300">message</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td className="p-2 border border-slate-300">no_file_uploaded</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Please upload your file.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">too_many_files</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Only one file is allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">file_too_large</td>
+      <td className="p-2 border border-slate-300">413</td>
+      <td className="p-2 border border-slate-300">File size exceeded.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">unsupported_file_type</td>
+      <td className="p-2 border border-slate-300">415</td>
+      <td className="p-2 border border-slate-300">File type not allowed.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">high_quality_dataset_only</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Current operation only supports 'high-quality' datasets.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_not_initialized</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The dataset is still being initialized or indexing. Please wait a moment.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">archived_document_immutable</td>
+      <td className="p-2 border border-slate-300">403</td>
+      <td className="p-2 border border-slate-300">The archived document is not editable.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">dataset_name_duplicate</td>
+      <td className="p-2 border border-slate-300">409</td>
+      <td className="p-2 border border-slate-300">The dataset name already exists. Please modify your dataset name.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_action</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">Invalid action.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_already_finished</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document has been processed. Please refresh the page or go to the document details.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">document_indexing</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The document is being processed and cannot be edited.</td>
+    </tr>
+    <tr>
+      <td className="p-2 border border-slate-300">invalid_metadata</td>
+      <td className="p-2 border border-slate-300">400</td>
+      <td className="p-2 border border-slate-300">The metadata content is incorrect. Please check and verify.</td>
+    </tr>
+  </tbody>
+</table>
+<div className="pb-4" />

+ 13 - 9
app/(commonLayout)/datasets/Container.tsx

@@ -9,6 +9,7 @@ import { useDebounceFn } from 'ahooks'
 // Components
 import ExternalAPIPanel from '../../components/datasets/external-api/external-api-panel'
 import Datasets from './Datasets'
+
 import DatasetFooter from './DatasetFooter'
 import ApiServer from './ApiServer'
 import Doc from './Doc'
@@ -40,7 +41,7 @@ const Container = () => {
   const options = useMemo(() => {
     return [
       { value: 'dataset', text: t('dataset.datasets') },
-      ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]),
+      // ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]),
     ]
   }, [currentWorkspace.role, t])
 
@@ -81,8 +82,8 @@ const Container = () => {
   }, [currentWorkspace, router])
 
   return (
-    <div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
-      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
+    <div ref={containerRef} className='grow relative flex flex-col overflow-y-auto'>
+      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] z-10 flex-wrap gap-y-2'>
         <TabSliderNew
           value={activeTab}
           onChange={newActiveTab => setActiveTab(newActiveTab)}
@@ -93,29 +94,32 @@ const Container = () => {
             <TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
             <SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
             <div className="w-[1px] h-4 bg-divider-regular" />
-            <Button
+            {/* <Button
               className='gap-0.5 shadows-shadow-xs'
               onClick={() => setShowExternalApiPanel(true)}
             >
               <ApiConnectionMod className='w-4 h-4 text-components-button-secondary-text' />
               <div className='flex px-0.5 justify-center items-center gap-1 text-components-button-secondary-text system-sm-medium'>{t('dataset.externalAPIPanelTitle')}</div>
-            </Button>
+            </Button> */}
           </div>
         )}
-        {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
+        {/* {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />} */}
       </div>
       {activeTab === 'dataset' && (
         <>
           <Datasets containerRef={containerRef} tags={tagIDs} keywords={searchKeywords} />
-          <DatasetFooter />
+         
+
+          {/* <DatasetFooter /> */}
           {showTagManagementModal && (
             <TagManagementModal type='knowledge' show={showTagManagementModal} />
           )}
         </>
+
       )}
-      {activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />}
+      {/* {activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />}
 
-      {showExternalApiPanel && <ExternalAPIPanel onClose={() => setShowExternalApiPanel(false)} />}
+      {showExternalApiPanel && <ExternalAPIPanel onClose={() => setShowExternalApiPanel(false)} />} */}
     </div>
   )
 }

+ 2 - 2
app/(commonLayout)/datasets/DatasetCard.tsx

@@ -139,7 +139,7 @@ const DatasetCard = ({
                 </Tooltip>
               )}
             </div>
-            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
+            {/* <div className='flex items-center mt-[1px] text-xs leading-[18px] text-gray-500'>
               <div
                 className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
                 title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
@@ -157,7 +157,7 @@ const DatasetCard = ({
                   </>
                 }
               </div>
-            </div>
+            </div> */}
           </div>
         </div>
         <div

+ 1 - 0
app/(commonLayout)/datasets/Datasets.tsx

@@ -81,6 +81,7 @@ const Datasets = ({
         <DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
       ))}
     </nav>
+    
   )
 }
 

+ 1 - 1
app/(commonLayout)/datasets/NewDatasetCard.tsx

@@ -11,7 +11,7 @@ const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
   const { t } = useTranslation()
 
   return (
-    <div className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
+    <div style={{backgroundColor:'rgba(255,255,255,.6)'}} className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
       min-h-[160px] transition-all duration-200 ease-in-out'
     >
       <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>

+ 7 - 0
app/(commonLayout)/explore/installed/[appId]/page.tsx

@@ -1,16 +1,23 @@
+// 导入FC类型
 import type { FC } from 'react'
+// 导入React库
 import React from 'react'
+// 导入Main组件
 import Main from '@/app/components/explore/installed-app'
 
+// 定义InstalledApp组件的props类型
 export type IInstalledAppProps = {
   params: {
     appId: string
   }
 }
 
+// 定义InstalledApp组件
 const InstalledApp: FC<IInstalledAppProps> = ({ params: { appId } }) => {
+  // 返回Main组件,并传入appId作为props
   return (
     <Main id={appId} />
   )
 }
+// 使用React.memo优化InstalledApp组件
 export default React.memo(InstalledApp)