mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
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:
@@ -1,5 +1,5 @@
|
||||
# --------- install dependence -----------
|
||||
FROM node:20.14.0-alpine AS mainDeps
|
||||
FROM node:20.14.0-alpine AS maindeps
|
||||
WORKDIR /app
|
||||
|
||||
ARG proxy
|
||||
@@ -26,10 +26,10 @@ ARG proxy
|
||||
|
||||
# copy common node_modules and one project node_modules
|
||||
COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./
|
||||
COPY --from=mainDeps /app/node_modules ./node_modules
|
||||
COPY --from=mainDeps /app/packages ./packages
|
||||
COPY --from=maindeps /app/node_modules ./node_modules
|
||||
COPY --from=maindeps /app/packages ./packages
|
||||
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
|
||||
|
||||
@@ -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 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
|
||||
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
|
||||
|
@@ -45,7 +45,11 @@ import ChatBoxDivider from '../../Divider';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
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 { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import ChatProvider, { ChatBoxContext, ChatProviderProps } from './Provider';
|
||||
@@ -93,14 +97,6 @@ type Props = OutLinkChatAuthProps &
|
||||
onDelMessage?: (e: { contentId: string }) => void;
|
||||
};
|
||||
|
||||
/*
|
||||
The input is divided into sections
|
||||
1. text
|
||||
2. img
|
||||
3. file
|
||||
4. ....
|
||||
*/
|
||||
|
||||
const ChatBox = (
|
||||
{
|
||||
feedbackType = FeedbackTypeEnum.hidden,
|
||||
@@ -377,7 +373,13 @@ const ChatBox = (
|
||||
* user confirm send prompt
|
||||
*/
|
||||
const sendPrompt: SendPromptFnType = useCallback(
|
||||
({ text = '', files = [], history = chatHistories, autoTTSResponse = false }) => {
|
||||
({
|
||||
text = '',
|
||||
files = [],
|
||||
history = chatHistories,
|
||||
autoTTSResponse = false,
|
||||
isInteractivePrompt = false
|
||||
}) => {
|
||||
variablesForm.handleSubmit(
|
||||
async (variables) => {
|
||||
if (!onStartChat) return;
|
||||
@@ -444,6 +446,7 @@ const ChatBox = (
|
||||
] as UserChatItemValueItemType[],
|
||||
status: 'finish'
|
||||
},
|
||||
// 普通 chat 模式,需要增加一个 AI 来接收响应消息
|
||||
{
|
||||
dataId: responseChatId,
|
||||
obj: ChatRoleEnum.AI,
|
||||
@@ -459,20 +462,26 @@ const ChatBox = (
|
||||
}
|
||||
];
|
||||
|
||||
const isInteractive = checkIsInteractiveByHistories(history);
|
||||
// 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({});
|
||||
setQuestionGuide([]);
|
||||
scrollToBottom('smooth', 100);
|
||||
|
||||
try {
|
||||
// create abort obj
|
||||
const abortSignal = new AbortController();
|
||||
chatController.current = abortSignal;
|
||||
|
||||
// Last empty ai message will be removed
|
||||
// 最后一条 AI 消息是空的,会被过滤掉,这里得到的 messages,不会包含最后一条 AI 消息,所以不需要 slice 了。
|
||||
// 这里,无论是否为交互模式,最后都是 Human 的消息。
|
||||
const messages = chats2GPTMessages({ messages: newChatList, reserveId: true });
|
||||
|
||||
const {
|
||||
@@ -480,7 +489,7 @@ const ChatBox = (
|
||||
responseText,
|
||||
isNewChat = false
|
||||
} = await onStartChat({
|
||||
messages: messages,
|
||||
messages, // 保证最后一条是 Human 的消息
|
||||
responseChatItemId: responseChatId,
|
||||
controller: abortSignal,
|
||||
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
|
||||
@@ -847,12 +856,6 @@ const ChatBox = (
|
||||
abortRequest();
|
||||
setValue('chatStarted', false);
|
||||
scrollToBottom('smooth', 500);
|
||||
},
|
||||
scrollToBottom,
|
||||
sendPrompt: (question: string) => {
|
||||
sendPrompt({
|
||||
text: question
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
|
@@ -27,20 +27,16 @@ export type ChatBoxInputFormType = {
|
||||
export type ChatBoxInputType = {
|
||||
text?: string;
|
||||
files?: UserInputFileItemType[];
|
||||
isInteractivePrompt?: boolean;
|
||||
};
|
||||
|
||||
export type SendPromptFnType = ({
|
||||
text,
|
||||
files,
|
||||
history,
|
||||
autoTTSResponse
|
||||
}: ChatBoxInputType & {
|
||||
autoTTSResponse?: boolean;
|
||||
history?: ChatSiteItemType[];
|
||||
}) => void;
|
||||
export type SendPromptFnType = (
|
||||
e: ChatBoxInputType & {
|
||||
autoTTSResponse?: boolean;
|
||||
history?: ChatSiteItemType[];
|
||||
}
|
||||
) => void;
|
||||
|
||||
export type ComponentRef = {
|
||||
restartChat: () => void;
|
||||
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
||||
sendPrompt: (question: string) => void;
|
||||
};
|
||||
|
@@ -52,7 +52,9 @@ export const checkIsInteractiveByHistories = (chatHistories: ChatSiteItemType[])
|
||||
|
||||
return (
|
||||
lastMessageValue.type === ChatItemValueTypeEnum.interactive &&
|
||||
!!lastMessageValue?.interactive?.params
|
||||
!!lastMessageValue?.interactive?.params &&
|
||||
// 如果用户选择了,则不认为是交互模式(可能是上一轮以交互结尾,发起的新的一轮对话)
|
||||
!lastMessageValue?.interactive?.params?.userSelectedVal
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -49,6 +49,7 @@ export const useChat = () => {
|
||||
|
||||
ChatBoxRef.current?.restartChat?.();
|
||||
}, [variablesForm]);
|
||||
|
||||
return {
|
||||
ChatBoxRef,
|
||||
chatRecords,
|
||||
|
@@ -22,7 +22,6 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { SendPromptFnType } from '../ChatContainer/ChatBox/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
|
||||
import { setUserSelectResultToHistories } from '../ChatContainer/ChatBox/utils';
|
||||
import { InteractiveNodeResponseItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
@@ -167,7 +166,7 @@ const RenderInteractive = React.memo(
|
||||
onClick={() => {
|
||||
onSendMessage?.({
|
||||
text: option.value,
|
||||
history: setUserSelectResultToHistories(chatHistories, option.value)
|
||||
isInteractivePrompt: true
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
@@ -57,12 +57,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
chatConfig
|
||||
} = req.body as Props;
|
||||
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 userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
|
||||
|
||||
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);
|
||||
|
||||
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
|
||||
|
||||
/* user auth */
|
||||
const [{ app }, { teamId, tmbId }] = await Promise.all([
|
||||
authApp({ req, authToken: true, appId, per: ReadPermissionVal }),
|
||||
@@ -76,13 +81,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
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));
|
||||
|
||||
// Plugin need to replace inputs
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 { streamFetch } from '@/web/common/api/fetch';
|
||||
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 { Box } from '@chakra-ui/react';
|
||||
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 ChatBox = dynamic(() => import('@/components/core/chat/ChatContainer/ChatBox'));
|
||||
|
||||
export const useChatTest = ({
|
||||
nodes,
|
||||
@@ -56,8 +56,10 @@ export const useChatTest = ({
|
||||
}
|
||||
);
|
||||
|
||||
const pluginInputs =
|
||||
nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
|
||||
const pluginInputs = useMemo(() => {
|
||||
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
|
||||
}, [nodes]);
|
||||
|
||||
const {
|
||||
ChatBoxRef,
|
||||
chatRecords,
|
||||
|
@@ -11,7 +11,6 @@ import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMount } from 'ahooks';
|
||||
|
||||
const provider = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -104,9 +103,15 @@ const provider = () => {
|
||||
} else {
|
||||
authCode(code);
|
||||
}
|
||||
}, [code, error, loginStore, state]);
|
||||
}, []);
|
||||
|
||||
return <Loading />;
|
||||
};
|
||||
|
||||
export default provider;
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: { ...(await serviceSideProps(context)) }
|
||||
};
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import { ssoLogin } from '@/web/support/user/api';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
|
||||
const provider = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -39,9 +40,15 @@ const provider = () => {
|
||||
clearToken();
|
||||
handleSSO();
|
||||
}
|
||||
}, [handleSSO, query]);
|
||||
}, []);
|
||||
|
||||
return <Loading />;
|
||||
};
|
||||
|
||||
export default provider;
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: { ...(await serviceSideProps(context)) }
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user