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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
|
@@ -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)',
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -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;
|
|
||||||
};
|
};
|
||||||
|
@@ -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
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -49,6 +49,7 @@ export const useChat = () => {
|
|||||||
|
|
||||||
ChatBoxRef.current?.restartChat?.();
|
ChatBoxRef.current?.restartChat?.();
|
||||||
}, [variablesForm]);
|
}, [variablesForm]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ChatBoxRef,
|
ChatBoxRef,
|
||||||
chatRecords,
|
chatRecords,
|
||||||
|
@@ -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
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
|
@@ -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)) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@@ -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)) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user