V4.9.1 feature (#4206)
* fix: remove DefaultTeam (#4037) * fix :Get application bound knowledge base information logical rewrite (#4057) * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * update package * fix: import dataset step error;perf: ai proxy avatar (#4074) * perf: pg config params * perf: ai proxy avatar * fix: import dataset step error * feat: data input ux * perf: app dataset rewite * fix: 文本提取不支持arrayString,arrayNumber等jsonSchema (#4079) * update doc ;perf: model test (#4098) * perf: extract array * update doc * perf: model test * perf: model test * perf: think tag parse (#4102) * chat quote reader (#3912) * init chat quote full text reader * linked structure * dataset data linked * optimize code * fix ts build * test finish * delete log * fix * fix ts * fix ts * remove nextId * initial scroll * fix * fix * perf: chunk read (#4109) * package * perf: chunk read * feat: api dataset support pdf parse;fix: chunk reader auth (#4117) * feat: api dataset support pdf parse * fix: chunk reader auth * feat: invitation link (#3979) * feat: invitation link schema and apis * feat: add invitation link * feat: member status: active, leave, forbidden * fix: expires show hours and minutes * feat: invalid invitation link hint * fix: typo * chore: fix typo & i18n * fix * pref: fe * feat: add ttl index for 30-day-clean-up * perf: invite member code (#4118) * perf: invite member code * fix: ts * fix: model test channel id;fix: quote reader (#4123) * fix: model test channel id * fix: quote reader * fix chat quote reader (#4125) * perf: model test;perf: sidebar trigger (#4127) * fix: import dataset step error;perf: ai proxy avatar (#4074) * perf: pg config params * perf: ai proxy avatar * fix: import dataset step error * feat: data input ux * perf: app dataset rewite * perf: model test * perf: sidebar trigger * lock * update nanoid version * fix: select component ux * fix: ts * fix: vitest * remove test * fix: prompt toolcall ui (#4139) * load log error adapt * fix: prompt toolcall ui * perf: commercial function tip * update package * pref: copy link (#4147) * fix(i18n): namespace (#4143) * hiden dataset source (#4152) * hiden dataset source * perf: reader * chore: move all tests into a single folder (#4160) * fix modal close scroll (#4162) * fix modal close scroll * update refresh * feat: rerank modal select and weight (#4164) * fix loadInitData refresh (#4169) * fix * fix * form input number default & api dataset max token * feat: mix search weight (#4170) * feat: mix search weight * feat: svg render * fix: avatar error remove (#4173) * fix: avatar error remove * fix: index * fix: guide * fix: auth * update package;fix: input data model ui (#4181) * update package * fix: ts * update config * update jieba package * add type sign * fix: input data ui * fix: page title refresh (#4186) * fix: ts * update jieba package * fix: page title refresh * fix: remove member length check when opening invite create modal (#4193) * add env to check internal ip (#4187) * fix: ts * update jieba package * add env to check internal ip * package * fix: jieba * reset package * update config * fix: jieba package * init shell * init version * change team reload * update jieba package (#4200) * update jieba package * package * update package * remove invalid code * action * package (#4201) * package * update package * remove invalid code * package * remove i18n tip (#4202) * doc (#4205) * fix: i18n (#4208) * fix: next config (#4207) * reset package * i18n * update config * i18n * remove log --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com> Co-authored-by: shilin <39396378+shilin66@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -18,7 +18,6 @@ import WorkorderButton from './WorkorderButton';
|
||||
|
||||
const Navbar = dynamic(() => import('./navbar'));
|
||||
const NavbarPhone = dynamic(() => import('./navbarPhone'));
|
||||
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
|
||||
const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'));
|
||||
const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal'));
|
||||
const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform'));
|
||||
@@ -151,7 +150,6 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
</Box>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
{!!userInfo && <UpdateInviteModal />}
|
||||
{notSufficientModalType && <NotSufficientModal type={notSufficientModalType} />}
|
||||
{!!userInfo && <SystemMsgModal />}
|
||||
{showUpdateNotification && (
|
||||
|
||||
@@ -85,7 +85,7 @@ export default React.memo(Markdown);
|
||||
function Code(e: any) {
|
||||
const { className, codeBlock, children } = e;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const codeType = match?.[1];
|
||||
const codeType = match?.[1]?.toLowerCase();
|
||||
|
||||
const strChildren = String(children);
|
||||
|
||||
@@ -96,7 +96,7 @@ function Code(e: any) {
|
||||
if (codeType === CodeClassNameEnum.guide) {
|
||||
return <ChatGuide text={strChildren} />;
|
||||
}
|
||||
if (codeType === CodeClassNameEnum.questionGuide) {
|
||||
if (codeType === CodeClassNameEnum.questionguide) {
|
||||
return <QuestionGuide text={strChildren} />;
|
||||
}
|
||||
if (codeType === CodeClassNameEnum.echarts) {
|
||||
@@ -105,7 +105,7 @@ function Code(e: any) {
|
||||
if (codeType === CodeClassNameEnum.iframe) {
|
||||
return <IframeCodeBlock code={strChildren} />;
|
||||
}
|
||||
if (codeType && codeType.toLowerCase() === CodeClassNameEnum.html) {
|
||||
if (codeType === CodeClassNameEnum.html || codeType === CodeClassNameEnum.svg) {
|
||||
return (
|
||||
<IframeHtmlCodeBlock className={className} codeBlock={codeBlock} match={match}>
|
||||
{children}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export enum CodeClassNameEnum {
|
||||
guide = 'guide',
|
||||
questionGuide = 'questionGuide',
|
||||
questionguide = 'questionguide',
|
||||
mermaid = 'mermaid',
|
||||
echarts = 'echarts',
|
||||
quote = 'quote',
|
||||
@@ -8,6 +8,7 @@ export enum CodeClassNameEnum {
|
||||
latex = 'latex',
|
||||
iframe = 'iframe',
|
||||
html = 'html',
|
||||
svg = 'svg',
|
||||
video = 'video',
|
||||
audio = 'audio'
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type Props = SelectProps & {
|
||||
disableTip?: string;
|
||||
};
|
||||
|
||||
const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
|
||||
const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
|
||||
useSystemStore();
|
||||
@@ -96,12 +96,12 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
|
||||
placeholder={t('common:not_model_config')}
|
||||
h={'40px'}
|
||||
{...props}
|
||||
onchange={(e) => {
|
||||
onChange={(e) => {
|
||||
if (e === 'price') {
|
||||
onOpen();
|
||||
return;
|
||||
}
|
||||
return onchange?.(e);
|
||||
return onChange?.(e);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -110,7 +110,7 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const MultipleRowSelector = ({ list, onchange, disableTip, placeholder, ...props }: Props) => {
|
||||
const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
|
||||
useSystemStore();
|
||||
@@ -178,9 +178,9 @@ const MultipleRowSelector = ({ list, onchange, disableTip, placeholder, ...props
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: string[]) => {
|
||||
return onchange?.(e[1]);
|
||||
return onChange?.(e[1]);
|
||||
},
|
||||
[onchange]
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const SelectedModel = useMemo(() => {
|
||||
|
||||
@@ -26,7 +26,7 @@ const I18nLngSelector = () => {
|
||||
<MySelect
|
||||
value={i18n.language}
|
||||
list={list}
|
||||
onchange={(val: any) => {
|
||||
onChange={(val: any) => {
|
||||
const lang = val;
|
||||
onChangeLng(lang);
|
||||
}}
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
interface Props extends BoxProps {}
|
||||
interface Props extends BoxProps {
|
||||
externalTrigger?: Boolean;
|
||||
}
|
||||
|
||||
const SideBar = (e?: Props) => {
|
||||
const {
|
||||
w = ['100%', '0 0 250px', '0 0 270px', '0 0 290px', '0 0 310px'],
|
||||
w = ['100%', '0 0 250px', '0 0 250px', '0 0 270px', '0 0 290px'],
|
||||
children,
|
||||
externalTrigger,
|
||||
...props
|
||||
} = e || {};
|
||||
|
||||
const [foldSideBar, setFoldSideBar] = useState(false);
|
||||
const [isFolded, setIsFolded] = useState(false);
|
||||
|
||||
// 保存上一次折叠状态
|
||||
const preFoledStatus = useRef<Boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (externalTrigger) {
|
||||
setIsFolded(true);
|
||||
preFoledStatus.current = isFolded;
|
||||
} else {
|
||||
// @ts-ignore
|
||||
setIsFolded(preFoledStatus.current);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [externalTrigger]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
position={'relative'}
|
||||
flex={foldSideBar ? '0 0 0' : w}
|
||||
flex={isFolded ? '0 0 0' : w}
|
||||
w={['100%', 0]}
|
||||
h={'100%'}
|
||||
zIndex={1}
|
||||
@@ -40,7 +58,7 @@ const SideBar = (e?: Props) => {
|
||||
bg={'rgba(0,0,0,0.5)'}
|
||||
cursor={'pointer'}
|
||||
transition={'0.2s'}
|
||||
{...(foldSideBar
|
||||
{...(isFolded
|
||||
? {
|
||||
opacity: 0.6
|
||||
}
|
||||
@@ -48,16 +66,16 @@ const SideBar = (e?: Props) => {
|
||||
visibility: 'hidden',
|
||||
opacity: 0
|
||||
})}
|
||||
onClick={() => setFoldSideBar(!foldSideBar)}
|
||||
onClick={() => setIsFolded(!isFolded)}
|
||||
>
|
||||
<MyIcon
|
||||
name={'common/backLight'}
|
||||
transform={foldSideBar ? 'rotate(180deg)' : ''}
|
||||
transform={isFolded ? 'rotate(180deg)' : ''}
|
||||
w={'14px'}
|
||||
color={'white'}
|
||||
/>
|
||||
</Flex>
|
||||
<Box position={'relative'} h={'100%'} overflow={foldSideBar ? 'hidden' : 'visible'}>
|
||||
<Box position={'relative'} h={'100%'} overflow={isFolded ? 'hidden' : 'visible'}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import Head from 'next/head';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?: string }) => {
|
||||
const formatIcon = useMemo(() => {
|
||||
@@ -11,13 +11,6 @@ const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?:
|
||||
return LOGO_ICON;
|
||||
}, [icon]);
|
||||
|
||||
useEffect(() => {
|
||||
// Force update document title
|
||||
if (title) {
|
||||
document.title = title;
|
||||
}
|
||||
}, [title]);
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
|
||||
@@ -154,7 +154,7 @@ const AIChatSettingsModal = ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={onChangeModel}
|
||||
onChange={onChangeModel}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
@@ -385,7 +385,7 @@ const AIChatSettingsModal = ({
|
||||
label: item
|
||||
}))}
|
||||
value={responseFormat}
|
||||
onchange={(e) => {
|
||||
onChange={(e) => {
|
||||
setValue(NodeInputKeyEnum.aiChatResponseFormat, e);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -212,7 +212,7 @@ const ModelTable = () => {
|
||||
w={'200px'}
|
||||
bg={'myGray.50'}
|
||||
value={provider}
|
||||
onchange={setProvider}
|
||||
onChange={setProvider}
|
||||
list={filterProviderList}
|
||||
/>
|
||||
</HStack>
|
||||
@@ -224,7 +224,7 @@ const ModelTable = () => {
|
||||
w={'150px'}
|
||||
bg={'myGray.50'}
|
||||
value={modelType}
|
||||
onchange={setModelType}
|
||||
onChange={setModelType}
|
||||
list={selectModelTypeList.current}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
@@ -77,7 +77,7 @@ const SettingLLMModel = ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={(e) => {
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...defaultData,
|
||||
model: e
|
||||
|
||||
@@ -2,13 +2,15 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Flex,
|
||||
HStack,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Switch,
|
||||
useTheme
|
||||
Slider,
|
||||
SliderTrack,
|
||||
SliderFilledTrack,
|
||||
SliderThumb
|
||||
} from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
@@ -17,30 +19,18 @@ import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import SelectAiModel from '@/components/Select/AIModelSelector';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
import { defaultDatasetMaxTokens } from '@fastgpt/global/core/app/constants';
|
||||
import InputSlider from '@fastgpt/web/components/common/MySlider/InputSlider';
|
||||
import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio';
|
||||
import { AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
export type DatasetParamsProps = {
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
limit?: number;
|
||||
similarity?: number;
|
||||
usingReRank?: boolean;
|
||||
datasetSearchUsingExtensionQuery?: boolean;
|
||||
datasetSearchExtensionModel?: string;
|
||||
datasetSearchExtensionBg?: string;
|
||||
|
||||
maxTokens?: number; // limit max tokens
|
||||
};
|
||||
enum SearchSettingTabEnum {
|
||||
searchMode = 'searchMode',
|
||||
limit = 'limit',
|
||||
@@ -51,17 +41,22 @@ const DatasetParamsModal = ({
|
||||
searchMode = DatasetSearchModeEnum.embedding,
|
||||
limit,
|
||||
similarity,
|
||||
embeddingWeight,
|
||||
usingReRank,
|
||||
maxTokens = defaultDatasetMaxTokens,
|
||||
rerankModel,
|
||||
rerankWeight,
|
||||
datasetSearchUsingExtensionQuery,
|
||||
datasetSearchExtensionModel,
|
||||
datasetSearchExtensionBg,
|
||||
maxTokens = defaultDatasetMaxTokens,
|
||||
onClose,
|
||||
onSuccess
|
||||
}: DatasetParamsProps & { onClose: () => void; onSuccess: (e: DatasetParamsProps) => void }) => {
|
||||
}: AppDatasetSearchParamsType & {
|
||||
maxTokens?: number; // limit max tokens
|
||||
onClose: () => void;
|
||||
onSuccess: (e: AppDatasetSearchParamsType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { teamPlanStatus } = useUserStore();
|
||||
const { reRankModelList, llmModelList, defaultModels } = useSystemStore();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
@@ -72,28 +67,41 @@ const DatasetParamsModal = ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
})))();
|
||||
const reRankModelSelectList = (() =>
|
||||
reRankModelList.map((item) => ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
})))();
|
||||
|
||||
const { register, setValue, getValues, handleSubmit, watch } =
|
||||
useForm<AppDatasetSearchParamsType>({
|
||||
defaultValues: {
|
||||
searchMode,
|
||||
embeddingWeight: embeddingWeight || 0.5,
|
||||
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
|
||||
rerankModel: rerankModel || defaultModels?.rerank?.model,
|
||||
rerankWeight: rerankWeight || 0.5,
|
||||
limit,
|
||||
similarity,
|
||||
datasetSearchUsingExtensionQuery,
|
||||
datasetSearchExtensionModel: datasetSearchExtensionModel || defaultModels.llm?.model,
|
||||
datasetSearchExtensionBg
|
||||
}
|
||||
});
|
||||
|
||||
const searchModeWatch = watch('searchMode');
|
||||
const embeddingWeightWatch = watch('embeddingWeight');
|
||||
const fullTextWeightWatch = useMemo(() => {
|
||||
const val = 1 - (embeddingWeightWatch || 0.5);
|
||||
return Number(val.toFixed(2));
|
||||
}, [embeddingWeightWatch]);
|
||||
|
||||
const { register, setValue, getValues, handleSubmit, watch } = useForm<DatasetParamsProps>({
|
||||
defaultValues: {
|
||||
limit,
|
||||
similarity,
|
||||
searchMode,
|
||||
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
|
||||
datasetSearchUsingExtensionQuery,
|
||||
datasetSearchExtensionModel: datasetSearchExtensionModel || defaultModels.llm?.model,
|
||||
datasetSearchExtensionBg
|
||||
}
|
||||
});
|
||||
const datasetSearchUsingCfrForm = watch('datasetSearchUsingExtensionQuery');
|
||||
const queryExtensionModel = watch('datasetSearchExtensionModel');
|
||||
const cfbBgDesc = watch('datasetSearchExtensionBg');
|
||||
const usingReRankWatch = watch('usingReRank');
|
||||
const searchModeWatch = watch('searchMode');
|
||||
|
||||
const searchModeList = useMemo(() => {
|
||||
const list = Object.values(DatasetSearchModeMap);
|
||||
return list;
|
||||
}, []);
|
||||
const usingReRankWatch = watch('usingReRank');
|
||||
const reRankModelWatch = watch('rerankModel');
|
||||
const rerankWeightWatch = watch('rerankWeight');
|
||||
|
||||
const showSimilarity = useMemo(() => {
|
||||
if (similarity === undefined) return false;
|
||||
@@ -134,93 +142,160 @@ const DatasetParamsModal = ({
|
||||
title={t('common:core.dataset.search.Dataset Search Params')}
|
||||
w={['90vw', '550px']}
|
||||
>
|
||||
<ModalBody flex={'auto'} overflow={'auto'}>
|
||||
<ModalBody flex={'auto'} overflow={'auto'} px={[4, 10]}>
|
||||
<LightRowTabs<SearchSettingTabEnum>
|
||||
width={'100%'}
|
||||
mb={3}
|
||||
list={[
|
||||
{
|
||||
icon: 'modal/setting',
|
||||
icon: 'common/setting',
|
||||
label: t('common:core.dataset.search.search mode'),
|
||||
value: SearchSettingTabEnum.searchMode
|
||||
},
|
||||
{
|
||||
icon: 'support/outlink/apikeyFill',
|
||||
icon: 'core/dataset/searchfilter',
|
||||
label: t('common:core.dataset.search.Filter'),
|
||||
value: SearchSettingTabEnum.limit
|
||||
},
|
||||
{
|
||||
label: t('common:core.module.template.Query extension'),
|
||||
value: SearchSettingTabEnum.queryExtension,
|
||||
icon: '/imgs/workflow/cfr.svg'
|
||||
icon: 'core/dataset/questionExtension'
|
||||
}
|
||||
]}
|
||||
inlineStyles={{
|
||||
borderBottomColor: 'myGray.200',
|
||||
borderBottom: '1px solid'
|
||||
}}
|
||||
value={currentTabType}
|
||||
onChange={setCurrentTabType}
|
||||
/>
|
||||
{currentTabType === SearchSettingTabEnum.searchMode && (
|
||||
<>
|
||||
<MyRadio
|
||||
gridGap={2}
|
||||
gridTemplateColumns={'repeat(1,1fr)'}
|
||||
list={searchModeList}
|
||||
value={getValues('searchMode')}
|
||||
<Box mt={3}>
|
||||
<LeftRadio<`${DatasetSearchModeEnum}`>
|
||||
py={2.5}
|
||||
gridGap={4}
|
||||
list={[
|
||||
{
|
||||
title: t('common:core.dataset.search.mode.embedding'),
|
||||
desc: t('common:core.dataset.search.mode.embedding desc'),
|
||||
value: DatasetSearchModeEnum.embedding
|
||||
},
|
||||
{
|
||||
title: t('common:core.dataset.search.mode.fullTextRecall'),
|
||||
desc: t('common:core.dataset.search.mode.fullTextRecall desc'),
|
||||
value: DatasetSearchModeEnum.fullTextRecall
|
||||
},
|
||||
{
|
||||
title: t('common:core.dataset.search.mode.mixedRecall'),
|
||||
desc: t('common:core.dataset.search.mode.mixedRecall desc'),
|
||||
value: DatasetSearchModeEnum.mixedRecall,
|
||||
children: searchModeWatch === DatasetSearchModeEnum.mixedRecall && (
|
||||
<Box mt={3}>
|
||||
<HStack justifyContent={'space-between'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontSize={'sm'} color={'myGray.900'}>
|
||||
{t('common:core.dataset.search.mode.embedding')}
|
||||
</Box>
|
||||
<Box fontSize={'xs'} color={'myGray.500'}>
|
||||
{embeddingWeightWatch}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontSize={'sm'} color={'myGray.900'}>
|
||||
{t('common:core.dataset.search.score.fullText')}
|
||||
</Box>
|
||||
<Box fontSize={'xs'} color={'myGray.500'}>
|
||||
{fullTextWeightWatch}
|
||||
</Box>
|
||||
</Flex>
|
||||
</HStack>
|
||||
<Slider
|
||||
defaultValue={embeddingWeightWatch}
|
||||
min={0.1}
|
||||
max={0.9}
|
||||
step={0.01}
|
||||
onChange={(e) => {
|
||||
setValue('embeddingWeight', Number(e.toFixed(2)));
|
||||
}}
|
||||
>
|
||||
<SliderTrack bg={'#F9518E'}>
|
||||
<SliderFilledTrack bg={'#3370FF'} />
|
||||
</SliderTrack>
|
||||
<SliderThumb boxShadow={'none'} bg={'none'}>
|
||||
<MyIcon transform={'translateY(10px)'} name={'sliderTag'} w={'1rem'} />
|
||||
</SliderThumb>
|
||||
</Slider>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
]}
|
||||
value={searchModeWatch}
|
||||
onChange={(e) => {
|
||||
setValue('searchMode', e as `${DatasetSearchModeEnum}`);
|
||||
setRefresh(!refresh);
|
||||
setValue('searchMode', e);
|
||||
}}
|
||||
/>
|
||||
{/* Rerank */}
|
||||
<>
|
||||
<Divider my={4} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
py={3}
|
||||
px={4}
|
||||
border={theme.borders.sm}
|
||||
borderWidth={'1.5px'}
|
||||
borderRadius={'md'}
|
||||
position={'relative'}
|
||||
{...(getValues('usingReRank')
|
||||
? {
|
||||
borderColor: 'primary.400'
|
||||
}
|
||||
: {})}
|
||||
onClick={(e) => {
|
||||
if (!showReRank) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:core.ai.Not deploy rerank model')
|
||||
});
|
||||
}
|
||||
if (
|
||||
teamPlanStatus?.standardConstants &&
|
||||
!teamPlanStatus?.standardConstants?.permissionReRank
|
||||
) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.team.limit.No permission rerank')
|
||||
});
|
||||
}
|
||||
setValue('usingReRank', !getValues('usingReRank'));
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
>
|
||||
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
|
||||
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
|
||||
<Box fontSize={'sm'}>{t('common:core.dataset.search.ReRank')}</Box>
|
||||
<Box fontSize={'xs'} color={'myGray.500'}>
|
||||
{t('common:core.dataset.search.ReRank desc')}
|
||||
<HStack mt={6} justifyContent={'space-between'}>
|
||||
<FormLabel>
|
||||
{t('common:core.dataset.search.ReRank')}
|
||||
<QuestionTip ml={0.5} label={t('common:core.dataset.search.ReRank desc')} />
|
||||
</FormLabel>
|
||||
{!showReRank ? (
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{t('common:core.ai.Not deploy rerank model')}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box position={'relative'} w={'18px'} h={'18px'}>
|
||||
<Checkbox colorScheme="primary" isChecked={getValues('usingReRank')} size="lg" />
|
||||
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
) : teamPlanStatus?.standardConstants &&
|
||||
!teamPlanStatus?.standardConstants?.permissionReRank ? (
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{t('common:support.team.limit.No permission rerank')}
|
||||
</Box>
|
||||
) : (
|
||||
<Switch {...register('usingReRank')} />
|
||||
)}
|
||||
</HStack>
|
||||
{usingReRankWatch && (
|
||||
<>
|
||||
<HStack mt={3} justifyContent={'space-between'}>
|
||||
<Box fontSize={'sm'} flex={'0 0 100px'} color={'myGray.700'}>
|
||||
{t('common:rerank_weight')}
|
||||
</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<InputSlider
|
||||
min={0.1}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={rerankWeightWatch}
|
||||
onChange={(val) => {
|
||||
setValue(
|
||||
NodeInputKeyEnum.datasetSearchRerankWeight,
|
||||
Number(val.toFixed(2))
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</HStack>
|
||||
<HStack mt={3}>
|
||||
<Box fontSize={'sm'} flex={'0 0 100px'} color={'myGray.700'}>
|
||||
{t('common:model.type.reRank')}
|
||||
</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<SelectAiModel
|
||||
bg={'myGray.50'}
|
||||
h={'36px'}
|
||||
value={reRankModelWatch}
|
||||
list={reRankModelSelectList}
|
||||
onChange={(val) => {
|
||||
setValue(NodeInputKeyEnum.datasetSearchRerankModel, val);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</HStack>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</>
|
||||
</Box>
|
||||
)}
|
||||
{currentTabType === SearchSettingTabEnum.limit && (
|
||||
<Box pt={5}>
|
||||
@@ -262,7 +337,7 @@ const DatasetParamsModal = ({
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Box color={'myGray.500'}>
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{t('common:core.dataset.search.No support similarity')}
|
||||
</Box>
|
||||
)}
|
||||
@@ -290,7 +365,7 @@ const DatasetParamsModal = ({
|
||||
width={'100%'}
|
||||
value={queryExtensionModel}
|
||||
list={chatModelSelectList}
|
||||
onchange={(val: any) => {
|
||||
onChange={(val: any) => {
|
||||
setValue('datasetSearchExtensionModel', val);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,6 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
@@ -35,29 +34,24 @@ export const DatasetSelectModal = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { allDatasets } = useDatasetStore();
|
||||
const [selectedDatasets, setSelectedDatasets] = useState<SelectedDatasetType>(
|
||||
defaultSelectedDatasets.filter((dataset) => {
|
||||
return allDatasets.find((item) => item._id === dataset.datasetId);
|
||||
})
|
||||
);
|
||||
const [selectedDatasets, setSelectedDatasets] =
|
||||
useState<SelectedDatasetType>(defaultSelectedDatasets);
|
||||
const { toast } = useToast();
|
||||
const { paths, setParentId, datasets, isFetching } = useDatasetSelect();
|
||||
const { Loading } = useLoading();
|
||||
|
||||
const filterDatasets = useMemo(() => {
|
||||
return {
|
||||
selected: allDatasets.filter((item) =>
|
||||
const filtered = {
|
||||
selected: datasets.filter((item) =>
|
||||
selectedDatasets.find((dataset) => dataset.datasetId === item._id)
|
||||
),
|
||||
unSelected: datasets.filter(
|
||||
(item) => !selectedDatasets.find((dataset) => dataset.datasetId === item._id)
|
||||
)
|
||||
};
|
||||
}, [datasets, allDatasets, selectedDatasets]);
|
||||
const activeVectorModel = allDatasets.find(
|
||||
(dataset) => dataset._id === selectedDatasets[0]?.datasetId
|
||||
)?.vectorModel?.model;
|
||||
return filtered;
|
||||
}, [datasets, selectedDatasets]);
|
||||
const activeVectorModel = defaultSelectedDatasets[0]?.vectorModel?.model;
|
||||
|
||||
return (
|
||||
<DatasetSelectContainer
|
||||
@@ -150,7 +144,15 @@ export const DatasetSelectModal = ({
|
||||
title: t('common:dataset.Select Dataset Tips')
|
||||
});
|
||||
}
|
||||
setSelectedDatasets((state) => [...state, { datasetId: item._id }]);
|
||||
setSelectedDatasets((state) => [
|
||||
...state,
|
||||
{
|
||||
datasetId: item._id,
|
||||
avatar: item.avatar,
|
||||
name: item.name,
|
||||
vectorModel: item.vectorModel
|
||||
}
|
||||
]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -200,7 +202,7 @@ export const DatasetSelectModal = ({
|
||||
onClick={() => {
|
||||
// filter out the dataset that is not in the kList
|
||||
const filterDatasets = selectedDatasets.filter((dataset) => {
|
||||
return allDatasets.find((item) => item._id === dataset.datasetId);
|
||||
return datasets.find((item) => item._id === dataset.datasetId);
|
||||
});
|
||||
|
||||
onClose();
|
||||
|
||||
@@ -54,7 +54,6 @@ const InputGuideConfig = ({
|
||||
onChange: (e: ChatInputGuideConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { chatT } = useI18n();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const {
|
||||
isOpen: isOpenLexiconConfig,
|
||||
@@ -87,11 +86,11 @@ const InputGuideConfig = ({
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/inputGuides'} mr={2} w={'20px'} />
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel color={'myGray.600'}>{chatT('input_guide')}</FormLabel>
|
||||
<FormLabel color={'myGray.600'}>{t('chat:input_guide')}</FormLabel>
|
||||
<ChatFunctionTip type={'inputGuide'} />
|
||||
</Flex>
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={chatT('config_input_guide')}>
|
||||
<MyTooltip label={t('chat:config_input_guide')}>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
iconSpacing={1}
|
||||
@@ -104,7 +103,7 @@ const InputGuideConfig = ({
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
<MyModal
|
||||
title={chatT('input_guide')}
|
||||
title={t('chat:input_guide')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
@@ -126,7 +125,7 @@ const InputGuideConfig = ({
|
||||
{isOpenQuestionGuide && (
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
<FormLabel>{chatT('input_guide_lexicon')}</FormLabel>
|
||||
<FormLabel>{t('chat:input_guide_lexicon')}</FormLabel>
|
||||
<Box fontSize={'xs'} px={2} bg={'myGray.100'} ml={1} rounded={'full'}>
|
||||
{total}
|
||||
</Box>
|
||||
@@ -144,7 +143,7 @@ const InputGuideConfig = ({
|
||||
</Flex>
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
<FormLabel>{chatT('custom_input_guide_url')}</FormLabel>
|
||||
<FormLabel>{t('chat:custom_input_guide_url')}</FormLabel>
|
||||
<Flex
|
||||
onClick={() => window.open(getDocPath('/docs/guide/course/chat_input_guide/'))}
|
||||
color={'primary.700'}
|
||||
@@ -181,7 +180,7 @@ const InputGuideConfig = ({
|
||||
export default React.memo(InputGuideConfig);
|
||||
|
||||
const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () => void }) => {
|
||||
const { chatT, commonT } = useI18n();
|
||||
const { commonT } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
@@ -232,7 +231,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
|
||||
if (res.insertLength < textList.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: chatT('insert_input_guide,_some_data_already_exists', { len: res.insertLength })
|
||||
title: t('chat:insert_input_guide,_some_data_already_exists', { len: res.insertLength })
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
@@ -301,7 +300,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
title={chatT('config_input_guide_lexicon_title')}
|
||||
title={t('chat:config_input_guide_lexicon_title')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
@@ -338,7 +337,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
|
||||
});
|
||||
}}
|
||||
>
|
||||
<QuestionTip ml={-2} label={chatT('csv_input_lexicon_tip')} />
|
||||
<QuestionTip ml={-2} label={t('chat:csv_input_lexicon_tip')} />
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box px={8}>
|
||||
@@ -394,7 +393,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
|
||||
<MyInput
|
||||
autoFocus
|
||||
rightIcon={<MyIcon name={'save'} w={'14px'} cursor={'pointer'} />}
|
||||
placeholder={chatT('new_input_guide_lexicon')}
|
||||
placeholder={t('chat:new_input_guide_lexicon')}
|
||||
onBlur={(e) => {
|
||||
createNewData([e.target.value.trim()]);
|
||||
}}
|
||||
@@ -411,7 +410,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
|
||||
px={8}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'sm'}
|
||||
EmptyChildren={<EmptyTip text={chatT('chat_input_guide_lexicon_is_empty')} />}
|
||||
EmptyChildren={<EmptyTip text={t('chat:chat_input_guide_lexicon_is_empty')} />}
|
||||
>
|
||||
{scrollDataList.map((data, index) => {
|
||||
const item = data.data;
|
||||
|
||||
@@ -125,7 +125,7 @@ const QGConfigModal = ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={(e) => {
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...value,
|
||||
model: e
|
||||
|
||||
@@ -22,7 +22,6 @@ export default function InputGuideBox({
|
||||
onSend: (text: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { chatT } = useI18n();
|
||||
const chatInputGuide = useContextSelector(ChatBoxContext, (v) => v.chatInputGuide);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
|
||||
@@ -65,9 +64,9 @@ export default function InputGuideBox({
|
||||
>
|
||||
<Flex alignItems={'center'} fontSize={'sm'} color={'myGray.600'} gap={2} mb={2} px={2}>
|
||||
<MyIcon name={'union'} />
|
||||
<Box>{chatT('input_guide')}</Box>
|
||||
<Box>{t('chat:input_guide')}</Box>
|
||||
</Flex>
|
||||
{data.map((item, index) => (
|
||||
{data.map((item) => (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
as={'li'}
|
||||
|
||||
@@ -59,7 +59,7 @@ type Props = BasicProps & {
|
||||
const RenderQuestionGuide = ({ questionGuides }: { questionGuides: string[] }) => {
|
||||
return (
|
||||
<Markdown
|
||||
source={`\`\`\`${CodeClassNameEnum.questionGuide}
|
||||
source={`\`\`\`${CodeClassNameEnum.questionguide}
|
||||
${JSON.stringify(questionGuides)}`}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, useTheme } from '@chakra-ui/react';
|
||||
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import QuoteItem, { formatScore } from '@/components/core/dataset/QuoteItem';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { getQuoteDataList } from '@/web/core/chat/api';
|
||||
|
||||
const QuoteList = React.memo(function QuoteList({
|
||||
chatItemDataId = '',
|
||||
rawSearch = []
|
||||
}: {
|
||||
chatItemDataId?: string;
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { chatId, appId, outLinkAuthData } = useChatStore();
|
||||
|
||||
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
|
||||
chatItemDataId,
|
||||
appId: v.appId,
|
||||
chatId: v.chatId,
|
||||
...(v.outLinkAuthData || {})
|
||||
}));
|
||||
const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
const showRouteToDatasetDetail = useContextSelector(
|
||||
ChatItemContext,
|
||||
(v) => v.showRouteToDatasetDetail
|
||||
);
|
||||
|
||||
const { data: quoteList } = useRequest2(
|
||||
async () =>
|
||||
await getQuoteDataList({
|
||||
datasetDataIdList: rawSearch.map((item) => item.id),
|
||||
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
|
||||
chatItemDataId,
|
||||
appId,
|
||||
chatId,
|
||||
...outLinkAuthData
|
||||
}),
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
|
||||
const formatedDataList = useMemo(() => {
|
||||
return rawSearch
|
||||
.map((item) => {
|
||||
const currentFilterItem = quoteList?.find((res) => res._id === item.id);
|
||||
|
||||
return {
|
||||
...item,
|
||||
q: currentFilterItem?.q || '',
|
||||
a: currentFilterItem?.a || ''
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aScore = formatScore(a.score);
|
||||
const bScore = formatScore(b.score);
|
||||
return (bScore.primaryScore?.value || 0) - (aScore.primaryScore?.value || 0);
|
||||
});
|
||||
}, [quoteList, rawSearch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{formatedDataList.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
flex={'1 0 0'}
|
||||
p={2}
|
||||
borderRadius={'sm'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
bg={i % 2 === 0 ? 'white' : 'myWhite.500'}
|
||||
>
|
||||
<QuoteItem
|
||||
quoteItem={item}
|
||||
canViewSource={showRawSource}
|
||||
canEditDataset={showRouteToDatasetDetail}
|
||||
{...RawSourceBoxProps}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default QuoteList;
|
||||
@@ -1,129 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import QuoteItem from '@/components/core/dataset/QuoteItem';
|
||||
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
|
||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
|
||||
const QuoteModal = ({
|
||||
rawSearch = [],
|
||||
onClose,
|
||||
chatItemId,
|
||||
metadata
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
onClose: () => void;
|
||||
chatItemId: string;
|
||||
metadata?: {
|
||||
collectionId: string;
|
||||
sourceId?: string;
|
||||
sourceName: string;
|
||||
};
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const filterResults = useMemo(
|
||||
() =>
|
||||
metadata
|
||||
? rawSearch.filter(
|
||||
(item) =>
|
||||
item.collectionId === metadata.collectionId && item.sourceId === metadata.sourceId
|
||||
)
|
||||
: rawSearch,
|
||||
[metadata, rawSearch]
|
||||
);
|
||||
|
||||
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
|
||||
appId: v.appId,
|
||||
chatId: v.chatId,
|
||||
chatItemId,
|
||||
...(v.outLinkAuthData || {})
|
||||
}));
|
||||
const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
const showRouteToDatasetDetail = useContextSelector(
|
||||
ChatItemContext,
|
||||
(v) => v.showRouteToDatasetDetail
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
h={['90vh', '80vh']}
|
||||
isCentered
|
||||
minW={['90vw', '600px']}
|
||||
iconSrc={!!metadata ? undefined : getWebReqUrl('/imgs/modal/quote.svg')}
|
||||
title={
|
||||
<Box>
|
||||
{metadata ? (
|
||||
<RawSourceBox {...metadata} {...RawSourceBoxProps} canView={showRawSource} />
|
||||
) : (
|
||||
<>{t('common:core.chat.Quote Amount', { amount: rawSearch.length })}</>
|
||||
)}
|
||||
<Box fontSize={'xs'} color={'myGray.500'} fontWeight={'normal'}>
|
||||
{t('common:core.chat.quote.Quote Tip')}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<ModalBody>
|
||||
<QuoteList rawSearch={filterResults} chatItemId={chatItemId} />
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuoteModal;
|
||||
|
||||
export const QuoteList = React.memo(function QuoteList({
|
||||
chatItemId,
|
||||
rawSearch = []
|
||||
}: {
|
||||
chatItemId?: string;
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
||||
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
|
||||
chatItemId,
|
||||
appId: v.appId,
|
||||
chatId: v.chatId,
|
||||
...(v.outLinkAuthData || {})
|
||||
}));
|
||||
const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
const showRouteToDatasetDetail = useContextSelector(
|
||||
ChatItemContext,
|
||||
(v) => v.showRouteToDatasetDetail
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{rawSearch.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
flex={'1 0 0'}
|
||||
p={2}
|
||||
borderRadius={'sm'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
bg={i % 2 === 0 ? 'white' : 'myWhite.500'}
|
||||
>
|
||||
<QuoteItem
|
||||
quoteItem={item}
|
||||
canViewSource={showRawSource}
|
||||
canEditDataset={showRouteToDatasetDetail}
|
||||
{...RawSourceBoxProps}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -14,8 +14,8 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||
import { useSize } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
|
||||
const QuoteModal = dynamic(() => import('./QuoteModal'));
|
||||
const ContextModal = dynamic(() => import('./ContextModal'));
|
||||
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
|
||||
|
||||
@@ -30,6 +30,7 @@ const ResponseTags = ({
|
||||
const { t } = useTranslation();
|
||||
const quoteListRef = React.useRef<HTMLDivElement>(null);
|
||||
const dataId = historyItem.dataId;
|
||||
const chatTime = historyItem.time || new Date();
|
||||
|
||||
const {
|
||||
totalQuoteList: quoteList = [],
|
||||
@@ -38,17 +39,15 @@ const ResponseTags = ({
|
||||
historyPreviewLength = 0
|
||||
} = useMemo(() => addStatisticalDataToHistoryItem(historyItem), [historyItem]);
|
||||
|
||||
const [quoteModalData, setQuoteModalData] = useState<{
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
metadata?: {
|
||||
collectionId: string;
|
||||
sourceId?: string;
|
||||
sourceName: string;
|
||||
};
|
||||
}>();
|
||||
const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
|
||||
|
||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
|
||||
const notSharePage = useMemo(() => chatType !== 'share', [chatType]);
|
||||
|
||||
const {
|
||||
@@ -67,6 +66,7 @@ const ResponseTags = ({
|
||||
? quoteListRef.current.scrollHeight > (isPc ? 50 : 55)
|
||||
: true;
|
||||
|
||||
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
const sourceList = useMemo(() => {
|
||||
return Object.values(
|
||||
quoteList.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => {
|
||||
@@ -81,7 +81,8 @@ const ResponseTags = ({
|
||||
sourceName: item.sourceName,
|
||||
sourceId: item.sourceId,
|
||||
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }),
|
||||
collectionId: item.collectionId
|
||||
collectionId: item.collectionId,
|
||||
datasetId: item.datasetId
|
||||
}));
|
||||
}, [quoteList]);
|
||||
|
||||
@@ -99,7 +100,11 @@ const ResponseTags = ({
|
||||
<>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||
<Box width={'100%'}>
|
||||
<ChatBoxDivider icon="core/chat/quoteFill" text={t('common:core.chat.Quote')} />
|
||||
<ChatBoxDivider
|
||||
icon="core/chat/quoteFill"
|
||||
text={t('common:core.chat.Quote')}
|
||||
iconColor="#E82F72"
|
||||
/>
|
||||
</Box>
|
||||
{quoteFolded && quoteIsOverflow && (
|
||||
<MyIcon
|
||||
@@ -135,15 +140,13 @@ const ResponseTags = ({
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{sourceList.map((item) => {
|
||||
{sourceList.map((item, index) => {
|
||||
return (
|
||||
<MyTooltip key={item.collectionId} label={t('common:core.chat.quote.Read Quote')}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
fontSize={'xs'}
|
||||
border={'sm'}
|
||||
py={1.5}
|
||||
px={2}
|
||||
borderRadius={'sm'}
|
||||
_hover={{
|
||||
'.controller': {
|
||||
@@ -155,20 +158,60 @@ const ResponseTags = ({
|
||||
cursor={'pointer'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setQuoteModalData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
collectionId: item.collectionId,
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName
|
||||
}
|
||||
});
|
||||
|
||||
if (isShowReadRawSource) {
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionId: item.collectionId,
|
||||
sourceId: item.sourceId || '',
|
||||
sourceName: item.sourceName,
|
||||
datasetId: item.datasetId,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionIdList: [item.collectionId],
|
||||
sourceId: item.sourceId || '',
|
||||
sourceName: item.sourceName,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
height={6}
|
||||
>
|
||||
<MyIcon name={item.icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box className="textEllipsis3" wordBreak={'break-all'} flex={'1 0 0'}>
|
||||
{item.sourceName}
|
||||
</Box>
|
||||
<Flex
|
||||
color={'myGray.500'}
|
||||
bg={'myGray.150'}
|
||||
w={4}
|
||||
justifyContent={'center'}
|
||||
fontSize={'10px'}
|
||||
h={'full'}
|
||||
alignItems={'center'}
|
||||
>
|
||||
{index + 1}
|
||||
</Flex>
|
||||
<Flex px={1.5}>
|
||||
<MyIcon name={item.icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box
|
||||
className="textEllipsis3"
|
||||
wordBreak={'break-all'}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'mini'}
|
||||
>
|
||||
{item.sourceName}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
);
|
||||
@@ -196,7 +239,20 @@ const ResponseTags = ({
|
||||
colorSchema="blue"
|
||||
type="borderSolid"
|
||||
cursor={'pointer'}
|
||||
onClick={() => setQuoteModalData({ rawSearch: quoteList })}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))],
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('chat:citations', { num: quoteList.length })}
|
||||
</MyTag>
|
||||
@@ -246,15 +302,10 @@ const ResponseTags = ({
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
{...quoteModalData}
|
||||
chatItemId={historyItem.dataId}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
/>
|
||||
)}
|
||||
{isOpenContextModal && <ContextModal dataId={dataId} onClose={onCloseContextModal} />}
|
||||
{isOpenWholeModal && <WholeResponseModal dataId={dataId} onClose={onCloseWholeModal} />}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal dataId={dataId} chatTime={chatTime} onClose={onCloseWholeModal} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ export const VariableInputItem = ({
|
||||
value: item.value
|
||||
}))}
|
||||
value={value}
|
||||
onchange={(e) => setValue(`variables.${item.key}`, e)}
|
||||
onChange={(e) => setValue(`variables.${item.key}`, e)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -12,12 +12,18 @@ const RenderResponseDetail = () => {
|
||||
const isChatting = useContextSelector(PluginRunContext, (v) => v.isChatting);
|
||||
|
||||
const responseData = chatRecords?.[1]?.responseData || [];
|
||||
const chatTime = new Date();
|
||||
|
||||
return isChatting ? (
|
||||
<>{t('chat:in_progress')}</>
|
||||
) : (
|
||||
<Box flex={'1 0 0'} h={'100%'} overflow={'auto'}>
|
||||
<ResponseBox useMobile={true} response={responseData} dataId={chatRecords?.[1]?.dataId} />
|
||||
<ResponseBox
|
||||
useMobile={true}
|
||||
response={responseData}
|
||||
dataId={chatRecords?.[1]?.dataId}
|
||||
chatTime={chatTime}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -162,7 +162,7 @@ const RenderPluginInput = ({
|
||||
}
|
||||
if (inputType === FlowNodeInputTypeEnum.select && input.list) {
|
||||
return (
|
||||
<MySelect list={input.list} value={value} onchange={onChange} isDisabled={isDisabled} />
|
||||
<MySelect list={input.list} value={value} onChange={onChange} isDisabled={isDisabled} />
|
||||
);
|
||||
}
|
||||
if (inputType === FlowNodeInputTypeEnum.fileSelect) {
|
||||
@@ -179,7 +179,7 @@ const RenderPluginInput = ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={onChange}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,19 @@ import React from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';
|
||||
|
||||
const ChatBoxDivider = ({ icon, text }: { icon: IconNameType; text: string }) => {
|
||||
const ChatBoxDivider = ({
|
||||
icon,
|
||||
text,
|
||||
iconColor
|
||||
}: {
|
||||
icon: IconNameType;
|
||||
text: string;
|
||||
iconColor?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<Flex alignItems={'center'} py={2} gap={2}>
|
||||
<MyIcon name={icon} w={'14px'} color={'myGray.900'} />
|
||||
<MyIcon name={icon} w={'14px'} color={iconColor || 'myGray.900'} />
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{text}
|
||||
</Box>
|
||||
|
||||
@@ -299,6 +299,7 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||
<MyNumberInput
|
||||
min={input.min}
|
||||
max={input.max}
|
||||
defaultValue={input.defaultValue}
|
||||
isDisabled={interactive.params.submitted}
|
||||
bg={'white'}
|
||||
register={register}
|
||||
@@ -321,7 +322,7 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||
list={input.list}
|
||||
value={value}
|
||||
isDisabled={interactive.params.submitted}
|
||||
onchange={(e) => setValue(input.label, e)}
|
||||
onChange={(e) => setValue(input.label, e)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import { QuoteList } from '../ChatContainer/ChatBox/components/QuoteModal';
|
||||
import QuoteList from '../ChatContainer/ChatBox/components/QuoteList';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import { formatNumber } from '@fastgpt/global/common/math/tools';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
@@ -32,11 +32,13 @@ type sideTabItemType = {
|
||||
export const WholeResponseContent = ({
|
||||
activeModule,
|
||||
hideTabs,
|
||||
dataId
|
||||
dataId,
|
||||
chatTime
|
||||
}: {
|
||||
activeModule: ChatHistoryItemResType;
|
||||
hideTabs?: boolean;
|
||||
dataId?: string;
|
||||
chatTime?: Date;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -226,10 +228,23 @@ export const WholeResponseContent = ({
|
||||
{activeModule?.searchMode && (
|
||||
<Row
|
||||
label={t('common:core.dataset.search.search mode')}
|
||||
// @ts-ignore
|
||||
value={t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
|
||||
rawDom={
|
||||
<Flex border={'base'} borderRadius={'md'} p={2}>
|
||||
<Box>
|
||||
{/* @ts-ignore */}
|
||||
{t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
|
||||
</Box>
|
||||
{activeModule.embeddingWeight && (
|
||||
<>{`(${t('chat:response_hybrid_weight', {
|
||||
emb: activeModule.embeddingWeight,
|
||||
text: 1 - activeModule.embeddingWeight
|
||||
})})`}</>
|
||||
)}
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Row
|
||||
label={t('common:core.chat.response.module similarity')}
|
||||
value={activeModule?.similarity}
|
||||
@@ -237,7 +252,19 @@ export const WholeResponseContent = ({
|
||||
<Row label={t('common:core.chat.response.module limit')} value={activeModule?.limit} />
|
||||
<Row
|
||||
label={t('common:core.chat.response.search using reRank')}
|
||||
value={`${activeModule?.searchUsingReRank}`}
|
||||
rawDom={
|
||||
<Box border={'base'} borderRadius={'md'} p={2}>
|
||||
{activeModule?.searchUsingReRank ? (
|
||||
activeModule?.rerankModel ? (
|
||||
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
|
||||
) : (
|
||||
'True'
|
||||
)
|
||||
) : (
|
||||
`False`
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
{activeModule.queryExtensionResult && (
|
||||
<>
|
||||
@@ -263,7 +290,7 @@ export const WholeResponseContent = ({
|
||||
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
|
||||
<Row
|
||||
label={t('common:core.chat.response.module quoteList')}
|
||||
rawDom={<QuoteList chatItemId={dataId} rawSearch={activeModule.quoteList} />}
|
||||
rawDom={<QuoteList chatItemDataId={dataId} rawSearch={activeModule.quoteList} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -562,11 +589,13 @@ const SideTabItem = ({
|
||||
export const ResponseBox = React.memo(function ResponseBox({
|
||||
response,
|
||||
dataId,
|
||||
chatTime,
|
||||
hideTabs = false,
|
||||
useMobile = false
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
dataId?: string;
|
||||
chatTime: Date;
|
||||
hideTabs?: boolean;
|
||||
useMobile?: boolean;
|
||||
}) {
|
||||
@@ -689,7 +718,12 @@ export const ResponseBox = React.memo(function ResponseBox({
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flex={'5 0 0'} w={0} height={'100%'}>
|
||||
<WholeResponseContent dataId={dataId} activeModule={activeModule} hideTabs={hideTabs} />
|
||||
<WholeResponseContent
|
||||
dataId={dataId}
|
||||
activeModule={activeModule}
|
||||
hideTabs={hideTabs}
|
||||
chatTime={chatTime}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
) : (
|
||||
@@ -753,6 +787,7 @@ export const ResponseBox = React.memo(function ResponseBox({
|
||||
dataId={dataId}
|
||||
activeModule={activeModule}
|
||||
hideTabs={hideTabs}
|
||||
chatTime={chatTime}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
@@ -763,7 +798,15 @@ export const ResponseBox = React.memo(function ResponseBox({
|
||||
);
|
||||
});
|
||||
|
||||
const WholeResponseModal = ({ onClose, dataId }: { onClose: () => void; dataId: string }) => {
|
||||
const WholeResponseModal = ({
|
||||
onClose,
|
||||
dataId,
|
||||
chatTime
|
||||
}: {
|
||||
onClose: () => void;
|
||||
dataId: string;
|
||||
chatTime: Date;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { getHistoryResponseData } = useContextSelector(ChatBoxContext, (v) => v);
|
||||
@@ -792,7 +835,7 @@ const WholeResponseModal = ({ onClose, dataId }: { onClose: () => void; dataId:
|
||||
}
|
||||
>
|
||||
{!!response?.length ? (
|
||||
<ResponseBox response={response} dataId={dataId} />
|
||||
<ResponseBox response={response} dataId={dataId} chatTime={chatTime} />
|
||||
) : (
|
||||
<EmptyTip text={t('chat:no_workflow_response')} />
|
||||
)}
|
||||
|
||||
@@ -3,11 +3,10 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import React from 'react';
|
||||
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
|
||||
const { datasetT } = useI18n();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const item = DatasetTypeMap[type] || DatasetTypeMap['dataset'];
|
||||
|
||||
return (
|
||||
@@ -24,8 +23,7 @@ const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & Fle
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={item.icon as any} w={'16px'} mr={2} color={'myGray.400'} />
|
||||
{/* @ts-ignore */}
|
||||
<Box>{datasetT(item.label)}</Box>
|
||||
<Box>{t(item.label as any)}</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,8 +14,8 @@ import Markdown from '@/components/Markdown';
|
||||
|
||||
const InputDataModal = dynamic(() => import('@/pageComponents/dataset/detail/InputDataModal'));
|
||||
|
||||
type ScoreItemType = SearchDataResponseItemType['score'][0];
|
||||
const scoreTheme: Record<
|
||||
export type ScoreItemType = SearchDataResponseItemType['score'][0];
|
||||
export const scoreTheme: Record<
|
||||
string,
|
||||
{
|
||||
color: string;
|
||||
@@ -44,6 +44,47 @@ const scoreTheme: Record<
|
||||
}
|
||||
};
|
||||
|
||||
export const formatScore = (score: ScoreItemType[]) => {
|
||||
if (!Array.isArray(score)) {
|
||||
return {
|
||||
primaryScore: undefined,
|
||||
secondaryScore: []
|
||||
};
|
||||
}
|
||||
|
||||
// rrf -> rerank -> embedding -> fullText 优先级
|
||||
let rrfScore: ScoreItemType | undefined = undefined;
|
||||
let reRankScore: ScoreItemType | undefined = undefined;
|
||||
let embeddingScore: ScoreItemType | undefined = undefined;
|
||||
let fullTextScore: ScoreItemType | undefined = undefined;
|
||||
|
||||
score.forEach((item) => {
|
||||
if (item.type === SearchScoreTypeEnum.rrf) {
|
||||
rrfScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.reRank) {
|
||||
reRankScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.embedding) {
|
||||
embeddingScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.fullText) {
|
||||
fullTextScore = item;
|
||||
}
|
||||
});
|
||||
|
||||
const primaryScore = (rrfScore ||
|
||||
reRankScore ||
|
||||
embeddingScore ||
|
||||
fullTextScore) as unknown as ScoreItemType;
|
||||
const secondaryScore = [rrfScore, reRankScore, embeddingScore, fullTextScore].filter(
|
||||
// @ts-ignore
|
||||
(item) => item && primaryScore && item.type !== primaryScore.type
|
||||
) as unknown as ScoreItemType[];
|
||||
|
||||
return {
|
||||
primaryScore,
|
||||
secondaryScore
|
||||
};
|
||||
};
|
||||
|
||||
const QuoteItem = ({
|
||||
quoteItem,
|
||||
canViewSource,
|
||||
@@ -58,44 +99,7 @@ const QuoteItem = ({
|
||||
const [editInputData, setEditInputData] = useState<{ dataId: string; collectionId: string }>();
|
||||
|
||||
const score = useMemo(() => {
|
||||
if (!Array.isArray(quoteItem.score)) {
|
||||
return {
|
||||
primaryScore: undefined,
|
||||
secondaryScore: []
|
||||
};
|
||||
}
|
||||
|
||||
// rrf -> rerank -> embedding -> fullText 优先级
|
||||
let rrfScore: ScoreItemType | undefined = undefined;
|
||||
let reRankScore: ScoreItemType | undefined = undefined;
|
||||
let embeddingScore: ScoreItemType | undefined = undefined;
|
||||
let fullTextScore: ScoreItemType | undefined = undefined;
|
||||
|
||||
quoteItem.score.forEach((item) => {
|
||||
if (item.type === SearchScoreTypeEnum.rrf) {
|
||||
rrfScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.reRank) {
|
||||
reRankScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.embedding) {
|
||||
embeddingScore = item;
|
||||
} else if (item.type === SearchScoreTypeEnum.fullText) {
|
||||
fullTextScore = item;
|
||||
}
|
||||
});
|
||||
|
||||
const primaryScore = (rrfScore ||
|
||||
reRankScore ||
|
||||
embeddingScore ||
|
||||
fullTextScore) as unknown as ScoreItemType;
|
||||
const secondaryScore = [rrfScore, reRankScore, embeddingScore, fullTextScore].filter(
|
||||
// @ts-ignore
|
||||
(item) => item && primaryScore && item.type !== primaryScore.type
|
||||
) as unknown as ScoreItemType[];
|
||||
|
||||
return {
|
||||
primaryScore,
|
||||
secondaryScore
|
||||
};
|
||||
return formatScore(quoteItem.score);
|
||||
}, [quoteItem.score]);
|
||||
|
||||
return (
|
||||
@@ -239,7 +243,7 @@ const QuoteItem = ({
|
||||
color={'primary.500'}
|
||||
href={`/dataset/detail?datasetId=${quoteItem.datasetId}¤tTab=dataCard&collectionId=${quoteItem.collectionId}`}
|
||||
>
|
||||
{t('common:core.dataset.Go Dataset')}
|
||||
{t('chat:to_dataset')}
|
||||
<MyIcon name={'common/rightArrowLight'} w={'10px'} />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@@ -23,7 +23,7 @@ const RawSourceBox = ({
|
||||
collectionId,
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
chatItemDataId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
@@ -40,7 +40,7 @@ const RawSourceBox = ({
|
||||
collectionId,
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
chatItemDataId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
|
||||
@@ -164,7 +164,7 @@ const LafAccountModal = ({
|
||||
}
|
||||
placeholder={t('common:plugin.App')}
|
||||
value={watch('appid')}
|
||||
onchange={(e) => {
|
||||
onChange={(e) => {
|
||||
setValue('appid', e);
|
||||
}}
|
||||
{...(register('appid'), { required: true })}
|
||||
|
||||
@@ -49,7 +49,7 @@ const DefaultPermissionList = ({
|
||||
<MySelect
|
||||
list={defaultPermissionSelectList}
|
||||
value={per}
|
||||
onchange={(per) => {
|
||||
onChange={(per) => {
|
||||
if (isInheritPermission && hasParent) {
|
||||
openConfirm(
|
||||
() => onRequestChange(per),
|
||||
|
||||
@@ -79,7 +79,7 @@ const UpdateContactModal = ({
|
||||
title={
|
||||
mode === 'notification_account'
|
||||
? t('common:support.user.info.notification_receiving_hint')
|
||||
: t('account_info:contact')
|
||||
: t('common:contact_way')
|
||||
}
|
||||
>
|
||||
<ModalBody px={10}>
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { Button, ModalFooter, ModalBody, Flex, Box, useTheme } from '@chakra-ui/react';
|
||||
import { getTeamList, updateInviteResult } from '@/web/support/user/team/api';
|
||||
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
|
||||
const UpdateInviteModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { initUserInfo } = useUserStore();
|
||||
|
||||
const { ConfirmModal, openConfirm } = useConfirm({});
|
||||
|
||||
const { data: inviteList = [], run: fetchInviteList } = useRequest2(
|
||||
async () => (feConfigs.isPlus ? getTeamList(TeamMemberStatusEnum.waiting) : []),
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
|
||||
const { runAsync: onAccept, loading: isLoadingAccept } = useRequest2(updateInviteResult, {
|
||||
onSuccess() {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:user.team.invite.Accepted')
|
||||
});
|
||||
fetchInviteList();
|
||||
initUserInfo();
|
||||
}
|
||||
});
|
||||
const { runAsync: onReject, loading: isLoadingReject } = useRequest2(updateInviteResult, {
|
||||
onSuccess() {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:user.team.invite.Reject')
|
||||
});
|
||||
fetchInviteList();
|
||||
initUserInfo();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={inviteList && inviteList.length > 0}
|
||||
iconSrc="/imgs/modal/team.svg"
|
||||
title={
|
||||
<Box>
|
||||
<Box>{t('common:user.team.Processing invitations')}</Box>
|
||||
<Box fontWeight={'normal'} fontSize={'sm'} color={'myGray.500'}>
|
||||
{t('common:user.team.Processing invitations Tips', { amount: inviteList?.length })}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
maxW={['90vw', '500px']}
|
||||
>
|
||||
<ModalBody>
|
||||
{inviteList?.map((item) => (
|
||||
<Flex
|
||||
key={item.teamId}
|
||||
alignItems={'center'}
|
||||
border={theme.borders.base}
|
||||
borderRadius={'md'}
|
||||
px={3}
|
||||
py={2}
|
||||
_notFirst={{
|
||||
mt: 3
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w={['16px', '23px']} />
|
||||
<Box mx={2}>{item.teamName}</Box>
|
||||
<Box flex={1} />
|
||||
<Button
|
||||
size="sm"
|
||||
variant={'solid'}
|
||||
colorScheme="green"
|
||||
isLoading={isLoadingAccept}
|
||||
onClick={() => {
|
||||
openConfirm(
|
||||
() =>
|
||||
onAccept({
|
||||
tmbId: item.tmbId,
|
||||
status: TeamMemberStatusEnum.active
|
||||
}),
|
||||
undefined,
|
||||
t('common:user.team.invite.Accept Confirm')
|
||||
)();
|
||||
}}
|
||||
>
|
||||
{t('common:user.team.invite.accept')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
ml={2}
|
||||
variant={'solid'}
|
||||
colorScheme="red"
|
||||
isLoading={isLoadingReject}
|
||||
onClick={() => {
|
||||
openConfirm(
|
||||
() =>
|
||||
onReject({
|
||||
tmbId: item.tmbId,
|
||||
status: TeamMemberStatusEnum.reject
|
||||
}),
|
||||
undefined,
|
||||
t('common:user.team.invite.Reject Confirm')
|
||||
)();
|
||||
}}
|
||||
>
|
||||
{t('common:user.team.invite.reject')}
|
||||
</Button>
|
||||
</Flex>
|
||||
))}
|
||||
</ModalBody>
|
||||
<ModalFooter justifyContent={'center'}>
|
||||
<Box>{t('common:user.team.invite.Deal Width Footer Tip')}</Box>
|
||||
</ModalFooter>
|
||||
|
||||
<ConfirmModal />
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(UpdateInviteModal);
|
||||
Reference in New Issue
Block a user