4.8.10 perf (#2630)

* perf: i18n init

* i18n

* fix: user select end status

* fix: interactive workflow

* fix: restart chat

* fix: oauth login
This commit is contained in:
Archer
2024-09-06 15:45:02 +08:00
committed by GitHub
parent c614f8b9ca
commit 9334a0dcf6
14 changed files with 174 additions and 134 deletions

View File

@@ -40,9 +40,17 @@ export const getLastInteractiveValue = (histories: ChatItemType[]) => {
const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1]; const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1];
if ( if (
lastValue && !lastValue ||
lastValue.type === ChatItemValueTypeEnum.interactive && lastValue.type !== ChatItemValueTypeEnum.interactive ||
!!lastValue.interactive !lastValue.interactive
) {
return null;
}
// Check is user select
if (
lastValue.interactive.type === 'userSelect' &&
!lastValue.interactive.params.userSelectedVal
) { ) {
return lastValue.interactive; return lastValue.interactive;
} }

View File

@@ -25,7 +25,7 @@ import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { AIChatItemType } from '@fastgpt/global/core/chat/type'; import { AIChatItemType } from '@fastgpt/global/core/chat/type';
import { updateToolInputValue } from './utils'; import { updateToolInputValue } from './utils';
import { computedMaxToken, computedTemperature } from '../../../../ai/utils'; import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools'; import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
import { addLog } from '../../../../../common/system/log'; import { addLog } from '../../../../../common/system/log';
type ToolRunResponseType = { type ToolRunResponseType = {
@@ -367,6 +367,7 @@ async function streamResponse({
}); });
let textAnswer = ''; let textAnswer = '';
let callingTool: { name: string; arguments: string } | null = null;
let toolCalls: ChatCompletionMessageToolCall[] = []; let toolCalls: ChatCompletionMessageToolCall[] = [];
for await (const part of stream) { for await (const part of stream) {
@@ -390,69 +391,71 @@ async function streamResponse({
}); });
} else if (responseChoice?.tool_calls?.[0]) { } else if (responseChoice?.tool_calls?.[0]) {
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0]; const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool // In a stream response, only one tool is returned at a time. If have id, description is executing a tool
if (toolCall.id) { if (toolCall.id || callingTool) {
const toolNode = toolNodes.find((item) => item.nodeId === toolCall.function?.name); // Start call tool
if (toolCall.id) {
callingTool = {
name: toolCall.function.name || '',
arguments: toolCall.function.arguments || ''
};
} else if (callingTool) {
// Continue call
callingTool.name += toolCall.function.name || '';
callingTool.arguments += toolCall.function.arguments || '';
}
const toolFunction = callingTool!;
const toolNode = toolNodes.find((item) => item.nodeId === toolFunction.name);
if (toolNode) { if (toolNode) {
if (toolCall.function?.arguments === undefined) { // New tool, add to list.
toolCall.function.arguments = ''; const toolId = getNanoid();
} toolCalls.push({
...toolCall,
id: toolId,
function: toolFunction,
toolName: toolNode.name,
toolAvatar: toolNode.avatar
});
// Get last tool call workflowStreamResponse?.({
const lastToolCall = toolCalls[toolCalls.length - 1]; event: SseResponseEventEnum.toolCall,
data: {
// new tool tool: {
if (lastToolCall?.id !== toolCall.id) { id: toolId,
toolCalls.push({ toolName: toolNode.name,
...toolCall, toolAvatar: toolNode.avatar,
toolName: toolNode.name, functionName: toolFunction.name,
toolAvatar: toolNode.avatar params: toolFunction?.arguments ?? '',
}); response: ''
workflowStreamResponse?.({
event: SseResponseEventEnum.toolCall,
data: {
tool: {
id: toolCall.id,
toolName: toolNode.name,
toolAvatar: toolNode.avatar,
functionName: toolCall.function.name,
params: toolCall.function.arguments,
response: ''
}
} }
});
continue;
}
// last tool, update params
} else {
continue;
}
}
/* arg 插入最后一个工具的参数里 */
const arg: string = toolCall?.function?.arguments ?? '';
const currentTool = toolCalls[toolCalls.length - 1];
if (currentTool) {
currentTool.function.arguments += arg;
workflowStreamResponse?.({
write,
event: SseResponseEventEnum.toolParams,
data: {
tool: {
id: currentTool.id,
toolName: '',
toolAvatar: '',
params: arg,
response: ''
} }
} });
}); callingTool = null;
}
} else {
/* arg 插入最后一个工具的参数里 */
const arg: string = toolCall?.function?.arguments ?? '';
const currentTool = toolCalls[toolCalls.length - 1];
if (currentTool && arg) {
currentTool.function.arguments += arg;
workflowStreamResponse?.({
write,
event: SseResponseEventEnum.toolParams,
data: {
tool: {
id: currentTool.id,
toolName: '',
toolAvatar: '',
params: arg,
response: ''
}
}
});
}
} }
} }
} }

View File

@@ -137,6 +137,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
let chatNodeUsages: ChatNodeUsageType[] = []; let chatNodeUsages: ChatNodeUsageType[] = [];
let toolRunResponse: ToolRunResponseItemType; let toolRunResponse: ToolRunResponseItemType;
let debugNextStepRunNodes: RuntimeNodeItemType[] = []; let debugNextStepRunNodes: RuntimeNodeItemType[] = [];
// 记录交互节点,交互节点需要在工作流完全结束后再进行计算
let workflowInteractiveResponse:
| {
entryNodeIds: string[];
interactiveResponse: UserSelectInteractive;
}
| undefined;
/* Store special response field */ /* Store special response field */
function pushStore( function pushStore(
@@ -338,6 +345,16 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
if (!nodeRunResult) return []; if (!nodeRunResult) return [];
// In the current version, only one interactive node is allowed at the same time
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
if (interactiveResponse) {
workflowInteractiveResponse = {
entryNodeIds: [nodeRunResult.node.nodeId],
interactiveResponse
};
return [];
}
// Update the node output at the end of the run and get the next nodes // Update the node output at the end of the run and get the next nodes
let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput( let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput(
nodeRunResult.node, nodeRunResult.node,
@@ -351,18 +368,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index (node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
); );
// In the current version, only one interactive node is allowed at the same time
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
if (interactiveResponse) {
chatAssistantResponse.push(
handleInteractiveResult({
entryNodeIds: [nodeRunResult.node.nodeId],
interactiveResponse
})
);
return [];
}
// Run next nodes先运行 run 的,再运行 skip 的) // Run next nodes先运行 run 的,再运行 skip 的)
const nextStepActiveNodesResults = ( const nextStepActiveNodesResults = (
await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node))) await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node)))
@@ -543,6 +548,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
await nodeRunWithActive(pluginOutputModule); await nodeRunWithActive(pluginOutputModule);
} }
// Interactive node
if (workflowInteractiveResponse) {
const interactiveResult = handleInteractiveResult({
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
interactiveResponse: workflowInteractiveResponse.interactiveResponse
});
chatAssistantResponse.push(interactiveResult);
}
return { return {
flowResponses: chatResponses, flowResponses: chatResponses,
flowUsages: chatNodeUsages, flowUsages: chatNodeUsages,

View File

@@ -715,11 +715,13 @@ export const theme = extendTheme({
lg: '1px solid #D0E0E2' lg: '1px solid #D0E0E2'
}, },
radii: { radii: {
xs: '4px', none: '0',
sm: '6px', xs: '0.25rem',
md: '8px', sm: '0.375rem',
lg: '12px', md: '0.5rem',
xl: '16px' semilg: '0.625rem',
lg: '0.75rem',
xl: '1rem'
}, },
shadows: { shadows: {
1: '0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)', 1: '0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)',

View File

@@ -1,5 +1,5 @@
# --------- install dependence ----------- # --------- install dependence -----------
FROM node:20.14.0-alpine AS mainDeps FROM node:20.14.0-alpine AS maindeps
WORKDIR /app WORKDIR /app
ARG proxy ARG proxy
@@ -26,10 +26,10 @@ ARG proxy
# copy common node_modules and one project node_modules # copy common node_modules and one project node_modules
COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./ COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./
COPY --from=mainDeps /app/node_modules ./node_modules COPY --from=maindeps /app/node_modules ./node_modules
COPY --from=mainDeps /app/packages ./packages COPY --from=maindeps /app/packages ./packages
COPY ./projects/app ./projects/app COPY ./projects/app ./projects/app
COPY --from=mainDeps /app/projects/app/node_modules ./projects/app/node_modules COPY --from=maindeps /app/projects/app/node_modules ./projects/app/node_modules
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
@@ -63,9 +63,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/projects/app/.next/server/chunks
COPY --from=builder --chown=nextjs:nodejs /app/projects/app/.next/server/worker /app/projects/app/.next/server/worker COPY --from=builder --chown=nextjs:nodejs /app/projects/app/.next/server/worker /app/projects/app/.next/server/worker
# copy standload packages # copy standload packages
COPY --from=mainDeps /app/node_modules/tiktoken ./node_modules/tiktoken COPY --from=maindeps /app/node_modules/tiktoken ./node_modules/tiktoken
RUN rm -rf ./node_modules/tiktoken/encoders RUN rm -rf ./node_modules/tiktoken/encoders
COPY --from=mainDeps /app/node_modules/@zilliz/milvus2-sdk-node ./node_modules/@zilliz/milvus2-sdk-node COPY --from=maindeps /app/node_modules/@zilliz/milvus2-sdk-node ./node_modules/@zilliz/milvus2-sdk-node
# copy package.json to version file # copy package.json to version file

View File

@@ -45,7 +45,11 @@ import ChatBoxDivider from '../../Divider';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { checkIsInteractiveByHistories, formatChatValue2InputType } from './utils'; import {
checkIsInteractiveByHistories,
formatChatValue2InputType,
setUserSelectResultToHistories
} from './utils';
import { textareaMinH } from './constants'; import { textareaMinH } from './constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import ChatProvider, { ChatBoxContext, ChatProviderProps } from './Provider'; import ChatProvider, { ChatBoxContext, ChatProviderProps } from './Provider';
@@ -93,14 +97,6 @@ type Props = OutLinkChatAuthProps &
onDelMessage?: (e: { contentId: string }) => void; onDelMessage?: (e: { contentId: string }) => void;
}; };
/*
The input is divided into sections
1. text
2. img
3. file
4. ....
*/
const ChatBox = ( const ChatBox = (
{ {
feedbackType = FeedbackTypeEnum.hidden, feedbackType = FeedbackTypeEnum.hidden,
@@ -377,7 +373,13 @@ const ChatBox = (
* user confirm send prompt * user confirm send prompt
*/ */
const sendPrompt: SendPromptFnType = useCallback( const sendPrompt: SendPromptFnType = useCallback(
({ text = '', files = [], history = chatHistories, autoTTSResponse = false }) => { ({
text = '',
files = [],
history = chatHistories,
autoTTSResponse = false,
isInteractivePrompt = false
}) => {
variablesForm.handleSubmit( variablesForm.handleSubmit(
async (variables) => { async (variables) => {
if (!onStartChat) return; if (!onStartChat) return;
@@ -444,6 +446,7 @@ const ChatBox = (
] as UserChatItemValueItemType[], ] as UserChatItemValueItemType[],
status: 'finish' status: 'finish'
}, },
// 普通 chat 模式,需要增加一个 AI 来接收响应消息
{ {
dataId: responseChatId, dataId: responseChatId,
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
@@ -459,20 +462,26 @@ const ChatBox = (
} }
]; ];
const isInteractive = checkIsInteractiveByHistories(history);
// Update histories(Interactive input does not require new session rounds) // Update histories(Interactive input does not require new session rounds)
setChatHistories(isInteractive ? newChatList.slice(0, -2) : newChatList); setChatHistories(
isInteractivePrompt
? // 把交互的结果存储到对话记录中,交互模式下,不需要新的会话轮次
setUserSelectResultToHistories(newChatList.slice(0, -2), text)
: newChatList
);
// 清空输入内容 // 清空输入内容
resetInputVal({}); resetInputVal({});
setQuestionGuide([]); setQuestionGuide([]);
scrollToBottom('smooth', 100); scrollToBottom('smooth', 100);
try { try {
// create abort obj // create abort obj
const abortSignal = new AbortController(); const abortSignal = new AbortController();
chatController.current = abortSignal; chatController.current = abortSignal;
// Last empty ai message will be removed // 最后一条 AI 消息是空的,会被过滤掉,这里得到的 messages不会包含最后一条 AI 消息,所以不需要 slice 了。
// 这里,无论是否为交互模式,最后都是 Human 的消息。
const messages = chats2GPTMessages({ messages: newChatList, reserveId: true }); const messages = chats2GPTMessages({ messages: newChatList, reserveId: true });
const { const {
@@ -480,7 +489,7 @@ const ChatBox = (
responseText, responseText,
isNewChat = false isNewChat = false
} = await onStartChat({ } = await onStartChat({
messages: messages, messages, // 保证最后一条是 Human 的消息
responseChatItemId: responseChatId, responseChatItemId: responseChatId,
controller: abortSignal, controller: abortSignal,
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }), generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
@@ -847,12 +856,6 @@ const ChatBox = (
abortRequest(); abortRequest();
setValue('chatStarted', false); setValue('chatStarted', false);
scrollToBottom('smooth', 500); scrollToBottom('smooth', 500);
},
scrollToBottom,
sendPrompt: (question: string) => {
sendPrompt({
text: question
});
} }
})); }));

View File

@@ -27,20 +27,16 @@ export type ChatBoxInputFormType = {
export type ChatBoxInputType = { export type ChatBoxInputType = {
text?: string; text?: string;
files?: UserInputFileItemType[]; files?: UserInputFileItemType[];
isInteractivePrompt?: boolean;
}; };
export type SendPromptFnType = ({ export type SendPromptFnType = (
text, e: ChatBoxInputType & {
files, autoTTSResponse?: boolean;
history, history?: ChatSiteItemType[];
autoTTSResponse }
}: ChatBoxInputType & { ) => void;
autoTTSResponse?: boolean;
history?: ChatSiteItemType[];
}) => void;
export type ComponentRef = { export type ComponentRef = {
restartChat: () => void; restartChat: () => void;
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
sendPrompt: (question: string) => void;
}; };

View File

@@ -52,7 +52,9 @@ export const checkIsInteractiveByHistories = (chatHistories: ChatSiteItemType[])
return ( return (
lastMessageValue.type === ChatItemValueTypeEnum.interactive && lastMessageValue.type === ChatItemValueTypeEnum.interactive &&
!!lastMessageValue?.interactive?.params !!lastMessageValue?.interactive?.params &&
// 如果用户选择了,则不认为是交互模式(可能是上一轮以交互结尾,发起的新的一轮对话)
!lastMessageValue?.interactive?.params?.userSelectedVal
); );
}; };

View File

@@ -49,6 +49,7 @@ export const useChat = () => {
ChatBoxRef.current?.restartChat?.(); ChatBoxRef.current?.restartChat?.();
}, [variablesForm]); }, [variablesForm]);
return { return {
ChatBoxRef, ChatBoxRef,
chatRecords, chatRecords,

View File

@@ -22,7 +22,6 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import { SendPromptFnType } from '../ChatContainer/ChatBox/type'; import { SendPromptFnType } from '../ChatContainer/ChatBox/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider'; import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
import { setUserSelectResultToHistories } from '../ChatContainer/ChatBox/utils';
import { InteractiveNodeResponseItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type'; import { InteractiveNodeResponseItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
@@ -167,7 +166,7 @@ const RenderInteractive = React.memo(
onClick={() => { onClick={() => {
onSendMessage?.({ onSendMessage?.({
text: option.value, text: option.value,
history: setUserSelectResultToHistories(chatHistories, option.value) isInteractivePrompt: true
}); });
}} }}
> >

View File

@@ -57,12 +57,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
chatConfig chatConfig
} = req.body as Props; } = req.body as Props;
try { try {
if (!Array.isArray(nodes)) {
throw new Error('Nodes is not array');
}
if (!Array.isArray(edges)) {
throw new Error('Edges is not array');
}
const chatMessages = GPTMessages2Chats(messages); const chatMessages = GPTMessages2Chats(messages);
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length); // console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
/* user auth */ /* user auth */
const [{ app }, { teamId, tmbId }] = await Promise.all([ const [{ app }, { teamId, tmbId }] = await Promise.all([
authApp({ req, authToken: true, appId, per: ReadPermissionVal }), authApp({ req, authToken: true, appId, per: ReadPermissionVal }),
@@ -76,13 +81,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const isPlugin = app.type === AppTypeEnum.plugin; const isPlugin = app.type === AppTypeEnum.plugin;
if (!Array.isArray(nodes)) {
throw new Error('Nodes is not array');
}
if (!Array.isArray(edges)) {
throw new Error('Edges is not array');
}
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, chatMessages)); let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, chatMessages));
// Plugin need to replace inputs // Plugin need to replace inputs

View File

@@ -1,5 +1,5 @@
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import React from 'react'; import React, { useMemo } from 'react';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type'; import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import { streamFetch } from '@/web/common/api/fetch'; import { streamFetch } from '@/web/common/api/fetch';
import { getMaxHistoryLimitFromNodes } from '@fastgpt/global/core/workflow/runtime/utils'; import { getMaxHistoryLimitFromNodes } from '@fastgpt/global/core/workflow/runtime/utils';
@@ -14,9 +14,9 @@ import dynamic from 'next/dynamic';
import { useChat } from '@/components/core/chat/ChatContainer/useChat'; import { useChat } from '@/components/core/chat/ChatContainer/useChat';
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox')); const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
const ChatBox = dynamic(() => import('@/components/core/chat/ChatContainer/ChatBox'));
export const useChatTest = ({ export const useChatTest = ({
nodes, nodes,
@@ -56,8 +56,10 @@ export const useChatTest = ({
} }
); );
const pluginInputs = const pluginInputs = useMemo(() => {
nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || []; return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
}, [nodes]);
const { const {
ChatBoxRef, ChatBoxRef,
chatRecords, chatRecords,

View File

@@ -11,7 +11,6 @@ import Loading from '@fastgpt/web/components/common/MyLoading';
import { serviceSideProps } from '@/web/common/utils/i18n'; import { serviceSideProps } from '@/web/common/utils/i18n';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useMount } from 'ahooks';
const provider = () => { const provider = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -104,9 +103,15 @@ const provider = () => {
} else { } else {
authCode(code); authCode(code);
} }
}, [code, error, loginStore, state]); }, []);
return <Loading />; return <Loading />;
}; };
export default provider; export default provider;
export async function getServerSideProps(context: any) {
return {
props: { ...(await serviceSideProps(context)) }
};
}

View File

@@ -8,6 +8,7 @@ import { ssoLogin } from '@/web/support/user/api';
import Loading from '@fastgpt/web/components/common/MyLoading'; import Loading from '@fastgpt/web/components/common/MyLoading';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { serviceSideProps } from '@/web/common/utils/i18n';
const provider = () => { const provider = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -39,9 +40,15 @@ const provider = () => {
clearToken(); clearToken();
handleSSO(); handleSSO();
} }
}, [handleSSO, query]); }, []);
return <Loading />; return <Loading />;
}; };
export default provider; export default provider;
export async function getServerSideProps(context: any) {
return {
props: { ...(await serviceSideProps(context)) }
};
}