V4.8.20 feature (#3686)

* Aiproxy (#3649)

* model config

* feat: model config ui

* perf: rename variable

* feat: custom request url

* perf: model buffer

* perf: init model

* feat: json model config

* auto login

* fix: ts

* update packages

* package

* fix: dockerfile

* feat: usage filter & export & dashbord (#3538)

* feat: usage filter & export & dashbord

* adjust ui

* fix tmb scroll

* fix code & selecte all

* merge

* perf: usages list;perf: move components (#3654)

* perf: usages list

* team sub plan load

* perf: usage dashboard code

* perf: dashboard ui

* perf: move components

* add default model config (#3653)

* 4.8.20 test (#3656)

* provider

* perf: model config

* model perf (#3657)

* fix: model

* dataset quote

* perf: model config

* model tag

* doubao model config

* perf: config model

* feat: model test

* fix: POST 500 error on dingtalk bot (#3655)

* feat: default model (#3662)

* move model config

* feat: default model

* fix: false triggerd org selection (#3661)

* export usage csv i18n (#3660)

* export usage csv i18n

* fix build

* feat: markdown extension (#3663)

* feat: markdown extension

* media cros

* rerank test

* default price

* perf: default model

* fix: cannot custom provider

* fix: default model select

* update bg

* perf: default model selector

* fix: usage export

* i18n

* fix: rerank

* update init extension

* perf: ip limit check

* doubao model order

* web default modle

* perf: tts selector

* perf: tts error

* qrcode package

* reload buffer (#3665)

* reload buffer

* reload buffer

* tts selector

* fix: err tip (#3666)

* fix: err tip

* perf: training queue

* doc

* fix interactive edge (#3659)

* fix interactive edge

* fix

* comment

* add gemini model

* fix: chat model select

* perf: supplement assistant empty response (#3669)

* perf: supplement assistant empty response

* check array

* perf: max_token count;feat: support resoner output;fix: member scroll (#3681)

* perf: supplement assistant empty response

* check array

* perf: max_token count

* feat: support resoner output

* member scroll

* update provider order

* i18n

* fix: stream response (#3682)

* perf: supplement assistant empty response

* check array

* fix: stream response

* fix: model config cannot set to null

* fix: reasoning response (#3684)

* perf: supplement assistant empty response

* check array

* fix: reasoning response

* fix: reasoning response

* doc (#3685)

* perf: supplement assistant empty response

* check array

* doc

* lock

* animation

* update doc

* update compose

* doc

* doc

---------

Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
This commit is contained in:
Archer
2025-02-05 00:10:47 +08:00
committed by GitHub
parent c393002f1d
commit db2c0a0bdb
496 changed files with 9031 additions and 4726 deletions

View File

@@ -0,0 +1,105 @@
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
import {
Button,
Link,
Popover,
PopoverTrigger,
PopoverContent,
PopoverHeader,
PopoverBody,
PopoverArrow,
PopoverCloseButton
} from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import React, { useMemo } from 'react';
import { getQuoteData } from '@/web/core/dataset/api';
import MyBox from '@fastgpt/web/components/common/MyBox';
import RawSourceBox from '../core/dataset/RawSourceBox';
import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection/utils';
import Markdown from '.';
const A = ({ children, ...props }: any) => {
const { t } = useTranslation();
const {
data: quoteData,
loading,
runAsync
} = useRequest2(getQuoteData, {
manual: true
});
// empty href link
if (!props.href && typeof children?.[0] === 'string') {
const text = useMemo(() => String(children), [children]);
return (
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
<Button
variant={'whitePrimary'}
size={'xs'}
borderRadius={'md'}
my={1}
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
>
{text}
</Button>
</MyTooltip>
);
}
// Quote
if (props.href === 'QUOTE' && typeof children?.[0] === 'string') {
return (
<Popover
direction="rtl"
isLazy
placement="auto"
strategy={'fixed'}
onOpen={() => runAsync(String(children))}
>
<PopoverTrigger>
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
<MyTooltip label={t('common:read_quote')}>
<MyIcon
name={'core/chat/quoteSign'}
w={'1rem'}
color={'primary.700'}
cursor={'pointer'}
transform={'translateY(-3px)'}
/>
</MyTooltip>
</Button>
</PopoverTrigger>
<PopoverContent boxShadow={'lg'} w={'400px'}>
<MyBox isLoading={loading} minH={'300px'}>
<PopoverArrow />
<PopoverHeader h={'40px'} display={'flex'} alignItems={'center'}>
{quoteData?.collection && (
<RawSourceBox
collectionId={quoteData?.collection._id}
{...getCollectionSourceData(quoteData?.collection)}
fontSize={'sm'}
color={'black'}
textDecoration={'none'}
/>
)}
<PopoverCloseButton />
</PopoverHeader>
<PopoverBody fontSize={'sm'} maxH={'400px'} overflow={'auto'}>
<Markdown source={quoteData?.q} />
<Markdown source={quoteData?.a} />
</PopoverBody>
</MyBox>
</PopoverContent>
</Popover>
);
}
return <Link {...props}>{children}</Link>;
};
export default A;

View File

@@ -0,0 +1,31 @@
import React, { useEffect } from 'react';
import { Box } from '@chakra-ui/react';
import { useMarkdownWidth } from '../hooks';
const AudioBlock = ({ code: audioUrl }: { code: string }) => {
const { width, Ref } = useMarkdownWidth();
useEffect(() => {
fetch(audioUrl?.trim(), {
mode: 'cors',
credentials: 'omit'
})
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
const audio = document.getElementById('player');
audio?.setAttribute('src', url);
})
.catch((err) => {
console.log(err);
});
}, [audioUrl]);
return (
<Box w={width} ref={Ref}>
<audio id="player" controls style={{ width: '100%' }} />
</Box>
);
};
export default AudioBlock;

View File

@@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex } from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useTranslation } from 'next-i18next';
export const codeLight: { [key: string]: React.CSSProperties } = {

View File

@@ -0,0 +1,31 @@
import React, { useEffect } from 'react';
import { Box } from '@chakra-ui/react';
import { useMarkdownWidth } from '../hooks';
const VideoBlock = ({ code: videoUrl }: { code: string }) => {
const { width, Ref } = useMarkdownWidth();
useEffect(() => {
fetch(videoUrl?.trim(), {
mode: 'cors',
credentials: 'omit'
})
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
const video = document.getElementById('player');
video?.setAttribute('src', url);
})
.catch((err) => {
console.log(err);
});
}, [videoUrl]);
return (
<Box w={width} ref={Ref}>
<video id="player" controls />
</Box>
);
};
export default VideoBlock;

View File

@@ -12,11 +12,11 @@ import {
ModalCloseButton
} from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useTranslation } from 'next-i18next';
import { useMarkdownWidth } from '../hooks';
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';
import { codeLight } from '../CodeLight';
import { codeLight } from './CodeLight';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const StyledButton = ({

View File

@@ -10,23 +10,21 @@ import RehypeExternalLinks from 'rehype-external-links';
import styles from './index.module.scss';
import dynamic from 'next/dynamic';
import { Link, Button, Box } from '@chakra-ui/react';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
import { Box } from '@chakra-ui/react';
import { CodeClassNameEnum } from './utils';
const CodeLight = dynamic(() => import('./CodeLight'), { ssr: false });
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
const MdImage = dynamic(() => import('./img/Image'), { ssr: false });
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'), { ssr: false });
const IframeCodeBlock = dynamic(() => import('./codeBlock/Iframe'), { ssr: false });
const IframeHtmlCodeBlock = dynamic(() => import('./codeBlock/iframe-html'), { ssr: false });
const VideoBlock = dynamic(() => import('./codeBlock/Video'), { ssr: false });
const AudioBlock = dynamic(() => import('./codeBlock/Audio'), { ssr: false });
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
const A = dynamic(() => import('./A'), { ssr: false });
type Props = {
source?: string;
@@ -74,7 +72,10 @@ const MarkdownRender = ({ source = '', showAnimation, isDisabled, forbidZhFormat
'$1$3 $2$4'
)
// 处理引用标记
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1')
// 处理 [quote:id] 格式引用,将 [quote:675934a198f46329dfc6d05a] 转换为 [675934a198f46329dfc6d05a]()
.replace(/\[quote:?\s*([a-f0-9]{24})\](?!\()/gi, '[$1](QUOTE)')
.replace(/\[([a-f0-9]{24})\](?!\()/g, '[$1](QUOTE)');
// 还原 URL
const finalText = textWithSpaces.replace(
@@ -140,6 +141,12 @@ function Code(e: any) {
</IframeHtmlCodeBlock>
);
}
if (codeType === CodeClassNameEnum.video) {
return <VideoBlock code={strChildren} />;
}
if (codeType === CodeClassNameEnum.audio) {
return <AudioBlock code={strChildren} />;
}
return (
<CodeLight className={className} codeBlock={codeBlock} match={match}>
@@ -155,53 +162,6 @@ function Image({ src }: { src?: string }) {
return <MdImage src={src} />;
}
function A({ children, ...props }: any) {
const { t } = useTranslation();
// empty href link
if (!props.href && typeof children?.[0] === 'string') {
const text = useMemo(() => String(children), [children]);
return (
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
<Button
variant={'whitePrimary'}
size={'xs'}
borderRadius={'md'}
my={1}
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
>
{text}
</Button>
</MyTooltip>
);
}
// quote link(未使用)
if (children?.length === 1 && typeof children?.[0] === 'string') {
const text = String(children);
if (text === MARKDOWN_QUOTE_SIGN && props.href) {
return (
<MyTooltip label={props.href}>
<MyIcon
name={'core/chat/quoteSign'}
transform={'translateY(-2px)'}
w={'18px'}
color={'primary.500'}
cursor={'pointer'}
_hover={{
color: 'primary.700'
}}
// onClick={() => getCollectionSourceAndOpen(props.href)}
/>
</MyTooltip>
);
}
}
return <Link {...props}>{children}</Link>;
}
function RewritePre({ children }: any) {
const modifiedChildren = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {

View File

@@ -7,7 +7,9 @@ export enum CodeClassNameEnum {
files = 'files',
latex = 'latex',
iframe = 'iframe',
html = 'html'
html = 'html',
video = 'video',
audio = 'audio'
}
function htmlTableToLatex(html: string) {

View File

@@ -3,8 +3,8 @@ import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MySelect, { SelectProps } from '@fastgpt/web/components/common/MySelect';
import { HUGGING_FACE_ICON, LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { Box, Flex, HStack, useDisclosure } from '@chakra-ui/react';
import { HUGGING_FACE_ICON } from '@fastgpt/global/common/system/constants';
import { Box, Flex, HStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic';
@@ -22,7 +22,8 @@ type Props = SelectProps & {
const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
const { t } = useTranslation();
const { feConfigs, llmModelList, vectorModelList } = useSystemStore();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore();
const avatarSize = useMemo(() => {
const size = {
@@ -35,7 +36,16 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
}, [props.size]);
const avatarList = list.map((item) => {
const modelData = getModelFromList([...llmModelList, ...vectorModelList], item.value);
const modelData = getModelFromList(
[
...llmModelList,
...embeddingModelList,
...ttsModelList,
...sttModelList,
...reRankModelList
],
item.value
);
return {
value: item.value,
@@ -54,20 +64,6 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
};
});
const expandList = useMemo(() => {
return feConfigs?.show_pay
? avatarList.concat({
label: (
<Flex alignItems={'center'}>
<Avatar borderRadius={'0'} mr={2} src={LOGO_ICON} w={avatarSize} />
<Box>{t('common:support.user.Price')}</Box>
</Flex>
),
value: 'price'
})
: avatarList;
}, [feConfigs.show_pay, avatarList, avatarSize, t]);
return (
<Box
css={{
@@ -82,7 +78,8 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
<MySelect
className="nowheel"
isDisabled={!!disableTip}
list={expandList}
list={avatarList}
h={'40px'}
{...props}
onchange={(e) => {
if (e === 'price') {
@@ -100,7 +97,8 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
};
const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
const { t } = useTranslation();
const { llmModelList, vectorModelList } = useSystemStore();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore();
const [value, setValue] = useState<string[]>([]);
const avatarSize = useMemo(() => {
@@ -136,7 +134,7 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
}));
for (const item of list) {
const modelData = getModelFromList([...llmModelList, ...vectorModelList], item.value);
const modelData = getModelFromList([...llmModelList, ...embeddingModelList], item.value);
const provider =
renderList.find((item) => item.value === (modelData?.provider || 'Other')) ??
renderList[renderList.length - 1];
@@ -148,7 +146,7 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
}
return renderList.filter((item) => item.children.length > 0);
}, [avatarSize, list, llmModelList, t, vectorModelList]);
}, [avatarSize, list, llmModelList, t, embeddingModelList]);
const onSelect = useCallback(
(e: string[]) => {
@@ -158,7 +156,16 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
);
const SelectedModel = useMemo(() => {
const modelData = getModelFromList([...llmModelList, ...vectorModelList], props.value);
const modelData = getModelFromList(
[
...llmModelList,
...embeddingModelList,
...ttsModelList,
...sttModelList,
...reRankModelList
],
props.value
);
setValue([modelData.provider, props.value]);
@@ -174,7 +181,15 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
<Box>{modelData?.name}</Box>
</HStack>
);
}, [avatarSize, llmModelList, props.value, vectorModelList]);
}, [
llmModelList,
embeddingModelList,
ttsModelList,
sttModelList,
reRankModelList,
props.value,
avatarSize
]);
return (
<Box
@@ -192,7 +207,9 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
value={value}
rowMinWidth="160px"
ButtonProps={{
isDisabled: !!disableTip
isDisabled: !!disableTip,
h: '40px',
...props
}}
/>
</MyTooltip>

View File

@@ -72,6 +72,7 @@ const AIChatSettingsModal = ({
defaultValues: defaultData
});
const model = watch('model');
const reasoning = watch(NodeInputKeyEnum.aiChatReasoning);
const showResponseAnswerText = watch(NodeInputKeyEnum.aiChatIsResponseText) !== undefined;
const showVisionSwitch = watch(NodeInputKeyEnum.aiChatVision) !== undefined;
const showMaxHistoriesSlider = watch('maxHistories') !== undefined;
@@ -80,8 +81,12 @@ const AIChatSettingsModal = ({
const temperature = watch('temperature');
const useVision = watch('aiChatVision');
const selectedModel = getWebLLMModel(model);
const selectedModel = useMemo(() => {
return getWebLLMModel(model);
}, [model]);
const llmSupportVision = !!selectedModel?.vision;
const llmSupportTemperature = typeof selectedModel?.maxTemperature === 'number';
const llmSupportReasoning = !!selectedModel?.reasoning;
const tokenLimit = useMemo(() => {
return selectedModel?.maxResponse || 4096;
@@ -244,7 +249,7 @@ const AIChatSettingsModal = ({
</Box>
<Box flex={'1 0 0'}>
<InputSlider
min={100}
min={0}
max={tokenLimit}
step={200}
isDisabled={maxToken === undefined}
@@ -256,36 +261,51 @@ const AIChatSettingsModal = ({
/>
</Box>
</Flex>
<Flex {...FlexItemStyles}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
{t('app:temperature')}
<QuestionTip label={t('app:temperature_tip')} />
</Flex>
<Switch
isChecked={temperature !== undefined}
size={'sm'}
onChange={(e) => {
setValue('temperature', e.target.checked ? 0 : undefined);
}}
/>
</Box>
<Box flex={'1 0 0'}>
<InputSlider
min={0}
max={10}
step={1}
value={temperature}
isDisabled={temperature === undefined}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatTemperature, e);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
{llmSupportTemperature && (
<Flex {...FlexItemStyles}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
{t('app:temperature')}
<QuestionTip label={t('app:temperature_tip')} />
</Flex>
<Switch
isChecked={temperature !== undefined}
size={'sm'}
onChange={(e) => {
setValue('temperature', e.target.checked ? 0 : undefined);
}}
/>
</Box>
<Box flex={'1 0 0'}>
<InputSlider
min={0}
max={10}
step={1}
value={temperature}
isDisabled={temperature === undefined}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatTemperature, e);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
)}
{llmSupportReasoning && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>{t('app:reasoning_response')}</Flex>
<Switch
isChecked={reasoning || false}
size={'sm'}
onChange={(e) => {
const value = e.target.checked;
setValue(NodeInputKeyEnum.aiChatReasoning, value);
}}
/>
</Box>
</Flex>
)}
{showResponseAnswerText && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
@@ -307,12 +327,11 @@ const AIChatSettingsModal = ({
)}
{showVisionSwitch && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
<Box {...LabelStyles} w={llmSupportVision ? '9rem' : 'auto'}>
<Flex alignItems={'center'}>
{t('app:llm_use_vision')}
<QuestionTip ml={1} label={t('app:llm_use_vision_tip')}></QuestionTip>
</Flex>
{llmSupportVision ? (
<Switch
isChecked={useVision}
@@ -323,7 +342,7 @@ const AIChatSettingsModal = ({
}}
/>
) : (
<Box fontSize={'sm'} color={'myGray.500'}>
<Box ml={3} fontSize={'sm'} color={'myGray.500'}>
{t('app:llm_not_support_vision')}
</Box>
)}

View File

@@ -26,6 +26,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import dynamic from 'next/dynamic';
import CopyBox from '@fastgpt/web/components/common/String/CopyBox';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
@@ -53,7 +54,8 @@ const ModelTable = () => {
const [search, setSearch] = useState('');
const { llmModelList, audioSpeechModelList, vectorModelList, whisperModel } = useSystemStore();
const { llmModelList, ttsModelList, embeddingModelList, sttModelList, reRankModelList } =
useSystemStore();
const modelList = useMemo(() => {
const formatLLMModelList = llmModelList.map((item) => ({
@@ -80,64 +82,72 @@ const ModelTable = () => {
) : (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
{item.charsPointsPrice || 0}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
),
tagColor: 'blue'
}));
const formatVectorModelList = vectorModelList.map((item) => ({
const formatVectorModelList = embeddingModelList.map((item) => ({
...item,
typeLabel: t('common:model.type.embedding'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
{item.charsPointsPrice || 0}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
),
tagColor: 'yellow'
}));
const formatAudioSpeechModelList = audioSpeechModelList.map((item) => ({
const formatAudioSpeechModelList = ttsModelList.map((item) => ({
...item,
typeLabel: t('common:model.type.tts'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
{item.charsPointsPrice || 0}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K ${t('common:unit.character')}`}
</Flex>
),
tagColor: 'green'
}));
const formatWhisperModel = {
...whisperModel,
const formatWhisperModelList = sttModelList.map((item) => ({
...item,
typeLabel: t('common:model.type.stt'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{whisperModel.charsPointsPrice}
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 60${t('common:unit.seconds')}`}
</Flex>
),
tagColor: 'purple'
};
}));
const formatRerankModelList = reRankModelList.map((item) => ({
...item,
typeLabel: t('common:model.type.reRank'),
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
tagColor: 'red'
}));
const list = (() => {
if (modelType === ModelTypeEnum.chat) return formatLLMModelList;
if (modelType === ModelTypeEnum.llm) return formatLLMModelList;
if (modelType === ModelTypeEnum.embedding) return formatVectorModelList;
if (modelType === ModelTypeEnum.tts) return formatAudioSpeechModelList;
if (modelType === ModelTypeEnum.stt) return [formatWhisperModel];
if (modelType === ModelTypeEnum.stt) return formatWhisperModelList;
if (modelType === ModelTypeEnum.rerank) return formatRerankModelList;
return [
...formatLLMModelList,
...formatVectorModelList,
...formatAudioSpeechModelList,
formatWhisperModel
...formatWhisperModelList,
...formatRerankModelList
];
})();
const formatList = list.map((item) => {
@@ -167,9 +177,10 @@ const ModelTable = () => {
return filterList;
}, [
llmModelList,
vectorModelList,
audioSpeechModelList,
whisperModel,
embeddingModelList,
ttsModelList,
sttModelList,
reRankModelList,
t,
modelType,
provider,
@@ -179,15 +190,16 @@ const ModelTable = () => {
const filterProviderList = useMemo(() => {
const allProviderIds: string[] = [
...llmModelList,
...vectorModelList,
...audioSpeechModelList,
whisperModel
...embeddingModelList,
...ttsModelList,
...sttModelList,
...reRankModelList
].map((model) => model.provider);
return providerList.current.filter(
(item) => allProviderIds.includes(item.value) || item.value === ''
);
}, [audioSpeechModelList, llmModelList, vectorModelList, whisperModel]);
}, [ttsModelList, llmModelList, embeddingModelList, sttModelList, reRankModelList]);
return (
<Flex flexDirection={'column'} h={'100%'}>
@@ -241,7 +253,9 @@ const ModelTable = () => {
<Td fontSize={'sm'}>
<HStack>
<Avatar src={item.avatar} w={'1.2rem'} />
<Box color={'myGray.900'}>{item.name}</Box>
<CopyBox value={item.name} color={'myGray.900'}>
{item.name}
</CopyBox>
</HStack>
</Td>
<Td>

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { LLMModelTypeEnum, llmModelTypeFilterMap } from '@fastgpt/global/core/ai/constants';
import { Box, css, HStack, IconButton, useDisclosure } from '@chakra-ui/react';
@@ -7,8 +7,8 @@ import AISettingModal, { AIChatSettingsModalProps } from '@/components/core/ai/A
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useMount } from 'ahooks';
import AIModelSelector from '@/components/Select/AIModelSelector';
import { getWebDefaultModel } from '@/web/common/system/utils';
type Props = {
llmModelType?: `${LLMModelTypeEnum}`;
@@ -39,15 +39,19 @@ const SettingLLMModel = ({
}),
[llmModelList, llmModelType]
);
const defaultModel = useMemo(() => {
return getWebDefaultModel(modelList).model;
}, [modelList]);
// Set default model
useMount(() => {
if (!model && modelList.length > 0) {
useEffect(() => {
if (!modelList.find((item) => item.model === model) && !!defaultModel) {
onChange({
...defaultData,
model: modelList[0].model
model: defaultModel
});
}
});
}, [modelList, model, defaultModel, onChange]);
const {
isOpen: isOpenAIChatSetting,

View File

@@ -65,17 +65,15 @@ const DatasetParamsModal = ({
const theme = useTheme();
const { toast } = useToast();
const { teamPlanStatus } = useUserStore();
const { reRankModelList, llmModelList } = useSystemStore();
const { reRankModelList, llmModelList, defaultModels } = useSystemStore();
const [refresh, setRefresh] = useState(false);
const [currentTabType, setCurrentTabType] = useState(SearchSettingTabEnum.searchMode);
const chatModelSelectList = (() =>
llmModelList
.filter((model) => model.usedInQueryExtension)
.map((item) => ({
value: item.model,
label: item.name
})))();
llmModelList.map((item) => ({
value: item.model,
label: item.name
})))();
const { register, setValue, getValues, handleSubmit, watch } = useForm<DatasetParamsProps>({
defaultValues: {
@@ -84,7 +82,7 @@ const DatasetParamsModal = ({
searchMode,
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel: datasetSearchExtensionModel || chatModelSelectList[0]?.value,
datasetSearchExtensionModel: datasetSearchExtensionModel || defaultModels.llm?.model,
datasetSearchExtensionBg
}
});

View File

@@ -1,6 +1,6 @@
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { Box, Button, Flex, ModalBody, useDisclosure, Image } from '@chakra-ui/react';
import { Box, Button, Flex, ModalBody, useDisclosure, Image, HStack } from '@chakra-ui/react';
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'next-i18next';
import { TTSTypeEnum } from '@/web/core/app/constants';
@@ -9,13 +9,15 @@ import { useAudioPlay } from '@/web/common/utils/voice';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyModal from '@fastgpt/web/components/common/MyModal';
import MySlider from '@/components/Slider';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { defaultTTSConfig } from '@fastgpt/global/core/app/constants';
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import { AppContext } from '@/pageComponents/app/detail/context';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { getModelProvider } from '@fastgpt/global/core/ai/provider';
import MultipleRowSelect from '@fastgpt/web/components/common/MySelect/MultipleRowSelect';
const TTSSelect = ({
value = defaultTTSConfig,
@@ -25,33 +27,62 @@ const TTSSelect = ({
onChange: (e: AppTTSConfigType) => void;
}) => {
const { t } = useTranslation();
const { audioSpeechModelList } = useSystemStore();
const { ttsModelList } = useSystemStore();
const { isOpen, onOpen, onClose } = useDisclosure();
const appId = useContextSelector(AppContext, (v) => v.appId);
const list = useMemo(
const selectorList = useMemo(
() => [
{ label: t('common:core.app.tts.Close'), value: TTSTypeEnum.none },
{ label: t('common:core.app.tts.Web'), value: TTSTypeEnum.web },
...audioSpeechModelList.map((item) => item?.voices || []).flat()
{ label: t('app:tts_close'), value: TTSTypeEnum.none, children: [] },
{ label: t('app:tts_browser'), value: TTSTypeEnum.web, children: [] },
...ttsModelList.map((model) => {
const providerData = getModelProvider(model.provider);
return {
label: (
<HStack>
<Avatar borderRadius={'0'} w={'1.25rem'} src={providerData.avatar} />
<Box>{t(providerData.name)}</Box>
</HStack>
),
value: model.model,
children: model.voices.map((voice) => ({
label: voice.label,
value: voice.value
}))
};
})
],
[audioSpeechModelList, t]
[ttsModelList, t]
);
const formatValue = useMemo(() => {
if (!value || !value.type) {
return TTSTypeEnum.none;
return [TTSTypeEnum.none, undefined];
}
if (value.type === TTSTypeEnum.none || value.type === TTSTypeEnum.web) {
return value.type;
return [value.type, undefined];
}
return value.voice;
return [value.model, value.voice];
}, [value]);
const formLabel = useMemo(
() => list.find((item) => item.value === formatValue)?.label || t('common:common.UnKnow'),
[formatValue, list, t]
);
const formLabel = useMemo(() => {
const provider = selectorList.find((item) => item.value === formatValue[0]) || selectorList[0];
const voice = provider.children.find((item) => item.value === formatValue[1]);
return (
<Box maxW={'220px'} className="textEllipsis">
{voice ? (
<Flex alignItems={'center'}>
<Box>{provider.label}</Box>
<Box>-</Box>
<Box>{voice.label}</Box>
</Flex>
) : (
provider.label
)}
</Box>
);
}, [formatValue, selectorList, t]);
const { playAudioByText, cancelAudio, audioLoading, audioPlaying } = useAudioPlay({
appId,
@@ -59,25 +90,20 @@ const TTSSelect = ({
});
const onclickChange = useCallback(
(e: string) => {
if (e === TTSTypeEnum.none || e === TTSTypeEnum.web) {
onChange({ type: e as `${TTSTypeEnum}` });
(e: string[]) => {
console.log(e, '-=');
if (e[0] === TTSTypeEnum.none || e[0] === TTSTypeEnum.web) {
onChange({ type: e[0] });
} else {
const audioModel = audioSpeechModelList.find((item) =>
item.voices?.find((voice) => voice.value === e)
);
if (!audioModel) {
return;
}
onChange({
...value,
type: TTSTypeEnum.model,
model: audioModel.model,
voice: e
model: e[0],
voice: e[1]
});
}
},
[audioSpeechModelList, onChange, value]
[ttsModelList, onChange, value]
);
const onCloseTTSModal = useCallback(() => {
@@ -113,7 +139,13 @@ const TTSSelect = ({
<ModalBody px={[5, 16]} py={[4, 8]}>
<Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel>{t('common:core.app.tts.Speech model')}</FormLabel>
<MySelect w={'220px'} value={formatValue} list={list} onchange={onclickChange} />
<MultipleRowSelect
rowMinWidth="160px"
label={<Box minW={'150px'}>{formLabel}</Box>}
value={formatValue}
list={selectorList}
onSelect={onclickChange}
/>
</Flex>
<Flex mt={8} justifyContent={'space-between'}>
<FormLabel>{t('common:core.app.tts.Speech speed')}</FormLabel>
@@ -135,7 +167,7 @@ const TTSSelect = ({
}}
/>
</Flex>
{formatValue !== TTSTypeEnum.none && (
{formatValue[0] !== TTSTypeEnum.none && (
<Flex mt={10} justifyContent={'end'}>
{audioPlaying ? (
<Flex>

View File

@@ -30,7 +30,7 @@ import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/ut
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import InputTypeConfig from '@/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
import InputTypeConfig from '@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
export const defaultVariable: VariableItemType = {

View File

@@ -25,7 +25,7 @@ const WelcomeTextConfig = (props: TextareaProps) => {
mt={1.5}
rows={6}
fontSize={'sm'}
bg={'white'}
bg={'myGray.50'}
minW={'384px'}
placeholder={t('common:core.app.tip.welcomeTextTip')}
autoHeight

View File

@@ -107,7 +107,7 @@ const ChatInput = ({
);
/* whisper init */
const { whisperModel } = useSystemStore();
const { sttModelList } = useSystemStore();
const canvasRef = useRef<HTMLCanvasElement>(null);
const {
isSpeaking,
@@ -293,7 +293,7 @@ const ChatInput = ({
/>
<Flex alignItems={'center'} position={'absolute'} right={[2, 4]} bottom={['10px', '12px']}>
{/* voice-input */}
{whisperConfig.open && !inputValue && !isChatting && !!whisperModel && (
{whisperConfig.open && !inputValue && !isChatting && sttModelList.length > 0 && (
<>
<canvas
ref={canvasRef}
@@ -431,7 +431,7 @@ const ChatInput = ({
stopSpeak,
t,
whisperConfig.open,
whisperModel
sttModelList
]
);

View File

@@ -1,4 +1,4 @@
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { Flex, FlexProps, css, useTheme } from '@chakra-ui/react';
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';

View File

@@ -15,7 +15,7 @@ import FilesBlock from './FilesBox';
import { ChatBoxContext } from '../Provider';
import { useContextSelector } from 'use-context-selector';
import AIResponseBox from '../../../components/AIResponseBox';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';

View File

@@ -10,7 +10,7 @@ import { AdminFbkType } from '@fastgpt/global/core/chat/type.d';
import SelectCollections from '@/web/core/dataset/components/SelectCollections';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
const InputDataModal = dynamic(() => import('@/pageComponents/dataset/detail/InputDataModal'));
export type AdminMarkType = {
feedbackDataId?: string;

View File

@@ -201,6 +201,7 @@ const ChatBox = ({
({
event,
text = '',
reasoningText,
status,
name,
tool,
@@ -225,6 +226,25 @@ const ChatBox = ({
status,
moduleName: name
};
} else if (event === SseResponseEventEnum.answer && reasoningText) {
if (lastValue.type === ChatItemValueTypeEnum.reasoning && lastValue.reasoning) {
lastValue.reasoning.content += reasoningText;
return {
...item,
value: item.value.slice(0, -1).concat(lastValue)
};
} else {
const val: AIChatItemValueItemType = {
type: ChatItemValueTypeEnum.reasoning,
reasoning: {
content: reasoningText
}
};
return {
...item,
value: item.value.concat(val)
};
}
} else if (
(event === SseResponseEventEnum.answer || event === SseResponseEventEnum.fastAnswer) &&
text

View File

@@ -6,6 +6,7 @@ import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/t
export type generatingMessageProps = {
event: SseResponseEventEnum;
text?: string;
reasoningText?: string;
name?: string;
status?: 'running' | 'finish';
tool?: ToolModuleResponseItemType;

View File

@@ -8,6 +8,7 @@ import {
Box,
Button,
Flex,
HStack,
Textarea
} from '@chakra-ui/react';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
@@ -139,6 +140,58 @@ ${toolResponse}`}
},
(prevProps, nextProps) => isEqual(prevProps, nextProps)
);
const RenderResoningContent = React.memo(function RenderResoningContent({
content,
isChatting,
isLastResponseValue
}: {
content: string;
isChatting: boolean;
isLastResponseValue: boolean;
}) {
const { t } = useTranslation();
const showAnimation = isChatting && isLastResponseValue;
return (
<Accordion allowToggle defaultIndex={isLastResponseValue ? 0 : undefined}>
<AccordionItem borderTop={'none'} borderBottom={'none'}>
<AccordionButton
w={'auto'}
bg={'white'}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'myGray.200'}
boxShadow={'1'}
pl={3}
pr={2.5}
py={1}
_hover={{
bg: 'auto'
}}
>
<HStack mr={2} spacing={1}>
<MyIcon name={'core/chat/think'} w={'0.85rem'} />
<Box fontSize={'sm'}>{t('chat:ai_reasoning')}</Box>
</HStack>
{showAnimation && <MyIcon name={'common/loading'} w={'0.85rem'} />}
<AccordionIcon color={'myGray.600'} ml={5} />
</AccordionButton>
<AccordionPanel
py={0}
pr={0}
pl={3}
mt={2}
borderLeft={'2px solid'}
borderColor={'myGray.300'}
color={'myGray.500'}
>
<Markdown source={content} showAnimation={showAnimation} />
</AccordionPanel>
</AccordionItem>
</Accordion>
);
});
const RenderUserSelectInteractive = React.memo(function RenderInteractive({
interactive
}: {
@@ -290,6 +343,14 @@ const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => {
return (
<RenderText showAnimation={isChatting && isLastResponseValue} text={value.text.content} />
);
if (value.type === ChatItemValueTypeEnum.reasoning && value.reasoning)
return (
<RenderResoningContent
isChatting={isChatting}
isLastResponseValue={isLastResponseValue}
content={value.reasoning.content}
/>
);
if (value.type === ChatItemValueTypeEnum.tool && value.tools)
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {

View File

@@ -12,7 +12,7 @@ import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/da
import type { readCollectionSourceBody } from '@/pages/api/core/dataset/collection/read';
import Markdown from '@/components/Markdown';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
const InputDataModal = dynamic(() => import('@/pageComponents/dataset/detail/InputDataModal'));
type ScoreItemType = SearchDataResponseItemType['score'][0];
const scoreTheme: Record<

View File

@@ -26,7 +26,7 @@ import {
import type { EditApiKeyProps } from '@/global/support/openapi/api.d';
import dayjs from 'dayjs';
import { AddIcon } from '@chakra-ui/icons';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';

View File

@@ -353,8 +353,9 @@ function MemberModal({
_hover={{
bgColor: 'myGray.200'
}}
onClick={() => {
onClick={(e) => {
setParentPath(getOrgChildrenPath(org));
e.stopPropagation();
}}
/>
)}

View File

@@ -18,7 +18,7 @@ import type { TeamTagItemType } from '@fastgpt/global/support/user/team/type';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { RepeatIcon } from '@chakra-ui/icons';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useQuery } from '@tanstack/react-query';
import { getTeamsTags, loadTeamTagsByDomain } from '@/web/support/user/team/api';

View File

@@ -3,8 +3,8 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { Box, Button, Flex, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
import { NotSufficientModalType, useSystemStore } from '@/web/common/system/useSystemStore';
import ExtraPlan from '@/pages/price/components/ExtraPlan';
import StandardPlan from '@/pages/price/components/Standard';
import ExtraPlan from '@/pageComponents/price/ExtraPlan';
import StandardPlan from '@/pageComponents/price/Standard';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useUserStore } from '@/web/support/user/useUserStore';
@@ -29,7 +29,10 @@ const NotSufficientModal = ({ type }: { type: NotSufficientModalType }) => {
[TeamErrEnum.datasetSizeNotEnough]: t('common:support.wallet.Dataset_not_sufficient'),
[TeamErrEnum.datasetAmountNotEnough]: t('common:support.wallet.Dataset_amount_not_sufficient'),
[TeamErrEnum.teamMemberOverSize]: t('common:support.wallet.Team_member_over_size'),
[TeamErrEnum.appAmountNotEnough]: t('common:support.wallet.App_amount_not_sufficient')
[TeamErrEnum.appAmountNotEnough]: t('common:support.wallet.App_amount_not_sufficient'),
[TeamErrEnum.pluginAmountNotEnough]: t('common:support.wallet.App_amount_not_sufficient'),
[TeamErrEnum.websiteSyncNotEnough]: t('common:code_error.team_error.website_sync_not_enough'),
[TeamErrEnum.reRankNotEnough]: t('common:code_error.team_error.re_rank_not_enough')
};
return (

View File

@@ -1,14 +1,12 @@
import MyModal from '@fastgpt/web/components/common/MyModal';
import React, { useCallback, useEffect, useRef } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, ModalBody } from '@chakra-ui/react';
import { checkBalancePayResult } from '@/web/support/wallet/bill/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRouter } from 'next/router';
import { getErrText } from '@fastgpt/global/common/error/utils';
import LightTip from '@fastgpt/web/components/common/LightTip';
import Script from 'next/script';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import QRCode from 'qrcode';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
export type QRPayProps = {
readPrice: number;
@@ -27,71 +25,56 @@ const QRCodePayModal = ({
}: QRPayProps & { tip?: string; onSuccess?: () => any }) => {
const { t } = useTranslation();
const { toast } = useToast();
const dom = useRef<HTMLDivElement>(null);
const canvasRef = useRef<HTMLDivElement>(null);
const drawCode = useCallback(() => {
if (dom.current && window.QRCode && !dom.current.innerHTML) {
new window.QRCode(dom.current, {
text: codeUrl,
width: qrCodeSize,
height: qrCodeSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: window.QRCode.CorrectLevel.H
const canvas = document.createElement('canvas');
QRCode.toCanvas(canvas, codeUrl, {
width: qrCodeSize,
margin: 0,
color: {
dark: '#000000',
light: '#ffffff'
}
})
.then(() => {
if (canvasRef.current) {
canvasRef.current.innerHTML = '';
canvasRef.current.appendChild(canvas);
} else {
drawCode();
}
})
.catch((err) => {
console.error('QRCode generation error:', err);
});
}
}, [codeUrl]);
useEffect(() => {
let timer: NodeJS.Timeout;
const check = async () => {
try {
const res = await checkBalancePayResult(billId);
if (res) {
try {
await onSuccess?.();
toast({
title: res,
status: 'success'
});
return;
} catch (error) {
toast({
title: getErrText(error),
status: 'error'
});
}
}
} catch (error) {}
drawCode();
}, [drawCode]);
drawCode();
timer = setTimeout(check, 2000);
};
check();
return () => clearTimeout(timer);
}, [billId, drawCode, onSuccess, toast]);
useRequest2(() => checkBalancePayResult(billId), {
manual: false,
pollingInterval: 2000,
onSuccess: (res) => {
if (res) {
onSuccess?.();
}
},
errorToast: ''
});
return (
<>
<Script
src={getWebReqUrl('/js/qrcode.min.js')}
strategy="lazyOnload"
onLoad={drawCode}
></Script>
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'} pb={10} whiteSpace={'pre-wrap'}>
{tip && <LightTip text={tip} mb={8} textAlign={'left'} />}
<Box ref={dom} id={'payQRCode'} display={'inline-block'} h={`${qrCodeSize}px`}></Box>
<Box mt={5} textAlign={'center'}>
{t('common:pay.wechat', { price: readPrice })}
</Box>
</ModalBody>
</MyModal>
</>
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'} pb={10} whiteSpace={'pre-wrap'}>
{tip && <LightTip text={tip} mb={8} textAlign={'left'} />}
<Box ref={canvasRef} display={'inline-block'} h={`${qrCodeSize}px`}></Box>
<Box mt={5} textAlign={'center'}>
{t('common:pay.wechat', { price: readPrice })}
</Box>
</ModalBody>
</MyModal>
);
};

View File

@@ -1,6 +1,6 @@
import type {
LLMModelItemType,
VectorModelItemType,
EmbeddingModelItemType,
AudioSpeechModels,
STTModelType,
ReRankModelItemType
@@ -8,15 +8,15 @@ import type {
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
import { SystemDefaultModelType, SystemModelItemType } from '@fastgpt/service/core/ai/type';
export type InitDateResponse = {
bufferId?: string;
llmModels: LLMModelItemType[];
vectorModels: VectorModelItemType[];
audioSpeechModels: AudioSpeechModels[];
reRankModels: ReRankModelItemType[];
whisperModel: STTModelType;
feConfigs: FastGPTFeConfigsType;
feConfigs?: FastGPTFeConfigsType;
subPlans?: SubPlanType;
systemVersion: string;
activeModelList?: SystemModelItemType[];
defaultModels?: SystemDefaultModelType;
};

View File

@@ -27,7 +27,7 @@ import dayjs from 'dayjs';
import { useTranslation } from 'next-i18next';
import { useCallback, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider';
import Divider from '@/pageComponents/app/detail/WorkflowComponents/Flow/components/Divider';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import { InvoiceHeaderSingleForm } from './InvoiceHeaderForm';
import MyBox from '@fastgpt/web/components/common/MyBox';

View File

@@ -32,6 +32,7 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
const BillTable = () => {
const { t } = useTranslation();
const { toast } = useToast();
@@ -177,6 +178,7 @@ export default BillTable;
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
const { t } = useTranslation();
return (
<MyModal
isOpen={true}

View File

@@ -1,4 +1,4 @@
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider';
import Divider from '@/pageComponents/app/detail/WorkflowComponents/Flow/components/Divider';
import { getTeamInvoiceHeader, updateTeamInvoiceHeader } from '@/web/support/user/team/api';
import { Box, Button, Flex, HStack, Input, InputProps, Radio, RadioGroup } from '@chakra-ui/react';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import Tag from '@fastgpt/web/components/common/Tag';
import { useTranslation } from 'next-i18next';
import React, { useMemo, useState } from 'react';
import React, { useMemo, useRef, useState } from 'react';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useContextSelector } from 'use-context-selector';
import { TeamContext } from '../context';
@@ -50,6 +50,8 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
const refetchMembers = useContextSelector(TeamContext, (v) => v.refetchMembers);
const MemberScrollData = useContextSelector(TeamContext, (v) => v.MemberScrollData);
const [hoveredMemberId, setHoveredMemberId] = useState<string>();
const selectedMembersRef = useRef<HTMLDivElement>(null);
const [members, setMembers] = useState(group?.members || []);
const [searchKey, setSearchKey] = useState('');
@@ -155,7 +157,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
setSearchKey(e.target.value);
}}
/>
<MemberScrollData mt={3} flex={'1 0 0'} h={0}>
<MemberScrollData mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}>
{filtered.map((member) => {
return (
<HStack
@@ -185,7 +187,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
</Flex>
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}>
<Box mt={2}>{t('common:chosen') + ': ' + members.length}</Box>
<MemberScrollData mt={3} flex={'1 0 0'} h={0}>
<MemberScrollData ScrollContainerRef={selectedMembersRef} mt={3} flex={'1 0 0'} h={0}>
{members.map((member) => {
return (
<HStack

View File

@@ -169,8 +169,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
</Flex>
<Box flex={'1 0 0'} overflow={'auto'}>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<MemberScrollData>
<MemberScrollData>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<Table overflow={'unset'}>
<Thead>
<Tr bgColor={'white !important'}>
@@ -246,9 +246,9 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
))}
</Tbody>
</Table>
</MemberScrollData>
<ConfirmRemoveMemberModal />
</TableContainer>
<ConfirmRemoveMemberModal />
</TableContainer>
</MemberScrollData>
</Box>
<ConfirmLeaveTeamModal />

View File

@@ -121,36 +121,34 @@ function OrgMemberManageModal({
setSearchKey(e.target.value);
}}
/>
<Flex flexDirection="column" mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}>
<MemberScrollData>
{filterMembers.map((member) => {
return (
<HStack
py="2"
px={3}
borderRadius={'md'}
alignItems="center"
key={member.tmbId}
cursor={'pointer'}
_hover={{
bg: 'myGray.50',
...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {})
}}
_notLast={{ mb: 2 }}
onClick={() => handleToggleSelect(member.tmbId)}
>
<Checkbox
isChecked={!!isSelected(member.tmbId)}
icon={<CheckboxIcon name={'common/check'} />}
pointerEvents="none"
/>
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box>{member.memberName}</Box>
</HStack>
);
})}
</MemberScrollData>
</Flex>
<MemberScrollData mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}>
{filterMembers.map((member) => {
return (
<HStack
py="2"
px={3}
borderRadius={'md'}
alignItems="center"
key={member.tmbId}
cursor={'pointer'}
_hover={{
bg: 'myGray.50',
...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {})
}}
_notLast={{ mb: 2 }}
onClick={() => handleToggleSelect(member.tmbId)}
>
<Checkbox
isChecked={!!isSelected(member.tmbId)}
icon={<CheckboxIcon name={'common/check'} />}
pointerEvents="none"
/>
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box>{member.memberName}</Box>
</HStack>
);
})}
</MemberScrollData>
</Flex>
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}>
<Box mt={2}>{`${t('common:chosen')}:${selectedMembers.length}`}</Box>

View File

@@ -1,7 +1,7 @@
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import React from 'react';
import { ThirdPartyAccountType } from '../index';
import { ThirdPartyAccountType } from '../../../pages/account/thirdParty/index';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { useUserStore } from '@/web/support/user/useUserStore';

View File

@@ -0,0 +1,142 @@
import { getDashboardData } from '@/web/support/wallet/usage/api';
import { Box, Flex } from '@chakra-ui/react';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { addDays } from 'date-fns';
import { useTranslation } from 'next-i18next';
import React, { useMemo } from 'react';
import {
ResponsiveContainer,
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
TooltipProps
} from 'recharts';
import { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent';
import { UnitType, UsageFilterParams } from './type';
import dayjs from 'dayjs';
export type usageFormType = {
date: string;
totalPoints: number;
};
const CustomTooltip = ({ active, payload }: TooltipProps<ValueType, NameType>) => {
const data = payload?.[0]?.payload as usageFormType;
const { t } = useTranslation();
if (active && data) {
return (
<Box
bg={'white'}
p={3}
borderRadius={'md'}
border={'0.5px solid'}
borderColor={'myGray.200'}
boxShadow={
'0px 24px 48px -12px rgba(19, 51, 107, 0.20), 0px 0px 1px 0px rgba(19, 51, 107, 0.20)'
}
>
<Box fontSize={'mini'} color={'myGray.600'} mb={3}>
{data.date}
</Box>
<Box fontSize={'14px'} color={'myGray.900'} fontWeight={'medium'}>
{`${formatNumber(data.totalPoints)} ${t('account_usage:points')}`}
</Box>
</Box>
);
}
return null;
};
const UsageDashboard = ({
filterParams,
Tabs,
Selectors
}: {
filterParams: UsageFilterParams;
Tabs: React.ReactNode;
Selectors: React.ReactNode;
}) => {
const { t } = useTranslation();
const { dateRange, selectTmbIds, usageSources, unit, isSelectAllSource, isSelectAllTmb } =
filterParams;
const { data: totalPoints = [], loading: totalPointsLoading } = useRequest2(
() =>
getDashboardData({
dateStart: dateRange.from
? new Date(dateRange.from.setHours(0, 0, 0, 0))
: new Date(new Date().setHours(0, 0, 0, 0)),
dateEnd: dateRange.to
? new Date(addDays(dateRange.to, 1).setHours(0, 0, 0, 0))
: new Date(addDays(new Date(), 1).setHours(0, 0, 0, 0)),
sources: isSelectAllSource ? undefined : usageSources,
teamMemberIds: isSelectAllTmb ? undefined : selectTmbIds,
unit
}).then((res) =>
res.map((item) => ({
...item,
date: dayjs(item.date).format('YYYY-MM-DD')
}))
),
{
manual: false,
refreshDeps: [filterParams]
}
);
const totalUsage = useMemo(() => {
return totalPoints.reduce((acc, curr) => acc + curr.totalPoints, 0);
}, [totalPoints]);
return (
<>
<Box>{Tabs}</Box>
<Box mt={4}>{Selectors}</Box>
<MyBox overflowY={'auto'} isLoading={totalPointsLoading}>
<Flex fontSize={'20px'} fontWeight={'medium'} my={6}>
<Box color={'black'}>{`${t('account_usage:total_usage')}:`}</Box>
<Box color={'primary.600'} ml={2}>
{`${formatNumber(totalUsage)} ${t('account_usage:points')}`}
</Box>
</Flex>
<Flex mb={4} fontSize={'mini'} color={'myGray.500'} fontWeight={'medium'}>
{t('account_usage:points')}
</Flex>
<ResponsiveContainer width="100%" height={424}>
<LineChart data={totalPoints} margin={{ top: 10, right: 30, left: -12, bottom: 0 }}>
<XAxis
dataKey="date"
padding={{ left: 40, right: 40 }}
tickMargin={10}
tickSize={0}
tick={{ fontSize: '12px', color: '#667085', fontWeight: '500' }}
/>
<YAxis
axisLine={false}
tickSize={0}
tickMargin={12}
tick={{ fontSize: '12px', color: '#667085', fontWeight: '500' }}
/>
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} />
<Tooltip content={<CustomTooltip />} />
<Line
type="monotone"
dataKey="totalPoints"
stroke="#5E8FFF"
strokeWidth={2.5}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</MyBox>
</>
);
};
export default React.memo(UsageDashboard);

View File

@@ -0,0 +1,180 @@
import {
Box,
Button,
Flex,
Table,
TableContainer,
Tbody,
Td,
Th,
Thead,
Tr
} from '@chakra-ui/react';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyBox from '@fastgpt/web/components/common/MyBox';
import dayjs from 'dayjs';
import { useTranslation } from 'next-i18next';
import React, { useMemo, useState } from 'react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { getUserUsages } from '@/web/support/wallet/usage/api';
import { addDays } from 'date-fns';
import dynamic from 'next/dynamic';
import { UsageFilterParams } from './type';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { downloadFetch } from '@/web/common/system/utils';
const UsageDetail = dynamic(() => import('./UsageDetail'));
const UsageTableList = ({
filterParams,
Tabs,
Selectors
}: {
Tabs: React.ReactNode;
Selectors: React.ReactNode;
filterParams: UsageFilterParams;
}) => {
const { t } = useTranslation();
const { dateRange, selectTmbIds, isSelectAllTmb, usageSources, isSelectAllSource, projectName } =
filterParams;
const requestParams = useMemo(() => {
return {
dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1),
sources: isSelectAllSource ? undefined : usageSources,
teamMemberIds: isSelectAllTmb ? undefined : selectTmbIds,
projectName
};
}, [
dateRange.from,
dateRange.to,
isSelectAllSource,
isSelectAllTmb,
projectName,
selectTmbIds,
usageSources
]);
const {
data: usages,
isLoading,
Pagination,
total
} = usePagination(getUserUsages, {
pageSize: 20,
params: requestParams,
refreshDeps: [requestParams]
});
const [usageDetail, setUsageDetail] = useState<UsageItemType>();
const { runAsync: exportUsage } = useRequest2(
async () => {
await downloadFetch({
url: `/api/proApi/support/wallet/usage/exportUsage`,
filename: `usage.csv`,
body: {
...requestParams,
appNameMap: {
['core.app.Question Guide']: t('common:core.app.Question Guide'),
['common:support.wallet.usage.Audio Speech']: t(
'common:support.wallet.usage.Audio Speech'
),
['support.wallet.usage.Whisper']: t('common:support.wallet.usage.Whisper'),
['support.wallet.moduleName.index']: t('common:support.wallet.moduleName.index'),
['support.wallet.moduleName.qa']: t('common:support.wallet.moduleName.qa'),
['core.dataset.training.Auto mode']: t('common:core.dataset.training.Auto mode'),
['common:core.module.template.ai_chat']: t('common:core.module.template.ai_chat')
},
sourcesMap: Object.fromEntries(
Object.entries(UsageSourceMap).map(([key, config]) => [
key,
{
label: t(config.label as any)
}
])
),
title: t('account_usage:export_title')
}
});
},
{
refreshDeps: [requestParams]
}
);
return (
<>
<Box>{Tabs}</Box>
<Flex mt={4} w={'100%'}>
<Box>{Selectors}</Box>
<Box flex={'1'} />
<PopoverConfirm
Trigger={<Button size={'md'}>{t('common:Export')}</Button>}
showCancel
content={t('account_usage:export_confirm_tip', { total })}
onConfirm={exportUsage}
/>
</Flex>
<MyBox position={'relative'} overflowY={'auto'} mt={3} flex={1} isLoading={isLoading}>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th>{t('common:user.Time')}</Th>
<Th>{t('account_usage:member')}</Th>
<Th>{t('account_usage:user_type')}</Th>
<Th>{t('account_usage:project_name')}</Th>
<Th>{t('account_usage:total_points')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{usages.map((item) => (
<Tr key={item.id}>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>
<Flex alignItems={'center'} color={'myGray.500'}>
<Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} />
{item.sourceMember.name}
</Flex>
</Td>
<Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td>
<Td>{t(item.appName as any) || '-'}</Td>
<Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td>
<Button
size={'sm'}
variant={'whitePrimary'}
onClick={() => setUsageDetail(item)}
>
{t('account_usage:details')}
</Button>
</Td>
</Tr>
))}
</Tbody>
</Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
)}
</TableContainer>
</MyBox>
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
{!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
)}
</>
);
};
export default React.memo(UsageTableList);

View File

@@ -0,0 +1,13 @@
import { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
export type UnitType = 'day' | 'month';
export type UsageFilterParams = {
dateRange: DateRangeType;
selectTmbIds: string[];
isSelectAllTmb: boolean;
usageSources: UsageSourceEnum[];
isSelectAllSource: boolean;
projectName: string;
unit: UnitType;
};

View File

@@ -3,7 +3,7 @@ import { Box, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { filterSensitiveNodesData } from '@/web/core/workflow/utils';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import MyPopover from '@fastgpt/web/components/common/MyPopover';
import { fileDownload } from '@/web/common/file/utils';
import { AppChatConfigType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';

View File

@@ -1,6 +1,6 @@
import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context';
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
import { AppContext } from '@/pages/app/detail/components/context';
import { AppContext } from './context';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useI18n } from '@/web/context/I18n';
import { resumeInheritPer } from '@/web/core/app/api';

View File

@@ -11,7 +11,7 @@ import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';
import CloseIcon from '@fastgpt/web/components/common/Icon/close';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { PcHeader } from '@/pages/chat/components/ChatHeader';
import { PcHeader } from '@/pageComponents/chat/ChatHeader';
import { GetChatTypeEnum } from '@/global/core/chat/constants';
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import ChatRecordContextProvider, {

View File

@@ -6,7 +6,7 @@ import { Box, Flex, FlexProps, Grid, ModalBody, Switch, useTheme } from '@chakra
import MyRadio from '@/components/common/MyRadio';
import { useForm } from 'react-hook-form';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { fileToBase64 } from '@/web/common/file/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -28,7 +28,7 @@ import {
putShareChat
} from '@/web/support/outLink/api';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useForm } from 'react-hook-form';
import { defaultOutLinkForm } from '@/web/core/app/constants';
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
@@ -233,7 +233,7 @@ const Share = ({ appId }: { appId: string; type: PublishChannelEnum }) => {
onEdit={() => {
toast({
status: 'success',
title: t('common:common.Update Successful')
title: t('common:common.Update Success')
});
refetchShareChatList();
setEditLinkData(undefined);

View File

@@ -1,4 +1,4 @@
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { Box, Image, Flex, ModalBody } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import MyIcon from '@fastgpt/web/components/common/Icon';

View File

@@ -16,7 +16,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import TagsEditModal from '../TagsEditModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { AppContext } from '@/pages/app/detail/components/context';
import { AppContext } from '@/pageComponents/app/detail/context';
import { useContextSelector } from 'use-context-selector';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import MyModal from '@fastgpt/web/components/common/MyModal';

View File

@@ -27,7 +27,7 @@ import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import { TTSTypeEnum } from '@/web/core/app/constants';
import { workflowSystemVariables } from '@/web/core/app/utils';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import { AppContext } from '@/pageComponents/app/detail/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';
@@ -138,9 +138,16 @@ const EditForm = ({
model: appForm.aiSettings.model,
temperature: appForm.aiSettings.temperature,
maxToken: appForm.aiSettings.maxToken,
maxHistories: appForm.aiSettings.maxHistories
maxHistories: appForm.aiSettings.maxHistories,
aiChatReasoning: appForm.aiSettings.aiChatReasoning ?? true
}}
onChange={({ model, temperature, maxToken, maxHistories }: SettingAIDataType) => {
onChange={({
model,
temperature,
maxToken,
maxHistories,
aiChatReasoning = false
}) => {
setAppForm((state) => ({
...state,
aiSettings: {
@@ -148,7 +155,8 @@ const EditForm = ({
model,
temperature,
maxToken,
maxHistories: maxHistories ?? 6
maxHistories: maxHistories ?? 6,
aiChatReasoning
}
}));
}}

View File

@@ -23,7 +23,7 @@ import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { getTeamsTags } from '@/web/support/user/team/api';
import { useQuery } from '@tanstack/react-query';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import { AppContext } from './context';
const TagsEditModal = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation();

View File

@@ -8,8 +8,8 @@ import { useTranslation } from 'next-i18next';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import { useChatTest } from '@/pages/app/detail/components/useChatTest';
import { AppContext } from '@/pageComponents/app/detail/context';
import { useChatTest } from '../../useChatTest';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';

Some files were not shown because too many files have changed in this diff Show More