mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 16:39:26 +08:00
Feature/mutil embedding model (#908)
Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: jyong <jyong@dify.ai> Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
This commit is contained in:
@@ -13,6 +13,9 @@ type IInfiniteVirtualListProps = {
|
||||
loadNextPage: () => Promise<any> // Callback function responsible for loading the next page of items.
|
||||
onClick: (detail: SegmentDetailModel) => void
|
||||
onChangeSwitch: (segId: string, enabled: boolean) => Promise<void>
|
||||
onDelete: (segId: string) => Promise<void>
|
||||
archived?: boolean
|
||||
|
||||
}
|
||||
|
||||
const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
|
||||
@@ -22,6 +25,8 @@ const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
|
||||
loadNextPage,
|
||||
onClick: onClickCard,
|
||||
onChangeSwitch,
|
||||
onDelete,
|
||||
archived,
|
||||
}) => {
|
||||
// If there are more items to be loaded then add an extra row to hold a loading indicator.
|
||||
const itemCount = hasNextPage ? items.length + 1 : items.length
|
||||
@@ -52,7 +57,9 @@ const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
|
||||
detail={segItem}
|
||||
onClick={() => onClickCard(segItem)}
|
||||
onChangeSwitch={onChangeSwitch}
|
||||
onDelete={onDelete}
|
||||
loading={false}
|
||||
archived={archived}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import { ArrowUpRightIcon } from '@heroicons/react/24/outline'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -7,11 +7,15 @@ import { StatusItem } from '../../list'
|
||||
import { DocumentTitle } from '../index'
|
||||
import s from './style.module.css'
|
||||
import { SegmentIndexTag } from './index'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import type { SegmentDetailModel } from '@/models/datasets'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
const ProgressBar: FC<{ percent: number; loading: boolean }> = ({ percent, loading }) => {
|
||||
return (
|
||||
@@ -35,8 +39,10 @@ type ISegmentCardProps = {
|
||||
score?: number
|
||||
onClick?: () => void
|
||||
onChangeSwitch?: (segId: string, enabled: boolean) => Promise<void>
|
||||
onDelete?: (segId: string) => Promise<void>
|
||||
scene?: UsageScene
|
||||
className?: string
|
||||
archived?: boolean
|
||||
}
|
||||
|
||||
const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
@@ -44,9 +50,11 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
score,
|
||||
onClick,
|
||||
onChangeSwitch,
|
||||
onDelete,
|
||||
loading = true,
|
||||
scene = 'doc',
|
||||
className = '',
|
||||
archived,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
@@ -60,6 +68,7 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
answer,
|
||||
} = detail as any
|
||||
const isDocScene = scene === 'doc'
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
const renderContent = () => {
|
||||
if (answer) {
|
||||
@@ -86,7 +95,7 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
s.segWrapper,
|
||||
(isDocScene && !enabled) ? 'bg-gray-25' : '',
|
||||
'group',
|
||||
!loading ? 'pb-4' : '',
|
||||
!loading ? 'pb-4 hover:pb-[10px]' : '',
|
||||
className,
|
||||
)}
|
||||
onClick={() => onClick?.()}
|
||||
@@ -116,6 +125,7 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
>
|
||||
<Switch
|
||||
size='md'
|
||||
disabled={archived}
|
||||
defaultValue={enabled}
|
||||
onChange={async (val) => {
|
||||
await onChangeSwitch?.(id, val)
|
||||
@@ -159,10 +169,18 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
<div className={cn(s.commonIcon, s.targetIcon)} />
|
||||
<div className={s.segDataText}>{formatNumber(hit_count)}</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="grow flex items-center">
|
||||
<div className={cn(s.commonIcon, s.bezierCurveIcon)} />
|
||||
<div className={s.segDataText}>{index_node_hash}</div>
|
||||
</div>
|
||||
{!archived && (
|
||||
<div className='shrink-0 w-6 h-6 flex items-center justify-center rounded-md hover:bg-red-100 hover:text-red-600 cursor-pointer group/delete' onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowModal(true)
|
||||
}}>
|
||||
<Trash03 className='w-[14px] h-[14px] text-gray-500 group-hover/delete:text-red-600' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
: <>
|
||||
@@ -187,6 +205,26 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{showModal && <Modal isShow={showModal} onClose={() => setShowModal(false)} className={s.delModal} closable>
|
||||
<div>
|
||||
<div className={s.warningWrapper}>
|
||||
<AlertCircle className='w-6 h-6 text-red-600' />
|
||||
</div>
|
||||
<div className='text-xl font-semibold text-gray-900 mb-1'>{t('datasetDocuments.segment.delete')}</div>
|
||||
<div className='flex gap-2 justify-end'>
|
||||
<Button onClick={() => setShowModal(false)}>{t('common.operation.cancel')}</Button>
|
||||
<Button
|
||||
type='warning'
|
||||
onClick={async () => {
|
||||
await onDelete?.(id)
|
||||
}}
|
||||
className='border-red-700 border-[0.5px]'
|
||||
>
|
||||
{t('common.operation.sure')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { debounce, isNil, omitBy } from 'lodash-es'
|
||||
import cn from 'classnames'
|
||||
import { StatusItem } from '../../list'
|
||||
import { DocumentContext } from '../index'
|
||||
import { ProcessStatus } from '../segment-add'
|
||||
import s from './style.module.css'
|
||||
import InfiniteVirtualList from './InfiniteVirtualList'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
@@ -18,7 +19,7 @@ import Input from '@/app/components/base/input'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import { disableSegment, enableSegment, fetchSegments, updateSegment } from '@/service/datasets'
|
||||
import { deleteSegment, disableSegment, enableSegment, fetchSegments, updateSegment } from '@/service/datasets'
|
||||
import type { SegmentDetailModel, SegmentUpdator, SegmentsQuery, SegmentsResponse } from '@/models/datasets'
|
||||
import { asyncRunSafe } from '@/utils'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
@@ -48,12 +49,14 @@ type ISegmentDetailProps = {
|
||||
onChangeSwitch?: (segId: string, enabled: boolean) => Promise<void>
|
||||
onUpdate: (segmentId: string, q: string, a: string, k: string[]) => void
|
||||
onCancel: () => void
|
||||
archived?: boolean
|
||||
}
|
||||
/**
|
||||
* Show all the contents of the segment
|
||||
*/
|
||||
export const SegmentDetail: FC<ISegmentDetailProps> = memo(({
|
||||
segInfo,
|
||||
archived,
|
||||
onChangeSwitch,
|
||||
onUpdate,
|
||||
onCancel,
|
||||
@@ -116,31 +119,30 @@ export const SegmentDetail: FC<ISegmentDetailProps> = memo(({
|
||||
return (
|
||||
<div className={'flex flex-col relative'}>
|
||||
<div className='absolute right-0 top-0 flex items-center h-7'>
|
||||
{
|
||||
isEditing
|
||||
? (
|
||||
<>
|
||||
<Button
|
||||
className='mr-2 !h-7 !px-3 !py-[5px] text-xs font-medium text-gray-700 !rounded-md'
|
||||
onClick={handleCancel}>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
className='!h-7 !px-3 !py-[5px] text-xs font-medium !rounded-md'
|
||||
onClick={handleSave}>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<div className='group relative flex justify-center items-center w-6 h-6 hover:bg-gray-100 rounded-md cursor-pointer'>
|
||||
<div className={cn(s.editTip, 'hidden items-center absolute -top-10 px-3 h-[34px] bg-white rounded-lg whitespace-nowrap text-xs font-semibold text-gray-700 group-hover:flex')}>{t('common.operation.edit')}</div>
|
||||
<Edit03 className='w-4 h-4 text-gray-500' onClick={() => setIsEditing(true)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='mx-3 w-[1px] h-3 bg-gray-200' />
|
||||
{isEditing && (
|
||||
<>
|
||||
<Button
|
||||
className='mr-2 !h-7 !px-3 !py-[5px] text-xs font-medium text-gray-700 !rounded-md'
|
||||
onClick={handleCancel}>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
className='!h-7 !px-3 !py-[5px] text-xs font-medium !rounded-md'
|
||||
onClick={handleSave}>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{!isEditing && !archived && (
|
||||
<>
|
||||
<div className='group relative flex justify-center items-center w-6 h-6 hover:bg-gray-100 rounded-md cursor-pointer'>
|
||||
<div className={cn(s.editTip, 'hidden items-center absolute -top-10 px-3 h-[34px] bg-white rounded-lg whitespace-nowrap text-xs font-semibold text-gray-700 group-hover:flex')}>{t('common.operation.edit')}</div>
|
||||
<Edit03 className='w-4 h-4 text-gray-500' onClick={() => setIsEditing(true)} />
|
||||
</div>
|
||||
<div className='mx-3 w-[1px] h-3 bg-gray-200' />
|
||||
</>
|
||||
)}
|
||||
<div className='flex justify-center items-center w-6 h-6 cursor-pointer' onClick={onCancel}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
@@ -176,6 +178,7 @@ export const SegmentDetail: FC<ISegmentDetailProps> = memo(({
|
||||
onChange={async (val) => {
|
||||
await onChangeSwitch?.(segInfo?.id || '', val)
|
||||
}}
|
||||
disabled={archived}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,13 +198,20 @@ export const splitArray = (arr: any[], size = 3) => {
|
||||
type ICompletedProps = {
|
||||
showNewSegmentModal: boolean
|
||||
onNewSegmentModalChange: (state: boolean) => void
|
||||
importStatus: ProcessStatus | string | undefined
|
||||
archived?: boolean
|
||||
// data: Array<{}> // all/part segments
|
||||
}
|
||||
/**
|
||||
* Embedding done, show list of all segments
|
||||
* Support search and filter
|
||||
*/
|
||||
const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModalChange }) => {
|
||||
const Completed: FC<ICompletedProps> = ({
|
||||
showNewSegmentModal,
|
||||
onNewSegmentModalChange,
|
||||
importStatus,
|
||||
archived,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { datasetId = '', documentId = '', docForm } = useContext(DocumentContext)
|
||||
@@ -250,11 +260,6 @@ const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModal
|
||||
getSegments(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSegmentsRes !== undefined)
|
||||
getSegments(false)
|
||||
}, [selectedStatus, searchValue])
|
||||
|
||||
const onClickCard = (detail: SegmentDetailModel) => {
|
||||
setCurrSegment({ segInfo: detail, showModal: true })
|
||||
}
|
||||
@@ -281,6 +286,17 @@ const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModal
|
||||
}
|
||||
}
|
||||
|
||||
const onDelete = async (segId: string) => {
|
||||
const [e] = await asyncRunSafe<CommonResponse>(deleteSegment({ datasetId, documentId, segmentId: segId }) as Promise<CommonResponse>)
|
||||
if (!e) {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
resetList()
|
||||
}
|
||||
else {
|
||||
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
|
||||
}
|
||||
}
|
||||
|
||||
const handleUpdateSegment = async (segmentId: string, question: string, answer: string, keywords: string[]) => {
|
||||
const params: SegmentUpdator = { content: '' }
|
||||
if (docForm === 'qa_model') {
|
||||
@@ -321,6 +337,16 @@ const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModal
|
||||
setAllSegments([...allSegments])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSegmentsRes !== undefined)
|
||||
getSegments(false)
|
||||
}, [selectedStatus, searchValue])
|
||||
|
||||
useEffect(() => {
|
||||
if (importStatus === ProcessStatus.COMPLETED)
|
||||
resetList()
|
||||
}, [importStatus])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={s.docSearchWrapper}>
|
||||
@@ -343,7 +369,9 @@ const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModal
|
||||
items={allSegments}
|
||||
loadNextPage={getSegments}
|
||||
onChangeSwitch={onChangeSwitch}
|
||||
onDelete={onDelete}
|
||||
onClick={onClickCard}
|
||||
archived={archived}
|
||||
/>
|
||||
<Modal isShow={currSegment.showModal} onClose={() => {}} className='!max-w-[640px] !overflow-visible'>
|
||||
<SegmentDetail
|
||||
@@ -351,6 +379,7 @@ const Completed: FC<ICompletedProps> = ({ showNewSegmentModal, onNewSegmentModal
|
||||
onChangeSwitch={onChangeSwitch}
|
||||
onUpdate={handleUpdateSegment}
|
||||
onCancel={onCloseModal}
|
||||
archived={archived}
|
||||
/>
|
||||
</Modal>
|
||||
<NewSegmentModal
|
||||
|
||||
@@ -132,3 +132,24 @@
|
||||
.editTip {
|
||||
box-shadow: 0px 4px 6px -2px rgba(16, 24, 40, 0.03), 0px 12px 16px -4px rgba(16, 24, 40, 0.08);
|
||||
}
|
||||
|
||||
.delModal {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(217, 45, 32, 0.05) 0%,
|
||||
rgba(217, 45, 32, 0) 24.02%
|
||||
),
|
||||
#f9fafb;
|
||||
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 8px 8px -4px rgba(16, 24, 40, 0.03);
|
||||
@apply rounded-2xl p-8;
|
||||
}
|
||||
.warningWrapper {
|
||||
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 8px 8px -4px rgba(16, 24, 40, 0.03);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
@apply h-12 w-12 border-[0.5px] border-gray-100 rounded-xl mb-3 flex items-center justify-center;
|
||||
}
|
||||
.warningIcon {
|
||||
@apply w-[22px] h-[22px] fill-current text-red-600;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user