Skip to content

Commit

Permalink
feat(web): parent-child-retrieval
Browse files Browse the repository at this point in the history
Signed-off-by: -LAN- <laipz8200@outlook.com>
  • Loading branch information
laipz8200 committed Dec 26, 2024
1 parent efdd54a commit f322bf5
Show file tree
Hide file tree
Showing 196 changed files with 9,007 additions and 3,087 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,85 +7,36 @@ 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 { RiApps2AddLine, RiInformation2Line } from '@remixicon/react'
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 type { 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'
import Tooltip from '@/app/components/base/tooltip'
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'

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)">
Expand Down Expand Up @@ -117,65 +68,80 @@ const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
type IExtraInfoProps = {
isMobile: boolean
relatedApps?: RelatedAppResponse
expand: boolean
}

const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
const locale = getLocaleOnClient()
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
const { t } = useTranslation()

const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
const relatedAppsTotal = relatedApps?.data?.length || 0

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) && (
return <div>
{hasRelatedApps && (
<>
{!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 && (
<Tooltip
position='right'
noDecoration
needsDelay
popupContent={
<LinkedAppsPanel
relatedApps={relatedApps.data}
isMobile={isMobile}
/>
}
>
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
<span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
<RiInformation2Line className='w-4 h-4' />
</div>
</Tooltip>
)}

{isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
{relatedApps?.total || '--'}
{relatedAppsTotal || '--'}
<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' />
{!hasRelatedApps && !expand && (
<Tooltip
position='right'
noDecoration
needsDelay
popupContent={
<div className='p-4 w-[240px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl'>
<div className='inline-flex p-2 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle'>
<RiApps2AddLine className='h-4 w-4 text-text-tertiary' />
</div>
<div className='text-xs text-text-tertiary my-2'>{t('common.datasetMenus.emptyTip')}</div>
<a
className='inline-flex items-center text-xs text-text-accent 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>
}
>
<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 className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
<span>{t('common.datasetMenus.noRelatedApp')}</span>
<RiInformation2Line className='w-4 h-4' />
</div>
</FloatPopoverContainer>
</Tooltip>
)}
</div>
}
Expand Down Expand Up @@ -235,7 +201,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}, [isMobile, setAppSiderbarExpand])

if (!datasetRes && !error)
return <Loading />
return <Loading type='app' />

return (
<div className='grow flex overflow-hidden'>
Expand All @@ -246,15 +212,15 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
desc={datasetRes?.description || '--'}
isExternal={datasetRes?.provider === 'external'}
navigation={navigation}
extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} /> : undefined}
extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : 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>
<div className="bg-background-default-subtle grow overflow-hidden">{children}</div>
</DatasetDetailContext.Provider>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const Settings = async () => {
const { t } = await translate(locale, 'dataset-settings')

return (
<div className='bg-white h-full overflow-y-auto'>
<div className='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 className='mb-1 system-xl-semibold text-text-primary'>{t('title')}</div>
<div className='system-sm-regular text-text-tertiary'>{t('desc')}</div>
</div>
<Form />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
.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;
}
Expand Down
17 changes: 12 additions & 5 deletions web/app/(commonLayout)/datasets/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ 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'
Expand All @@ -29,6 +28,7 @@ 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'
import Input from '@/app/components/base/input'

const Container = () => {
const { t } = useTranslation()
Expand Down Expand Up @@ -81,17 +81,24 @@ 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 bg-background-body overflow-y-auto'>
<div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-background-body 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'>
<div className='flex items-center justify-center gap-2'>
<TagFilter type='knowledge' value={tagFilterValue} onChange={handleTagsChange} />
<SearchInput className='w-[200px]' value={keywords} onChange={handleKeywordsChange} />
<Input
showLeftIcon
showClearIcon
wrapperClassName='w-[200px]'
value={keywords}
onChange={e => handleKeywordsChange(e.target.value)}
onClear={() => handleKeywordsChange('')}
/>
<div className="w-[1px] h-4 bg-divider-regular" />
<Button
className='gap-0.5 shadows-shadow-xs'
Expand Down
10 changes: 5 additions & 5 deletions web/app/(commonLayout)/datasets/DatasetCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const DatasetCard = ({
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'
className='group relative col-span-1 bg-components-card-bg border-[0.5px] border-solid border-components-card-border 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()
Expand All @@ -129,8 +129,8 @@ const DatasetCard = ({
<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>
<div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
<div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100 text-text-tertiary')} title={dataset.name}>{dataset.name}</div>
{!dataset.embedding_available && (
<Tooltip
popupContent={t('dataset.unavailableTip')}
Expand All @@ -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-text-tertiary'>
<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')}`}
Expand All @@ -162,7 +162,7 @@ const DatasetCard = ({
</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]',
'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-text-tertiary 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',
)}
Expand Down
45 changes: 45 additions & 0 deletions web/app/components/app-sidebar/dataset-info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '../base/app-icon'

const DatasetSvg = <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M0.833497 5.13481C0.833483 4.69553 0.83347 4.31654 0.858973 4.0044C0.88589 3.67495 0.94532 3.34727 1.10598 3.03195C1.34567 2.56155 1.72812 2.17909 2.19852 1.93941C2.51384 1.77875 2.84152 1.71932 3.17097 1.6924C3.48312 1.6669 3.86209 1.66691 4.30137 1.66693L7.62238 1.66684C8.11701 1.66618 8.55199 1.66561 8.95195 1.80356C9.30227 1.92439 9.62134 2.12159 9.88607 2.38088C10.1883 2.67692 10.3823 3.06624 10.603 3.50894L11.3484 5.00008H14.3679C15.0387 5.00007 15.5924 5.00006 16.0434 5.03691C16.5118 5.07518 16.9424 5.15732 17.3468 5.36339C17.974 5.68297 18.4839 6.19291 18.8035 6.82011C19.0096 7.22456 19.0917 7.65515 19.13 8.12356C19.1668 8.57455 19.1668 9.12818 19.1668 9.79898V13.5345C19.1668 14.2053 19.1668 14.7589 19.13 15.2099C19.0917 15.6784 19.0096 16.1089 18.8035 16.5134C18.4839 17.1406 17.974 17.6505 17.3468 17.9701C16.9424 18.1762 16.5118 18.2583 16.0434 18.2966C15.5924 18.3334 15.0387 18.3334 14.3679 18.3334H5.63243C4.96163 18.3334 4.40797 18.3334 3.95698 18.2966C3.48856 18.2583 3.05798 18.1762 2.65353 17.9701C2.02632 17.6505 1.51639 17.1406 1.19681 16.5134C0.990734 16.1089 0.908597 15.6784 0.870326 15.2099C0.833478 14.7589 0.833487 14.2053 0.833497 13.5345V5.13481ZM7.51874 3.33359C8.17742 3.33359 8.30798 3.34447 8.4085 3.37914C8.52527 3.41942 8.63163 3.48515 8.71987 3.57158C8.79584 3.64598 8.86396 3.7579 9.15852 4.34704L9.48505 5.00008L2.50023 5.00008C2.50059 4.61259 2.50314 4.34771 2.5201 4.14012C2.5386 3.91374 2.57 3.82981 2.59099 3.7886C2.67089 3.6318 2.79837 3.50432 2.95517 3.42442C2.99638 3.40343 3.08031 3.37203 3.30669 3.35353C3.54281 3.33424 3.85304 3.33359 4.3335 3.33359H7.51874Z" fill="#444CE7" />
</svg>

type Props = {
isExternal?: boolean
name: string
description: string
expand: boolean
extraInfo?: React.ReactNode
}

const DatasetInfo: FC<Props> = ({
name,
description,
isExternal,
expand,
extraInfo,
}) => {
const { t } = useTranslation()
return (
<div className='pl-1 pt-1'>
<div className='flex-shrink-0 mr-3'>
<AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />
</div>
{expand && (
<div className='mt-2'>
<div className='system-md-semibold text-text-secondary'>
{name}
</div>
<div className='mt-1 text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : t('dataset.localDocs')}</div>
<div className='my-3 system-xs-regular text-text-tertiary first-letter:capitalize'>{description}</div>
</div>
)}
{extraInfo}
</div>
)
}
export default React.memo(DatasetInfo)
Loading

0 comments on commit f322bf5

Please sign in to comment.