feat: api dataset support pdf parse;fix: chunk reader auth (#4117)

* feat: api dataset support pdf parse

* fix: chunk reader auth
This commit is contained in:
Archer 2025-03-12 12:41:19 +08:00 committed by archer
parent 30f83f848d
commit 1a3613cd2c
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
27 changed files with 378 additions and 355 deletions

View File

@ -11,6 +11,7 @@ weight: 799
1. 商业版支持单团队模式,更好的管理内部成员。 1. 商业版支持单团队模式,更好的管理内部成员。
2. 知识库分块阅读器。 2. 知识库分块阅读器。
3. API 知识库支持 PDF 增强解析。
## ⚙️ 优化 ## ⚙️ 优化

View File

@ -111,11 +111,13 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
const getFileContent = async ({ const getFileContent = async ({
teamId, teamId,
tmbId, tmbId,
apiFileId apiFileId,
customPdfParse
}: { }: {
teamId: string; teamId: string;
tmbId: string; tmbId: string;
apiFileId: string; apiFileId: string;
customPdfParse?: boolean;
}) => { }) => {
const data = await request<APIFileContentResponse>( const data = await request<APIFileContentResponse>(
`/v1/file/content`, `/v1/file/content`,
@ -133,7 +135,8 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
teamId, teamId,
tmbId, tmbId,
url: previewUrl, url: previewUrl,
relatedId: apiFileId relatedId: apiFileId,
customPdfParse
}); });
return rawText; return rawText;
} }

View File

@ -127,7 +127,8 @@ export const readApiServerFileContent = async ({
yuqueServer, yuqueServer,
apiFileId, apiFileId,
teamId, teamId,
tmbId tmbId,
customPdfParse
}: { }: {
apiServer?: APIFileServer; apiServer?: APIFileServer;
feishuServer?: FeishuServer; feishuServer?: FeishuServer;
@ -135,9 +136,15 @@ export const readApiServerFileContent = async ({
apiFileId: string; apiFileId: string;
teamId: string; teamId: string;
tmbId: string; tmbId: string;
customPdfParse?: boolean;
}) => { }) => {
if (apiServer) { if (apiServer) {
return useApiDatasetRequest({ apiServer }).getFileContent({ teamId, tmbId, apiFileId }); return useApiDatasetRequest({ apiServer }).getFileContent({
teamId,
tmbId,
apiFileId,
customPdfParse
});
} }
if (feishuServer || yuqueServer) { if (feishuServer || yuqueServer) {

View File

@ -11,19 +11,17 @@ import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { getQuoteDataList } from '@/web/core/chat/api'; import { getQuoteDataList } from '@/web/core/chat/api';
const QuoteList = React.memo(function QuoteList({ const QuoteList = React.memo(function QuoteList({
chatItemId, chatItemDataId = '',
rawSearch = [], rawSearch = []
chatTime
}: { }: {
chatItemId?: string; chatItemDataId?: string;
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
chatTime: Date;
}) { }) {
const theme = useTheme(); const theme = useTheme();
const { chatId, appId, outLinkAuthData } = useChatStore(); const { chatId, appId, outLinkAuthData } = useChatStore();
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({ const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
chatItemId, chatItemDataId,
appId: v.appId, appId: v.appId,
chatId: v.chatId, chatId: v.chatId,
...(v.outLinkAuthData || {}) ...(v.outLinkAuthData || {})
@ -34,13 +32,12 @@ const QuoteList = React.memo(function QuoteList({
(v) => v.showRouteToDatasetDetail (v) => v.showRouteToDatasetDetail
); );
const { data } = useRequest2( const { data: quoteList } = useRequest2(
async () => async () =>
await getQuoteDataList({ await getQuoteDataList({
datasetDataIdList: rawSearch.map((item) => item.id), datasetDataIdList: rawSearch.map((item) => item.id),
chatTime,
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))], collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
chatItemId: chatItemId || '', chatItemDataId,
appId, appId,
chatId, chatId,
...outLinkAuthData ...outLinkAuthData
@ -53,7 +50,7 @@ const QuoteList = React.memo(function QuoteList({
const formatedDataList = useMemo(() => { const formatedDataList = useMemo(() => {
return rawSearch return rawSearch
.map((item) => { .map((item) => {
const currentFilterItem = data?.quoteList.find((res) => res._id === item.id); const currentFilterItem = quoteList?.find((res) => res._id === item.id);
return { return {
...item, ...item,
@ -66,7 +63,7 @@ const QuoteList = React.memo(function QuoteList({
const bScore = formatScore(b.score); const bScore = formatScore(b.score);
return (bScore.primaryScore?.value || 0) - (aScore.primaryScore?.value || 0); return (bScore.primaryScore?.value || 0) - (aScore.primaryScore?.value || 0);
}); });
}, [data?.quoteList, rawSearch]); }, [quoteList, rawSearch]);
return ( return (
<> <>

View File

@ -42,6 +42,8 @@ const ResponseTags = ({
const [quoteFolded, setQuoteFolded] = useState<boolean>(true); const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
@ -156,17 +158,15 @@ const ResponseTags = ({
e.stopPropagation(); e.stopPropagation();
setQuoteData({ setQuoteData({
chatTime,
rawSearch: quoteList, rawSearch: quoteList,
metadata: { metadata: {
appId,
chatId,
chatItemDataId: dataId,
collectionId: item.collectionId, collectionId: item.collectionId,
collectionIdList: [
...new Set(quoteList.map((item) => item.collectionId))
],
sourceId: item.sourceId || '', sourceId: item.sourceId || '',
sourceName: item.sourceName, sourceName: item.sourceName,
datasetId: item.datasetId, datasetId: item.datasetId
chatItemId: historyItem.dataId
} }
}); });
}} }}
@ -225,15 +225,12 @@ const ResponseTags = ({
e.stopPropagation(); e.stopPropagation();
setQuoteData({ setQuoteData({
chatTime,
rawSearch: quoteList, rawSearch: quoteList,
metadata: { metadata: {
collectionId: '', appId,
collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))], chatId,
chatItemId: historyItem.dataId, chatItemDataId: dataId,
sourceId: '', collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))]
sourceName: '',
datasetId: ''
} }
}); });
}} }}

View File

@ -265,13 +265,7 @@ export const WholeResponseContent = ({
{activeModule.quoteList && activeModule.quoteList.length > 0 && ( {activeModule.quoteList && activeModule.quoteList.length > 0 && (
<Row <Row
label={t('common:core.chat.response.module quoteList')} label={t('common:core.chat.response.module quoteList')}
rawDom={ rawDom={<QuoteList chatItemDataId={dataId} rawSearch={activeModule.quoteList} />}
<QuoteList
chatItemId={dataId}
chatTime={chatTime || new Date()}
rawSearch={activeModule.quoteList}
/>
}
/> />
)} )}
</> </>

View File

@ -243,7 +243,7 @@ const QuoteItem = ({
color={'primary.500'} color={'primary.500'}
href={`/dataset/detail?datasetId=${quoteItem.datasetId}&currentTab=dataCard&collectionId=${quoteItem.collectionId}`} href={`/dataset/detail?datasetId=${quoteItem.datasetId}&currentTab=dataCard&collectionId=${quoteItem.collectionId}`}
> >
{t('common:core.dataset.Go Dataset')} {t('chat:to_dataset')}
<MyIcon name={'common/rightArrowLight'} w={'10px'} /> <MyIcon name={'common/rightArrowLight'} w={'10px'} />
</Link> </Link>
)} )}

View File

@ -23,7 +23,7 @@ const RawSourceBox = ({
collectionId, collectionId,
appId, appId,
chatId, chatId,
chatItemId, chatItemDataId,
shareId, shareId,
outLinkUid, outLinkUid,
teamId, teamId,
@ -40,7 +40,7 @@ const RawSourceBox = ({
collectionId, collectionId,
appId, appId,
chatId, chatId,
chatItemId, chatItemDataId,
shareId, shareId,
outLinkUid, outLinkUid,
teamId, teamId,

View File

@ -184,7 +184,6 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
borderRadius={'md'} borderRadius={'md'}
> >
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -88,7 +88,6 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
{quoteData && ( {quoteData && (
<Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}> <Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}>
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -163,7 +163,6 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
borderRadius={'md'} borderRadius={'md'}
> >
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -1,4 +1,4 @@
import { Box, Button, Flex, HStack } from '@chakra-ui/react'; import { Box, Flex, HStack } from '@chakra-ui/react';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
@ -8,41 +8,34 @@ import DownloadButton from './DownloadButton';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { downloadFetch } from '@/web/common/system/utils'; import { downloadFetch } from '@/web/common/system/utils';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { getCollectionSource, getDatasetDataPermission } from '@/web/core/dataset/api'; import { getDatasetDataPermission } from '@/web/core/dataset/api';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import ScoreTag from './ScoreTag'; import ScoreTag from './ScoreTag';
import { formatScore } from '@/components/core/dataset/QuoteItem'; import { formatScore } from '@/components/core/dataset/QuoteItem';
import NavButton from './NavButton'; import NavButton from './NavButton';
import { useLinkedScroll } from '@fastgpt/web/hooks/useLinkedScroll'; import { useLinkedScroll } from '@fastgpt/web/hooks/useLinkedScroll';
import CollectionQuoteItem from './CollectionQuoteItem'; import CollectionQuoteItem from './CollectionQuoteItem';
import { DatasetDataListItemType } from '@/global/core/dataset/type'; import { GetCollectionQuoteDataProps } from '@/web/core/chat/context/chatItemContext';
import { metadataType } from '@/web/core/chat/context/chatItemContext';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { getCollectionQuote } from '@/web/core/chat/api'; import { getCollectionQuote } from '@/web/core/chat/api';
import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollectionSource'; import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollectionSource';
import { QuoteDataItemType } from '@/service/core/chat/constants';
const CollectionReader = ({ const CollectionReader = ({
rawSearch, rawSearch,
metadata, metadata,
chatTime,
onClose onClose
}: { }: {
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
metadata: metadataType; metadata: GetCollectionQuoteDataProps;
chatTime: Date;
onClose: () => void; onClose: () => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const { toast } = useToast();
const { chatId, appId, outLinkAuthData } = useChatStore();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { collectionId, datasetId, chatItemId, sourceId, sourceName } = metadata; const { collectionId, datasetId, chatItemDataId, sourceId, sourceName } = metadata;
const [quoteIndex, setQuoteIndex] = useState(0); const [quoteIndex, setQuoteIndex] = useState(0);
const { data: permissionData, loading: isPermissionLoading } = useRequest2( const { data: permissionData, loading: isPermissionLoading } = useRequest2(
@ -75,11 +68,10 @@ const CollectionReader = ({
refreshDeps: [collectionId], refreshDeps: [collectionId],
params: { params: {
collectionId, collectionId,
chatTime, chatItemDataId,
chatItemId, chatId: metadata.chatId,
chatId, appId: metadata.appId,
appId, ...metadata.outLinkAuthData
...outLinkAuthData
}, },
initialId: currentQuoteItem?.id, initialId: currentQuoteItem?.id,
initialIndex: currentQuoteItem?.chunkIndex, initialIndex: currentQuoteItem?.chunkIndex,
@ -87,11 +79,14 @@ const CollectionReader = ({
}); });
const loading = isLoading || isPermissionLoading; const loading = isLoading || isPermissionLoading;
const isDeleted = !datasetDataList.find((item) => item._id === currentQuoteItem?.id); const isDeleted = useMemo(
() => !datasetDataList.find((item) => item._id === currentQuoteItem?.id),
[datasetDataList, currentQuoteItem?.id]
);
const formatedDataList = useMemo( const formatedDataList = useMemo(
() => () =>
datasetDataList.map((item: DatasetDataListItemType) => { datasetDataList.map((item: QuoteDataItemType) => {
const isCurrentSelected = currentQuoteItem?.id === item._id; const isCurrentSelected = currentQuoteItem?.id === item._id;
const quoteIndex = filterResults.findIndex((res) => res.id === item._id); const quoteIndex = filterResults.findIndex((res) => res.id === item._id);
@ -115,17 +110,12 @@ const CollectionReader = ({
filename: 'data.csv', filename: 'data.csv',
body: { body: {
collectionId: collectionId, collectionId: collectionId,
chatTime: chatTime, chatItemDataId
chatItemId: chatItemId
} }
}); });
}); });
const handleRead = getCollectionSourceAndOpen({ const handleRead = getCollectionSourceAndOpen(metadata);
appId,
chatId,
...metadata
});
const handleNavigate = useCallback( const handleNavigate = useCallback(
async (targetIndex: number) => { async (targetIndex: number) => {

View File

@ -4,75 +4,60 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import QuoteItem from './QuoteItem'; import QuoteItem from './QuoteItem';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import { formatScore } from '@/components/core/dataset/QuoteItem'; import { formatScore } from '@/components/core/dataset/QuoteItem';
import { metadataType } from '@/web/core/chat/context/chatItemContext'; import { GetAllQuoteDataProps } from '@/web/core/chat/context/chatItemContext';
import { getQuoteDataList } from '@/web/core/chat/api'; import { getQuoteDataList } from '@/web/core/chat/api';
const QuoteReader = ({ const QuoteReader = ({
rawSearch, rawSearch,
metadata, metadata,
chatTime,
onClose onClose
}: { }: {
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
metadata: metadataType; metadata: GetAllQuoteDataProps;
chatTime: Date;
onClose: () => void; onClose: () => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { chatId, appId, outLinkAuthData } = useChatStore(); const { data: quoteList, loading } = useRequest2(
const { data, loading } = useRequest2(
async () => async () =>
await getQuoteDataList({ await getQuoteDataList({
datasetDataIdList: rawSearch.map((item) => item.id), datasetDataIdList: rawSearch.map((item) => item.id),
chatTime,
collectionIdList: metadata.collectionIdList, collectionIdList: metadata.collectionIdList,
chatItemId: metadata.chatItemId, chatItemDataId: metadata.chatItemDataId,
appId, appId: metadata.appId,
chatId, chatId: metadata.chatId,
...outLinkAuthData ...metadata.outLinkAuthData
}), }),
{ {
manual: false manual: false
} }
); );
const filterResults = useMemo(() => {
if (!metadata.collectionId) {
return rawSearch;
}
return rawSearch.filter(
(item) => item.collectionId === metadata.collectionId && item.sourceId === metadata.sourceId
);
}, [metadata, rawSearch]);
const formatedDataList = useMemo(() => { const formatedDataList = useMemo(() => {
return filterResults return rawSearch
.map((item) => { .map((searchItem) => {
const currentFilterItem = data?.quoteList.find((res) => res._id === item.id); const dataItem = quoteList?.find((item) => item._id === searchItem.id);
return { return {
...item, id: searchItem.id,
q: currentFilterItem?.q || '', q: dataItem?.q || 'Can not find Data',
a: currentFilterItem?.a || '', a: dataItem?.a || '',
score: formatScore(item.score), score: formatScore(searchItem.score),
sourceName: searchItem?.sourceName || '',
icon: getSourceNameIcon({ icon: getSourceNameIcon({
sourceId: item.sourceId, sourceId: searchItem.sourceId,
sourceName: item.sourceName sourceName: searchItem.sourceName
}) })
}; };
}) })
.sort((a, b) => { .sort((a, b) => {
return (b.score.primaryScore?.value || 0) - (a.score.primaryScore?.value || 0); return (b.score.primaryScore?.value || 0) - (a.score.primaryScore?.value || 0);
}); });
}, [data?.quoteList, filterResults]); }, [quoteList, rawSearch]);
return ( return (
<Flex flexDirection={'column'} h={'full'}> <Flex flexDirection={'column'} h={'full'}>
@ -86,18 +71,7 @@ const QuoteReader = ({
> >
<Box flex={1} py={4}> <Box flex={1} py={4}>
<Flex gap={2} mr={2} mb={1}> <Flex gap={2} mr={2} mb={1}>
<MyIcon <MyIcon name={'core/chat/quoteFill'} w={['1rem', '1.25rem']} color={'primary.600'} />
name={
metadata.sourceId && metadata.sourceName
? (getSourceNameIcon({
sourceId: metadata.sourceId,
sourceName: metadata.sourceName
}) as any)
: 'core/chat/quoteFill'
}
w={['1rem', '1.25rem']}
color={'primary.600'}
/>
<Box <Box
maxW={['200px', '300px']} maxW={['200px', '300px']}
className={'textEllipsis'} className={'textEllipsis'}
@ -105,9 +79,7 @@ const QuoteReader = ({
color={'myGray.900'} color={'myGray.900'}
fontWeight={'medium'} fontWeight={'medium'}
> >
{metadata.sourceName {t('common:core.chat.Quote Amount', { amount: rawSearch.length })}
? metadata.sourceName
: t('common:core.chat.Quote Amount', { amount: rawSearch.length })}
</Box> </Box>
</Flex> </Flex>
<Box fontSize={'mini'} color={'myGray.500'}> <Box fontSize={'mini'} color={'myGray.500'}>

View File

@ -1,39 +1,28 @@
import React from 'react'; import React from 'react';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { ChatItemContext, metadataType } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext, GetQuoteProps } from '@/web/core/chat/context/chatItemContext';
import CollectionQuoteReader from './CollectionQuoteReader'; import CollectionQuoteReader from './CollectionQuoteReader';
import QuoteReader from './QuoteReader'; import QuoteReader from './QuoteReader';
const ChatQuoteList = ({ const ChatQuoteList = ({
chatTime,
rawSearch = [], rawSearch = [],
metadata, metadata,
onClose onClose
}: { }: {
chatTime: Date;
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
metadata: metadataType; metadata: GetQuoteProps;
onClose: () => void; onClose: () => void;
}) => { }) => {
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource); const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
return ( return (
<> <>
{metadata.collectionId && isShowReadRawSource ? ( {'collectionId' in metadata && isShowReadRawSource && (
<CollectionQuoteReader <CollectionQuoteReader rawSearch={rawSearch} metadata={metadata} onClose={onClose} />
rawSearch={rawSearch} )}
metadata={metadata} {'collectionIdList' in metadata && (
chatTime={chatTime} <QuoteReader rawSearch={rawSearch} metadata={metadata} onClose={onClose} />
onClose={onClose}
/>
) : (
<QuoteReader
rawSearch={rawSearch}
metadata={metadata}
chatTime={chatTime}
onClose={onClose}
/>
)} )}
</> </>
); );

View File

@ -5,25 +5,25 @@ import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { LinkedListResponse, LinkedPaginationProps } from '@fastgpt/web/common/fetch/type'; import { LinkedListResponse, LinkedPaginationProps } from '@fastgpt/web/common/fetch/type';
import { FilterQuery, Types } from 'mongoose'; import { FilterQuery, Types } from 'mongoose';
import { dataFieldSelector, processChatTimeFilter } from './getQuote'; import { quoteDataFieldSelector, QuoteDataItemType } from '@/service/core/chat/constants';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth'; import { processChatTimeFilter } from '@/service/core/chat/utils';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
export type GetCollectionQuoteProps = LinkedPaginationProps & { export type GetCollectionQuoteProps = LinkedPaginationProps & {
chatTime: Date; chatId: string;
chatItemDataId: string;
isInitialLoad: boolean; isInitialLoad: boolean;
collectionId: string; collectionId: string;
chatItemId: string;
appId: string; appId: string;
chatId: string;
shareId?: string; shareId?: string;
outLinkUid?: string; outLinkUid?: string;
teamId?: string; teamId?: string;
teamToken?: string; teamToken?: string;
}; };
export type GetCollectionQuoteRes = LinkedListResponse<DatasetDataSchemaType>; export type GetCollectionQuoteRes = LinkedListResponse<QuoteDataItemType>;
type BaseMatchType = FilterQuery<DatasetDataSchemaType>; type BaseMatchType = FilterQuery<DatasetDataSchemaType>;
@ -37,11 +37,10 @@ async function handler(
prevIndex, prevIndex,
nextId, nextId,
nextIndex, nextIndex,
chatTime,
isInitialLoad, isInitialLoad,
collectionId, collectionId,
chatItemId, chatItemDataId,
appId, appId,
chatId, chatId,
shareId, shareId,
@ -53,61 +52,50 @@ async function handler(
const limitedPageSize = Math.min(pageSize, 30); const limitedPageSize = Math.min(pageSize, 30);
try { const [{ chat }, { chatItem }] = await Promise.all([
await authDatasetCollection({ authChatCrud({
req, req,
authToken: true, authToken: true,
authApiKey: true, appId,
collectionId: req.body.collectionId, chatId,
per: ReadPermissionVal shareId,
}); outLinkUid,
} catch (error) { teamId,
await Promise.all([ teamToken
authChatCrud({ }),
req, authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
authToken: true, ]);
appId, if (!chat) return Promise.reject(ChatErrEnum.unAuthChat);
chatId,
shareId,
outLinkUid,
teamId,
teamToken
}),
authCollectionInChat({ appId, chatId, chatItemId, collectionId })
]);
}
const baseMatch: BaseMatchType = { const baseMatch: BaseMatchType = {
collectionId, collectionId,
$or: [ $or: [
{ updateTime: { $lt: new Date(chatTime) } }, { updateTime: { $lt: new Date(chatItem.time) } },
{ history: { $elemMatch: { updateTime: { $lt: new Date(chatTime) } } } } { history: { $elemMatch: { updateTime: { $lt: new Date(chatItem.time) } } } }
] ]
}; };
if (initialId && initialIndex !== undefined) { if (initialId && initialIndex !== undefined) {
return await handleInitialLoad( return await handleInitialLoad({
initialId, initialId,
initialIndex, initialIndex,
limitedPageSize, pageSize: limitedPageSize,
chatTime, chatTime: chatItem.time,
chatItemId,
isInitialLoad, isInitialLoad,
baseMatch baseMatch
); });
} }
if ((prevId && prevIndex !== undefined) || (nextId && nextIndex !== undefined)) { if ((prevId && prevIndex !== undefined) || (nextId && nextIndex !== undefined)) {
return await handlePaginatedLoad( return await handlePaginatedLoad({
prevId, prevId,
prevIndex, prevIndex,
nextId, nextId,
nextIndex, nextIndex,
limitedPageSize, pageSize: limitedPageSize,
chatTime, chatTime: chatItem.time,
chatItemId,
baseMatch baseMatch
); });
} }
return { list: [], hasMorePrev: false, hasMoreNext: false }; return { list: [], hasMorePrev: false, hasMoreNext: false };
@ -115,38 +103,39 @@ async function handler(
export default NextAPI(handler); export default NextAPI(handler);
async function handleInitialLoad( async function handleInitialLoad({
initialId: string, initialId,
initialIndex: number, initialIndex,
pageSize: number, pageSize,
chatTime: Date, chatTime,
chatItemId: string, isInitialLoad,
isInitialLoad: boolean, baseMatch
baseMatch: BaseMatchType }: {
): Promise<GetCollectionQuoteRes> { initialId: string;
initialIndex: number;
pageSize: number;
chatTime: Date;
isInitialLoad: boolean;
baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> {
const centerNode = await MongoDatasetData.findOne( const centerNode = await MongoDatasetData.findOne(
{ {
_id: new Types.ObjectId(initialId) _id: new Types.ObjectId(initialId)
}, },
dataFieldSelector quoteDataFieldSelector
).lean(); ).lean();
if (!centerNode) { if (!centerNode) {
if (isInitialLoad) { if (isInitialLoad) {
const list = await MongoDatasetData.find(baseMatch, dataFieldSelector) const list = await MongoDatasetData.find(baseMatch, quoteDataFieldSelector)
.sort({ chunkIndex: 1, _id: -1 }) .sort({ chunkIndex: 1, _id: -1 })
.limit(pageSize) .limit(pageSize)
.lean(); .lean();
const listRes = list.map((item, index) => ({
...item,
index: item.chunkIndex
}));
const hasMoreNext = list.length === pageSize; const hasMoreNext = list.length === pageSize;
return { return {
list: listRes, list: processChatTimeFilter(list, chatTime),
hasMorePrev: false, hasMorePrev: false,
hasMoreNext hasMoreNext
}; };
@ -173,28 +162,30 @@ async function handleInitialLoad(
const resultList = [...prevList, centerNode, ...nextList]; const resultList = [...prevList, centerNode, ...nextList];
const list = processChatTimeFilter(resultList, chatTime);
return { return {
list: list.map((item) => ({ list: processChatTimeFilter(resultList, chatTime),
...item,
index: item.chunkIndex
})),
hasMorePrev, hasMorePrev,
hasMoreNext hasMoreNext
}; };
} }
async function handlePaginatedLoad( async function handlePaginatedLoad({
prevId: string | undefined, prevId,
prevIndex: number | undefined, prevIndex,
nextId: string | undefined, nextId,
nextIndex: number | undefined, nextIndex,
pageSize: number, pageSize,
chatTime: Date, chatTime,
chatItemId: string, baseMatch
baseMatch: BaseMatchType }: {
): Promise<GetCollectionQuoteRes> { prevId: string | undefined;
prevIndex: number | undefined;
nextId: string | undefined;
nextIndex: number | undefined;
pageSize: number;
chatTime: Date;
baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> {
const { list, hasMore } = const { list, hasMore } =
prevId && prevIndex !== undefined prevId && prevIndex !== undefined
? await getPrevNodes(prevId, prevIndex, pageSize, baseMatch) ? await getPrevNodes(prevId, prevIndex, pageSize, baseMatch)
@ -203,10 +194,7 @@ async function handlePaginatedLoad(
const processedList = processChatTimeFilter(list, chatTime); const processedList = processChatTimeFilter(list, chatTime);
return { return {
list: processedList.map((item) => ({ list: processedList,
...item,
index: item.chunkIndex
})),
hasMorePrev: !!prevId && hasMore, hasMorePrev: !!prevId && hasMore,
hasMoreNext: !!nextId && hasMore hasMoreNext: !!nextId && hasMore
}; };
@ -217,7 +205,10 @@ async function getPrevNodes(
initialIndex: number, initialIndex: number,
limit: number, limit: number,
baseMatch: BaseMatchType baseMatch: BaseMatchType
) { ): Promise<{
list: DatasetDataSchemaType[];
hasMore: boolean;
}> {
const match: BaseMatchType = { const match: BaseMatchType = {
...baseMatch, ...baseMatch,
$or: [ $or: [
@ -226,7 +217,7 @@ async function getPrevNodes(
] ]
}; };
const list = await MongoDatasetData.find(match, dataFieldSelector) const list = await MongoDatasetData.find(match, quoteDataFieldSelector)
.sort({ chunkIndex: -1, _id: 1 }) .sort({ chunkIndex: -1, _id: 1 })
.limit(limit) .limit(limit)
.lean(); .lean();
@ -242,7 +233,10 @@ async function getNextNodes(
initialIndex: number, initialIndex: number,
limit: number, limit: number,
baseMatch: BaseMatchType baseMatch: BaseMatchType
) { ): Promise<{
list: DatasetDataSchemaType[];
hasMore: boolean;
}> {
const match: BaseMatchType = { const match: BaseMatchType = {
...baseMatch, ...baseMatch,
$or: [ $or: [
@ -251,7 +245,7 @@ async function getNextNodes(
] ]
}; };
const list = await MongoDatasetData.find(match, dataFieldSelector) const list = await MongoDatasetData.find(match, quoteDataFieldSelector)
.sort({ chunkIndex: 1, _id: -1 }) .sort({ chunkIndex: 1, _id: -1 })
.limit(limit) .limit(limit)
.lean(); .lean();

View File

@ -1,100 +1,64 @@
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat'; import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { quoteDataFieldSelector, QuoteDataItemType } from '@/service/core/chat/constants';
import { processChatTimeFilter } from '@/service/core/chat/utils';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
export type GetQuoteDataProps = { export type GetQuoteDataProps = {
datasetDataIdList: string[]; datasetDataIdList: string[];
chatTime: Date;
collectionIdList: string[]; collectionIdList: string[];
chatItemId: string;
appId: string;
chatId: string; chatId: string;
chatItemDataId: string;
appId: string;
shareId?: string; shareId?: string;
outLinkUid?: string; outLinkUid?: string;
teamId?: string; teamId?: string;
teamToken?: string; teamToken?: string;
}; };
export type GetQuoteDataRes = { export type GetQuoteDataRes = QuoteDataItemType[];
quoteList: DatasetDataSchemaType[];
};
export const dataFieldSelector =
'_id datasetId collectionId q a chunkIndex history updateTime currentChatItemId prevId';
async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuoteDataRes> { async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuoteDataRes> {
const { const {
datasetDataIdList, appId,
chatTime, chatId,
chatItemDataId,
shareId,
outLinkUid,
teamId,
teamToken,
collectionIdList, collectionIdList,
chatItemId, datasetDataIdList
chatId,
appId,
shareId,
outLinkUid,
teamId,
teamToken
} = req.body; } = req.body;
await authChatCrud({ const [chat, { chatItem }] = await Promise.all([
req, authChatCrud({
authToken: true, req,
appId, authToken: true,
chatId, appId,
shareId, chatId,
outLinkUid, shareId,
teamId, outLinkUid,
teamToken teamId,
}); teamToken
}),
await Promise.all( authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: collectionIdList })
collectionIdList.map(async (collectionId) => { ]);
await authCollectionInChat({ appId, chatId, chatItemId, collectionId }); if (!chat) return Promise.reject(ChatErrEnum.unAuthChat);
})
);
const list = await MongoDatasetData.find( const list = await MongoDatasetData.find(
{ _id: { $in: datasetDataIdList } }, { _id: { $in: datasetDataIdList }, collectionId: { $in: collectionIdList } },
dataFieldSelector quoteDataFieldSelector
).lean(); ).lean();
const quoteList = processChatTimeFilter(list, chatTime); const quoteList = processChatTimeFilter(list, chatItem.time);
return { return quoteList;
quoteList
};
} }
export default NextAPI(handler); export default NextAPI(handler);
export function processChatTimeFilter(list: DatasetDataSchemaType[], chatTime: Date) {
return list.map((item) => {
if (!item.history) return item;
const { history, ...rest } = item;
const formatedChatTime = new Date(chatTime);
if (item.updateTime <= formatedChatTime) {
return rest;
}
const latestHistoryIndex = history.findIndex(
(historyItem: any) => historyItem.updateTime <= formatedChatTime
);
if (latestHistoryIndex === -1) return rest;
const latestHistory = history[latestHistoryIndex];
return {
...rest,
q: latestHistory?.q || item.q,
a: latestHistory?.a || item.a,
updated: true
};
});
}

View File

@ -2,11 +2,7 @@ import type { NextApiRequest } from 'next';
import type { ApiDatasetCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d'; import type { ApiDatasetCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth'; import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { createCollectionAndInsertData } from '@fastgpt/service/core/dataset/collection/controller'; import { createCollectionAndInsertData } from '@fastgpt/service/core/dataset/collection/controller';
import { import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
TrainingModeEnum,
DatasetCollectionTypeEnum,
DatasetCollectionDataProcessModeEnum
} from '@fastgpt/global/core/dataset/constants';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
@ -16,7 +12,8 @@ import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
async function handler(req: NextApiRequest): CreateCollectionResponse { async function handler(req: NextApiRequest): CreateCollectionResponse {
const { name, apiFileId, ...body } = req.body as ApiDatasetCreateDatasetCollectionParams; const { name, apiFileId, customPdfParse, ...body } =
req.body as ApiDatasetCreateDatasetCollectionParams;
const { teamId, tmbId, dataset } = await authDataset({ const { teamId, tmbId, dataset } = await authDataset({
req, req,
@ -50,7 +47,8 @@ async function handler(req: NextApiRequest): CreateCollectionResponse {
yuqueServer, yuqueServer,
apiFileId, apiFileId,
teamId, teamId,
tmbId tmbId,
customPdfParse
}); });
const { collectionId, insertResults } = await createCollectionAndInsertData({ const { collectionId, insertResults } = await createCollectionAndInsertData({
@ -62,11 +60,12 @@ async function handler(req: NextApiRequest): CreateCollectionResponse {
teamId, teamId,
tmbId, tmbId,
type: DatasetCollectionTypeEnum.apiFile, type: DatasetCollectionTypeEnum.apiFile,
name: name, name,
apiFileId, apiFileId,
metadata: { metadata: {
relatedImgId: apiFileId relatedImgId: apiFileId
} },
customPdfParse
} }
}); });

View File

@ -1,9 +1,13 @@
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { useIPFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit'; import { useIPFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { responseWriteController } from '@fastgpt/service/common/response'; import { responseWriteController } from '@fastgpt/service/common/response';
import { addLog } from '@fastgpt/service/common/system/log'; import { addLog } from '@fastgpt/service/common/system/log';
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth'; import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
@ -11,21 +15,69 @@ import { NextApiResponse } from 'next';
export type ExportCollectionBody = { export type ExportCollectionBody = {
collectionId: string; collectionId: string;
appId?: string;
chatId?: string;
chatItemDataId?: string;
chatTime: Date; chatTime: Date;
}; } & OutLinkChatAuthProps;
async function handler(req: ApiRequestProps<ExportCollectionBody, {}>, res: NextApiResponse) { async function handler(req: ApiRequestProps<ExportCollectionBody, {}>, res: NextApiResponse) {
let { collectionId, chatTime } = req.body; const {
const { teamId, collection } = await authDatasetCollection({
req,
authToken: true,
collectionId, collectionId,
per: ReadPermissionVal appId,
}); chatId,
chatItemDataId,
shareId,
outLinkUid,
teamId,
teamToken,
chatTime
} = req.body;
const { collection, teamId: userTeamId } = await (async () => {
if (!appId || !chatId || !chatItemDataId) {
return authDatasetCollection({
req,
authToken: true,
authApiKey: true,
collectionId: req.body.collectionId,
per: ReadPermissionVal
});
}
/*
1. auth chat read permission
2. auth collection quote in chat
3. auth outlink open show quote
*/
const [authRes, collection] = await Promise.all([
authChatCrud({
req,
authToken: true,
appId,
chatId,
shareId,
outLinkUid,
teamId,
teamToken
}),
getCollectionWithDataset(collectionId),
authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
]);
if (!authRes.showRawSource) {
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
}
return {
...authRes,
collection
};
})();
const where = { const where = {
teamId, teamId: userTeamId,
datasetId: collection.datasetId, datasetId: collection.datasetId,
collectionId, collectionId,
...(chatTime ...(chatTime

View File

@ -19,7 +19,7 @@ export type readCollectionSourceBody = {
appId?: string; appId?: string;
chatId?: string; chatId?: string;
chatItemId?: string; chatItemDataId?: string;
} & OutLinkChatAuthProps; } & OutLinkChatAuthProps;
export type readCollectionSourceResponse = { export type readCollectionSourceResponse = {
@ -30,7 +30,7 @@ export type readCollectionSourceResponse = {
async function handler( async function handler(
req: ApiRequestProps<readCollectionSourceBody, readCollectionSourceQuery> req: ApiRequestProps<readCollectionSourceBody, readCollectionSourceQuery>
): Promise<readCollectionSourceResponse> { ): Promise<readCollectionSourceResponse> {
const { collectionId, appId, chatId, chatItemId, shareId, outLinkUid, teamId, teamToken } = const { collectionId, appId, chatId, chatItemDataId, shareId, outLinkUid, teamId, teamToken } =
req.body; req.body;
const { const {
@ -39,7 +39,7 @@ async function handler(
tmbId: uid, tmbId: uid,
authType authType
} = await (async () => { } = await (async () => {
if (!appId || !chatId || !chatItemId) { if (!appId || !chatId || !chatItemDataId) {
return authDatasetCollection({ return authDatasetCollection({
req, req,
authToken: true, authToken: true,
@ -66,7 +66,7 @@ async function handler(
teamToken teamToken
}), }),
getCollectionWithDataset(collectionId), getCollectionWithDataset(collectionId),
authCollectionInChat({ appId, chatId, chatItemId, collectionId }) authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
]); ]);
if (!authRes.showRawSource) { if (!authRes.showRawSource) {

View File

@ -231,7 +231,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
{quoteData && ( {quoteData && (
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}> <PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -304,7 +304,6 @@ const OutLink = (props: Props) => {
{quoteData && ( {quoteData && (
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}> <PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -245,7 +245,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
{quoteData && ( {quoteData && (
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}> <PageContainer flex={'1 0 0'} w={0} maxW={'560px'}>
<ChatQuoteList <ChatQuoteList
chatTime={quoteData.chatTime}
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}
onClose={() => setQuoteData(undefined)} onClose={() => setQuoteData(undefined)}

View File

@ -0,0 +1,13 @@
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type';
export const quoteDataFieldSelector = '_id q a history updateTime chunkIndex';
export type QuoteDataItemType = {
_id: string;
q: DatasetDataSchemaType['q'];
a: DatasetDataSchemaType['a'];
history?: DatasetDataSchemaType['history'];
updateTime: DatasetDataSchemaType['updateTime'];
index: DatasetDataSchemaType['chunkIndex'];
updated?: boolean;
};

View File

@ -0,0 +1,46 @@
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type';
import { QuoteDataItemType } from './constants';
// 获取对话时间时,引用的内容
export function processChatTimeFilter(
dataList: DatasetDataSchemaType[],
chatTime: Date
): QuoteDataItemType[] {
return dataList.map((item) => {
const defaultItem = {
_id: item._id,
q: item.q,
a: item.a,
updateTime: item.updateTime,
index: item.chunkIndex
};
if (!item.history) return defaultItem;
const history = item.history;
const formatedChatTime = new Date(chatTime);
if (item.updateTime <= formatedChatTime) {
return defaultItem;
}
const latestHistoryIndex = history.findIndex(
(historyItem) => historyItem.updateTime <= formatedChatTime
);
if (latestHistoryIndex === -1) {
return defaultItem;
}
const latestHistory = history[latestHistoryIndex];
return {
_id: item._id,
q: latestHistory.q,
a: latestHistory.a,
updateTime: latestHistory.updateTime,
index: item.chunkIndex,
updated: true
};
});
}

View File

@ -25,6 +25,14 @@ const defaultResponseShow = {
showNodeStatus: true, showNodeStatus: true,
showRawSource: true showRawSource: true
}; };
type AuthChatCommonProps = {
appId: string;
shareId?: string;
outLinkUid?: string;
teamId?: string;
teamToken?: string;
};
export async function authChatCrud({ export async function authChatCrud({
appId, appId,
chatId, chatId,
@ -35,14 +43,10 @@ export async function authChatCrud({
teamId: spaceTeamId, teamId: spaceTeamId,
teamToken, teamToken,
...props ...props
}: AuthModeType & { }: AuthModeType &
appId: string; AuthChatCommonProps & {
chatId?: string; chatId?: string;
shareId?: string; }): Promise<{
outLinkUid?: string;
teamId?: string;
teamToken?: string;
}): Promise<{
teamId: string; teamId: string;
tmbId: string; tmbId: string;
uid: string; uid: string;
@ -188,27 +192,29 @@ export async function authChatCrud({
} }
export const authCollectionInChat = async ({ export const authCollectionInChat = async ({
collectionId, collectionIds,
appId, appId,
chatId, chatId,
chatItemId chatItemDataId
}: { }: {
collectionId: string; collectionIds: string[];
appId: string; appId: string;
chatId: string; chatId: string;
chatItemId: string; chatItemDataId: string;
}) => { }): Promise<{
chatItem: { time: Date; responseData?: ChatHistoryItemResType[] };
}> => {
try { try {
const chatItem = (await MongoChatItem.findOne( const chatItem = (await MongoChatItem.findOne(
{ {
appId, appId,
chatId, chatId,
dataId: chatItemId dataId: chatItemDataId
}, },
'responseData' 'responseData time'
).lean()) as AIChatItemType; ).lean()) as { time: Date; responseData?: ChatHistoryItemResType[] };
if (!chatItem) return Promise.reject(DatasetErrEnum.unAuthDatasetFile); if (!chatItem) return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
// 找 responseData 里,是否有该文档 id // 找 responseData 里,是否有该文档 id
const responseData = chatItem.responseData || []; const responseData = chatItem.responseData || [];
@ -224,15 +230,16 @@ export const authCollectionInChat = async ({
}) })
.flat() || []; .flat() || [];
if ( const quoteListSet = new Set(
flatResData.some((item) => { flatResData
if (item.quoteList) { .map((item) => item.quoteList?.map((quote) => String(quote.collectionId)) || [])
return item.quoteList.some((quote) => quote.collectionId === collectionId); .flat()
} );
return false;
}) if (collectionIds.every((id) => quoteListSet.has(id))) {
) { return {
return true; chatItem
};
} }
} catch (error) {} } catch (error) {}
return Promise.reject(DatasetErrEnum.unAuthDatasetFile); return Promise.reject(DatasetErrEnum.unAuthDatasetFile);

View File

@ -1,11 +1,6 @@
import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
import type { import type { ChatHistoryItemType, ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
ChatHistoryItemType, import type { getResDataQuery } from '@/pages/api/core/chat/getResData';
ChatHistoryItemResType,
ChatSiteItemType,
ChatItemType
} from '@fastgpt/global/core/chat/type.d';
import { getResDataQuery } from '@/pages/api/core/chat/getResData';
import type { import type {
CloseCustomFeedbackParams, CloseCustomFeedbackParams,
InitChatProps, InitChatProps,
@ -22,16 +17,16 @@ import type {
DeleteChatItemProps, DeleteChatItemProps,
UpdateHistoryProps UpdateHistoryProps
} from '@/global/core/chat/api.d'; } from '@/global/core/chat/api.d';
import { UpdateChatFeedbackProps } from '@fastgpt/global/core/chat/api'; import type { UpdateChatFeedbackProps } from '@fastgpt/global/core/chat/api';
import { AuthTeamTagTokenProps } from '@fastgpt/global/support/user/team/tag'; import type { AuthTeamTagTokenProps } from '@fastgpt/global/support/user/team/tag';
import { AppListItemType } from '@fastgpt/global/core/app/type'; import type { AppListItemType } from '@fastgpt/global/core/app/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type { import type {
getPaginationRecordsBody, getPaginationRecordsBody,
getPaginationRecordsResponse getPaginationRecordsResponse
} from '@/pages/api/core/chat/getPaginationRecords'; } from '@/pages/api/core/chat/getPaginationRecords';
import { GetQuoteDataProps, GetQuoteDataRes } from '@/pages/api/core/chat/quote/getQuote'; import type { GetQuoteDataProps, GetQuoteDataRes } from '@/pages/api/core/chat/quote/getQuote';
import { import type {
GetCollectionQuoteProps, GetCollectionQuoteProps,
GetCollectionQuoteRes GetCollectionQuoteRes
} from '@/pages/api/core/chat/quote/getCollectionQuote'; } from '@/pages/api/core/chat/quote/getCollectionQuote';

View File

@ -9,6 +9,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
type ContextProps = { type ContextProps = {
showRouteToAppDetail: boolean; showRouteToAppDetail: boolean;
@ -32,19 +33,27 @@ type ChatBoxDataType = {
}; };
}; };
export type metadataType = { export type GetQuoteDataBasicProps = {
appId: string;
chatId: string;
chatItemDataId: string;
outLinkAuthData?: OutLinkChatAuthProps;
};
// 获取单个集合引用
export type GetCollectionQuoteDataProps = GetQuoteDataBasicProps & {
collectionId: string; collectionId: string;
collectionIdList: string[];
chatItemId: string;
sourceId: string; sourceId: string;
sourceName: string; sourceName: string;
datasetId: string; datasetId: string;
}; };
export type GetAllQuoteDataProps = GetQuoteDataBasicProps & {
collectionIdList: string[];
};
export type GetQuoteProps = GetAllQuoteDataProps | GetCollectionQuoteDataProps;
export type QuoteDataType = { export type QuoteDataType = {
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
metadata: metadataType; metadata: GetQuoteProps;
chatTime: Date;
}; };
type ChatItemContextType = { type ChatItemContextType = {