mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-21 03:35:36 +00:00
perf: isPc check;perf: dataset max token checker (#4872)
* perf: isPc check * perf: dataset max token checker * perf: dataset max token checker
This commit is contained in:
@@ -17,6 +17,8 @@ weight: 790
|
|||||||
1. LLM stream调用,默认超时调大。
|
1. LLM stream调用,默认超时调大。
|
||||||
2. 部分确认交互优化。
|
2. 部分确认交互优化。
|
||||||
3. 纠正原先知识库的“表格数据集”名称,改成“备份导入”。同时支持知识库索引的导出和导入。
|
3. 纠正原先知识库的“表格数据集”名称,改成“备份导入”。同时支持知识库索引的导出和导入。
|
||||||
|
4. 工作流知识库引用上限,如果工作流中没有相关 AI 节点,则交互模式改成纯手动输入,并且上限为 1000万。
|
||||||
|
5. 语音输入,移动端判断逻辑,准确判断是否为手机,而不是小屏。
|
||||||
|
|
||||||
## 🐛 修复
|
## 🐛 修复
|
||||||
|
|
||||||
|
@@ -60,5 +60,3 @@ export enum AppTemplateTypeEnum {
|
|||||||
// special type
|
// special type
|
||||||
contribute = 'contribute'
|
contribute = 'contribute'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultDatasetMaxTokens = 16000;
|
|
||||||
|
@@ -11,40 +11,6 @@ export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined
|
|||||||
nodes: T;
|
nodes: T;
|
||||||
isPlugin: boolean;
|
isPlugin: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
if (nodes) {
|
|
||||||
// Check dataset maxTokens
|
|
||||||
if (isPlugin) {
|
|
||||||
let maxTokens = 16000;
|
|
||||||
|
|
||||||
nodes.forEach((item) => {
|
|
||||||
if (
|
|
||||||
item.flowNodeType === FlowNodeTypeEnum.chatNode ||
|
|
||||||
item.flowNodeType === FlowNodeTypeEnum.tools
|
|
||||||
) {
|
|
||||||
const model =
|
|
||||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
|
||||||
const chatModel = getLLMModel(model);
|
|
||||||
const quoteMaxToken = chatModel.quoteMaxToken || 16000;
|
|
||||||
|
|
||||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
nodes.forEach((item) => {
|
|
||||||
if (item.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
|
|
||||||
item.inputs.forEach((input) => {
|
|
||||||
if (input.key === NodeInputKeyEnum.datasetMaxTokens) {
|
|
||||||
const val = input.value as number;
|
|
||||||
if (val > maxTokens) {
|
|
||||||
input.value = maxTokens;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodes
|
nodes
|
||||||
};
|
};
|
||||||
|
@@ -18,10 +18,10 @@ export const getWebReqUrl = (url: string = '') => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isMobile = () => {
|
export const isMobile = () => {
|
||||||
// 服务端渲染时返回 false
|
// SSR return false
|
||||||
if (typeof window === 'undefined') return false;
|
if (typeof window === 'undefined') return false;
|
||||||
|
|
||||||
// 1. 检查 User-Agent
|
// 1. Check User-Agent
|
||||||
const userAgent = navigator.userAgent.toLowerCase();
|
const userAgent = navigator.userAgent.toLowerCase();
|
||||||
const mobileKeywords = [
|
const mobileKeywords = [
|
||||||
'android',
|
'android',
|
||||||
@@ -36,12 +36,12 @@ export const isMobile = () => {
|
|||||||
];
|
];
|
||||||
const isMobileUA = mobileKeywords.some((keyword) => userAgent.includes(keyword));
|
const isMobileUA = mobileKeywords.some((keyword) => userAgent.includes(keyword));
|
||||||
|
|
||||||
// 2. 检查屏幕宽度
|
// 2. Check screen width
|
||||||
const isMobileWidth = window.innerWidth <= 900;
|
const isMobileWidth = window.innerWidth <= 900;
|
||||||
|
|
||||||
// 3. 检查是否支持触摸事件(排除触控屏PC)
|
// 3. Check if touch events are supported (exclude touch screen PCs)
|
||||||
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||||
|
|
||||||
// 综合判断:满足以下任一条件即视为移动端
|
// If any of the following conditions are met, it is considered a mobile device
|
||||||
return isMobileUA || (isMobileWidth && isTouchDevice);
|
return isMobileUA || (isMobileWidth && isTouchDevice);
|
||||||
};
|
};
|
||||||
|
@@ -25,11 +25,11 @@ import SelectAiModel from '@/components/Select/AIModelSelector';
|
|||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
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 InputSlider from '@fastgpt/web/components/common/MySlider/InputSlider';
|
||||||
import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio';
|
import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio';
|
||||||
import { type AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
|
import { type AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||||
|
|
||||||
enum SearchSettingTabEnum {
|
enum SearchSettingTabEnum {
|
||||||
searchMode = 'searchMode',
|
searchMode = 'searchMode',
|
||||||
@@ -48,7 +48,7 @@ const DatasetParamsModal = ({
|
|||||||
datasetSearchUsingExtensionQuery,
|
datasetSearchUsingExtensionQuery,
|
||||||
datasetSearchExtensionModel,
|
datasetSearchExtensionModel,
|
||||||
datasetSearchExtensionBg,
|
datasetSearchExtensionBg,
|
||||||
maxTokens = defaultDatasetMaxTokens,
|
maxTokens,
|
||||||
onClose,
|
onClose,
|
||||||
onSuccess
|
onSuccess
|
||||||
}: AppDatasetSearchParamsType & {
|
}: AppDatasetSearchParamsType & {
|
||||||
@@ -130,7 +130,7 @@ const DatasetParamsModal = ({
|
|||||||
|
|
||||||
// 保证只有 80 左右个刻度。
|
// 保证只有 80 左右个刻度。
|
||||||
const maxTokenStep = useMemo(() => {
|
const maxTokenStep = useMemo(() => {
|
||||||
if (maxTokens < 8000) return 80;
|
if (!maxTokens || maxTokens < 8000) return 80;
|
||||||
return Math.ceil(maxTokens / 80 / 100) * 100;
|
return Math.ceil(maxTokens / 80 / 100) * 100;
|
||||||
}, [maxTokens]);
|
}, [maxTokens]);
|
||||||
|
|
||||||
@@ -301,16 +301,27 @@ const DatasetParamsModal = ({
|
|||||||
<QuestionTip label={t('common:max_quote_tokens_tips')} />
|
<QuestionTip label={t('common:max_quote_tokens_tips')} />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box flex={'1 0 0'}>
|
<Box flex={'1 0 0'}>
|
||||||
<InputSlider
|
{maxTokens ? (
|
||||||
min={100}
|
<InputSlider
|
||||||
max={maxTokens}
|
min={100}
|
||||||
step={maxTokenStep}
|
max={maxTokens}
|
||||||
value={getValues(NodeInputKeyEnum.datasetMaxTokens) ?? 1000}
|
step={maxTokenStep}
|
||||||
onChange={(val) => {
|
value={getValues(NodeInputKeyEnum.datasetMaxTokens) ?? 1000}
|
||||||
setValue(NodeInputKeyEnum.datasetMaxTokens, val);
|
onChange={(val) => {
|
||||||
setRefresh(!refresh);
|
setValue(NodeInputKeyEnum.datasetMaxTokens, val);
|
||||||
}}
|
setRefresh(!refresh);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MyNumberInput
|
||||||
|
size={'sm'}
|
||||||
|
min={100}
|
||||||
|
max={1000000}
|
||||||
|
step={100}
|
||||||
|
register={register}
|
||||||
|
name={NodeInputKeyEnum.datasetMaxTokens}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@@ -214,7 +214,8 @@ const MobileVoiceInput = ({
|
|||||||
const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
||||||
({ onSendMessage, resetInputVal }, ref) => {
|
({ onSendMessage, resetInputVal }, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isPc = !isMobile();
|
const isMobileDevice = isMobile();
|
||||||
|
const { isPc } = useSystem();
|
||||||
|
|
||||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||||
@@ -265,10 +266,10 @@ const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPc) {
|
if (isMobileDevice) {
|
||||||
renderAudioGraphPc(analyser, canvas);
|
|
||||||
} else {
|
|
||||||
renderAudioGraphMobile(analyser, canvas);
|
renderAudioGraphMobile(analyser, canvas);
|
||||||
|
} else {
|
||||||
|
renderAudioGraphPc(analyser, canvas);
|
||||||
}
|
}
|
||||||
animationFrameId = window.requestAnimationFrame(renderCurve);
|
animationFrameId = window.requestAnimationFrame(renderCurve);
|
||||||
};
|
};
|
||||||
@@ -283,7 +284,7 @@ const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
|||||||
source.disconnect();
|
source.disconnect();
|
||||||
analyser.disconnect();
|
analyser.disconnect();
|
||||||
};
|
};
|
||||||
}, [stream, canvasRef, renderAudioGraphPc, renderAudioGraphMobile, isPc]);
|
}, [stream, canvasRef, renderAudioGraphPc, renderAudioGraphMobile, isMobileDevice]);
|
||||||
|
|
||||||
const onStartSpeak = useCallback(() => {
|
const onStartSpeak = useCallback(() => {
|
||||||
const finishWhisperTranscription = (text: string) => {
|
const finishWhisperTranscription = (text: string) => {
|
||||||
@@ -301,12 +302,12 @@ const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
|||||||
}, [autoTTSResponse, onSendMessage, resetInputVal, startSpeak, whisperConfig?.autoSend]);
|
}, [autoTTSResponse, onSendMessage, resetInputVal, startSpeak, whisperConfig?.autoSend]);
|
||||||
|
|
||||||
const onSpeach = useCallback(() => {
|
const onSpeach = useCallback(() => {
|
||||||
if (isPc) {
|
if (isMobileDevice) {
|
||||||
onStartSpeak();
|
|
||||||
} else {
|
|
||||||
setMobilePreSpeak(true);
|
setMobilePreSpeak(true);
|
||||||
|
} else {
|
||||||
|
onStartSpeak();
|
||||||
}
|
}
|
||||||
}, [isPc, onStartSpeak]);
|
}, [isMobileDevice, onStartSpeak]);
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
onSpeak: onSpeach
|
onSpeak: onSpeach
|
||||||
}));
|
}));
|
||||||
@@ -328,13 +329,7 @@ const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
|||||||
borderRadius={isPc ? 'md' : ''}
|
borderRadius={isPc ? 'md' : ''}
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
{isPc ? (
|
{isMobileDevice ? (
|
||||||
<PCVoiceInput
|
|
||||||
speakingTimeString={speakingTimeString}
|
|
||||||
stopSpeak={stopSpeak}
|
|
||||||
canvasRef={canvasRef}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<MobileVoiceInput
|
<MobileVoiceInput
|
||||||
isSpeaking={isSpeaking}
|
isSpeaking={isSpeaking}
|
||||||
onStartSpeak={onStartSpeak}
|
onStartSpeak={onStartSpeak}
|
||||||
@@ -342,6 +337,12 @@ const VoiceInput = forwardRef<VoiceInputComponentRef, VoiceInputProps>(
|
|||||||
stopSpeak={stopSpeak}
|
stopSpeak={stopSpeak}
|
||||||
canvasRef={canvasRef}
|
canvasRef={canvasRef}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<PCVoiceInput
|
||||||
|
speakingTimeString={speakingTimeString}
|
||||||
|
stopSpeak={stopSpeak}
|
||||||
|
canvasRef={canvasRef}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isTransCription && (
|
{isTransCription && (
|
||||||
|
@@ -24,6 +24,8 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
|||||||
import ValueTypeLabel from './render/ValueTypeLabel';
|
import ValueTypeLabel from './render/ValueTypeLabel';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||||
|
import InputSlider from '@fastgpt/web/components/common/MySlider/InputSlider';
|
||||||
|
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||||
|
|
||||||
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -32,35 +34,58 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
|
|
||||||
const CustomComponent = useMemo(() => {
|
const CustomComponent = useMemo(() => {
|
||||||
const quoteList = inputs.filter((item) => item.canEdit);
|
const quoteList = inputs.filter((item) => item.canEdit);
|
||||||
const tokenLimit = (() => {
|
const maxTokens = (() => {
|
||||||
let maxTokens = 16000;
|
let maxTokens = 0;
|
||||||
|
|
||||||
nodeList.forEach((item) => {
|
nodeList.forEach((item) => {
|
||||||
if ([FlowNodeTypeEnum.chatNode, FlowNodeTypeEnum.tools].includes(item.flowNodeType)) {
|
if ([FlowNodeTypeEnum.chatNode, FlowNodeTypeEnum.tools].includes(item.flowNodeType)) {
|
||||||
const model =
|
const model =
|
||||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
||||||
const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken || 16000;
|
const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken || 0;
|
||||||
|
|
||||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return maxTokens;
|
return maxTokens ? maxTokens : undefined;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const maxTokenStep = (() => {
|
||||||
|
if (!maxTokens || maxTokens < 8000) return 80;
|
||||||
|
return Math.ceil(maxTokens / 80 / 100) * 100;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[NodeInputKeyEnum.datasetMaxTokens]: (item: FlowNodeInputItemType) => (
|
[NodeInputKeyEnum.datasetMaxTokens]: (item: FlowNodeInputItemType) =>
|
||||||
<Box px={2}>
|
maxTokens ? (
|
||||||
<MySlider
|
<Box px={2} bg={'white'} py={2} border={'base'} borderRadius={'md'}>
|
||||||
markList={[
|
<InputSlider
|
||||||
{ label: '100', value: 100 },
|
min={100}
|
||||||
{ label: tokenLimit, value: tokenLimit }
|
max={maxTokens}
|
||||||
]}
|
step={maxTokenStep}
|
||||||
width={'100%'}
|
value={item.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
onChangeNode({
|
||||||
|
nodeId,
|
||||||
|
type: 'updateInput',
|
||||||
|
key: item.key,
|
||||||
|
value: {
|
||||||
|
...item,
|
||||||
|
value: e
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<MyNumberInput
|
||||||
|
size={'sm'}
|
||||||
min={100}
|
min={100}
|
||||||
max={tokenLimit}
|
max={1000000}
|
||||||
step={50}
|
step={100}
|
||||||
value={item.value}
|
value={item.value}
|
||||||
|
name={NodeInputKeyEnum.datasetMaxTokens}
|
||||||
|
bg={'white'}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -73,8 +98,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
),
|
||||||
),
|
|
||||||
[NodeInputKeyEnum.datasetQuoteList]: (item: FlowNodeInputItemType) => {
|
[NodeInputKeyEnum.datasetQuoteList]: (item: FlowNodeInputItemType) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@@ -12,7 +12,6 @@ import SearchParamsTip from '@/components/core/dataset/SearchParamsTip';
|
|||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '@/pageComponents/app/detail/WorkflowComponents/context';
|
import { WorkflowContext } from '@/pageComponents/app/detail/WorkflowComponents/context';
|
||||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||||
import { defaultDatasetMaxTokens } from '@fastgpt/global/core/app/constants';
|
|
||||||
import { type AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
|
import { type AppDatasetSearchParamsType } from '@fastgpt/global/core/app/type';
|
||||||
|
|
||||||
const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
||||||
@@ -36,19 +35,19 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const tokenLimit = useMemo(() => {
|
const tokenLimit = useMemo(() => {
|
||||||
let maxTokens = defaultDatasetMaxTokens;
|
let maxTokens = 0;
|
||||||
|
|
||||||
nodeList.forEach((item) => {
|
nodeList.forEach((item) => {
|
||||||
if ([FlowNodeTypeEnum.chatNode, FlowNodeTypeEnum.tools].includes(item.flowNodeType)) {
|
if ([FlowNodeTypeEnum.chatNode, FlowNodeTypeEnum.tools].includes(item.flowNodeType)) {
|
||||||
const model =
|
const model =
|
||||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
||||||
const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken || defaultDatasetMaxTokens;
|
const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken ?? 0;
|
||||||
|
|
||||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return maxTokens;
|
return maxTokens ? maxTokens : undefined;
|
||||||
}, [nodeList]);
|
}, [nodeList]);
|
||||||
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
Reference in New Issue
Block a user