mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 20:37:48 +00:00
Update userselect ux (#2610)
* perf: user select ux and api * perf: http variables replace code * perf: http variables replace code * perf: chat box question guide adapt interactive * remove comment
This commit is contained in:
@@ -3,7 +3,7 @@ import { getAIApi } from '../config';
|
||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
||||
import { loadRequestMessages } from '../../chat/utils';
|
||||
|
||||
export const Prompt_QuestionGuide = `你是一个AI智能助手,可以回答和解决我的问题。请结合前面的对话记录,帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
|
||||
export const Prompt_QuestionGuide = `你是一个AI智能助手,可以回答和解决我的问题。请结合前面的对话记录,帮我生成 3 个问题,引导我继续提问,生成问题的语言要与原问题相同。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
|
||||
|
||||
export async function createQuestionGuide({
|
||||
messages,
|
||||
@@ -19,6 +19,7 @@ export async function createQuestionGuide({
|
||||
content: Prompt_QuestionGuide
|
||||
}
|
||||
];
|
||||
|
||||
const ai = getAIApi({
|
||||
timeout: 480000
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { MongoChatItem } from './chatItemSchema';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { MongoChat } from './chatSchema';
|
||||
@@ -80,52 +80,6 @@ export const addCustomFeedbacks = async ({
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Update the user selected index of the interactive module
|
||||
*/
|
||||
export const updateUserSelectedResult = async ({
|
||||
appId,
|
||||
chatId,
|
||||
userSelectedVal
|
||||
}: {
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
userSelectedVal: string;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
try {
|
||||
const chatItem = await MongoChatItem.findOne(
|
||||
{ appId, chatId, obj: ChatRoleEnum.AI },
|
||||
'value'
|
||||
).sort({ _id: -1 });
|
||||
|
||||
if (!chatItem) return;
|
||||
|
||||
const interactiveValue = chatItem.value.find(
|
||||
(v) => v.type === ChatItemValueTypeEnum.interactive
|
||||
);
|
||||
|
||||
if (
|
||||
!interactiveValue ||
|
||||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!interactiveValue.interactive?.params
|
||||
)
|
||||
return;
|
||||
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal
|
||||
}
|
||||
};
|
||||
|
||||
await chatItem.save();
|
||||
} catch (error) {
|
||||
addLog.error('updateUserSelectedResult error', error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Delete chat files
|
||||
1. ChatId: Delete one chat files
|
||||
|
@@ -1,6 +1,10 @@
|
||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { MongoApp } from '../app/schema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
ChatItemValueTypeEnum,
|
||||
ChatRoleEnum,
|
||||
ChatSourceEnum
|
||||
} from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoChatItem } from './chatItemSchema';
|
||||
import { MongoChat } from './chatSchema';
|
||||
import { addLog } from '../../common/system/log';
|
||||
@@ -111,3 +115,85 @@ export async function saveChat({
|
||||
addLog.error(`update chat history error`, error);
|
||||
}
|
||||
}
|
||||
|
||||
export const updateInteractiveChat = async ({
|
||||
chatId,
|
||||
appId,
|
||||
teamId,
|
||||
tmbId,
|
||||
userSelectedVal,
|
||||
aiResponse,
|
||||
newVariables,
|
||||
newTitle
|
||||
}: {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
userSelectedVal: string;
|
||||
aiResponse: AIChatItemType & { dataId?: string };
|
||||
newVariables?: Record<string, any>;
|
||||
newTitle: string;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
|
||||
const chatItem = await MongoChatItem.findOne({ appId, chatId, obj: ChatRoleEnum.AI }).sort({
|
||||
_id: -1
|
||||
});
|
||||
|
||||
if (!chatItem || chatItem.obj !== ChatRoleEnum.AI) return;
|
||||
|
||||
const interactiveValue = chatItem.value[chatItem.value.length - 1];
|
||||
|
||||
if (
|
||||
!interactiveValue ||
|
||||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!interactiveValue.interactive?.params
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal
|
||||
}
|
||||
};
|
||||
|
||||
if (aiResponse.customFeedbacks) {
|
||||
chatItem.customFeedbacks = chatItem.customFeedbacks
|
||||
? [...chatItem.customFeedbacks, ...aiResponse.customFeedbacks]
|
||||
: aiResponse.customFeedbacks;
|
||||
}
|
||||
|
||||
if (aiResponse.responseData) {
|
||||
chatItem.responseData = chatItem.responseData
|
||||
? [...chatItem.responseData, ...aiResponse.responseData]
|
||||
: aiResponse.responseData;
|
||||
}
|
||||
|
||||
if (aiResponse.value) {
|
||||
chatItem.value = chatItem.value ? [...chatItem.value, ...aiResponse.value] : aiResponse.value;
|
||||
}
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
await chatItem.save({ session });
|
||||
await MongoChat.updateOne(
|
||||
{
|
||||
appId,
|
||||
chatId
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
variables: newVariables,
|
||||
title: newTitle,
|
||||
updateTime: new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
session
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
@@ -211,11 +211,40 @@ export const loadRequestMessages = async ({
|
||||
};
|
||||
}
|
||||
}
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
|
||||
if (item.content !== undefined && !item.content) return;
|
||||
if (Array.isArray(item.content) && item.content.length === 0) return;
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
.filter(Boolean) as ChatCompletionMessageParam[];
|
||||
};
|
||||
/*
|
||||
Merge data for some consecutive roles
|
||||
1. Contiguous assistant and both have content, merge content
|
||||
*/
|
||||
const mergeConsecutiveMessages = (
|
||||
messages: ChatCompletionMessageParam[]
|
||||
): ChatCompletionMessageParam[] => {
|
||||
return messages.reduce((mergedMessages: ChatCompletionMessageParam[], currentMessage) => {
|
||||
const lastMessage = mergedMessages[mergedMessages.length - 1];
|
||||
|
||||
if (
|
||||
lastMessage &&
|
||||
currentMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
|
||||
lastMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
|
||||
typeof lastMessage.content === 'string' &&
|
||||
typeof currentMessage.content === 'string'
|
||||
) {
|
||||
lastMessage.content += currentMessage ? `\n${currentMessage.content}` : '';
|
||||
} else {
|
||||
mergedMessages.push(currentMessage);
|
||||
}
|
||||
|
||||
return mergedMessages;
|
||||
}, []);
|
||||
};
|
||||
|
||||
if (messages.length === 0) {
|
||||
return Promise.reject('core.chat.error.Messages empty');
|
||||
@@ -245,11 +274,22 @@ export const loadRequestMessages = async ({
|
||||
...item,
|
||||
content: await parseUserContent(item.content)
|
||||
};
|
||||
} else if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
|
||||
return {
|
||||
role: item.role,
|
||||
content: item.content,
|
||||
function_call: item.function_call,
|
||||
name: item.name,
|
||||
refusal: item.refusal,
|
||||
tool_calls: item.tool_calls
|
||||
};
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
})
|
||||
)) as ChatCompletionMessageParam[];
|
||||
|
||||
return clearInvalidMessages(loadMessages) as SdkChatCompletionMessageParam[];
|
||||
return mergeConsecutiveMessages(
|
||||
clearInvalidMessages(loadMessages)
|
||||
) as SdkChatCompletionMessageParam[];
|
||||
};
|
||||
|
@@ -6,6 +6,7 @@ import {
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type {
|
||||
ChatDispatchProps,
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps,
|
||||
SystemVariablesType
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
@@ -145,14 +146,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
responseData,
|
||||
nodeDispatchUsages,
|
||||
toolResponses,
|
||||
assistantResponses
|
||||
}: {
|
||||
[NodeOutputKeyEnum.answerText]?: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
|
||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[];
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // tool module, save the response value
|
||||
}
|
||||
assistantResponses,
|
||||
rewriteHistories
|
||||
}: Omit<
|
||||
DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.answerText]?: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
|
||||
}>,
|
||||
'nodeResponse'
|
||||
>
|
||||
) {
|
||||
if (responseData) {
|
||||
chatResponses.push(responseData);
|
||||
@@ -182,6 +184,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (rewriteHistories) {
|
||||
histories = rewriteHistories;
|
||||
}
|
||||
}
|
||||
/* Pass the output of the node, to get next nodes and update edge status */
|
||||
function nodeOutput(
|
||||
|
@@ -12,8 +12,6 @@ import type {
|
||||
UserSelectInteractive,
|
||||
UserSelectOptionItemType
|
||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||
import { updateUserSelectedResult } from '../../../chat/controller';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
@@ -30,6 +28,7 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
const {
|
||||
workflowStreamResponse,
|
||||
runningAppInfo: { id: appId },
|
||||
histories,
|
||||
chatId,
|
||||
node: { nodeId, isEntry },
|
||||
params: { description, userSelectOptions },
|
||||
@@ -38,21 +37,11 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (!isEntry) {
|
||||
const answerText = description ? `\n${description}` : undefined;
|
||||
if (answerText) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.fastAnswer,
|
||||
data: textAdaptGptResponse({
|
||||
text: answerText
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
[NodeOutputKeyEnum.answerText]: answerText,
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'userSelect',
|
||||
params: {
|
||||
description,
|
||||
userSelectOptions
|
||||
}
|
||||
}
|
||||
@@ -70,14 +59,8 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
};
|
||||
}
|
||||
|
||||
// Update db
|
||||
updateUserSelectedResult({
|
||||
appId,
|
||||
chatId,
|
||||
userSelectedVal
|
||||
});
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), // Removes the current session record as the history of subsequent nodes
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]: userSelectOptions
|
||||
.filter((item) => item.value !== userSelectedVal)
|
||||
.map((item: any) => getHandleId(nodeId, 'source', item.key)),
|
||||
|
@@ -99,6 +99,18 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
};
|
||||
httpReqUrl = replaceVariable(httpReqUrl, allVariables);
|
||||
|
||||
const replaceStringVariables = (text: string) => {
|
||||
return replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text,
|
||||
nodes: runtimeNodes,
|
||||
variables: allVariables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
};
|
||||
|
||||
// parse header
|
||||
const headers = await (() => {
|
||||
try {
|
||||
@@ -110,24 +122,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
if (!httpHeader || httpHeader.length === 0) return {};
|
||||
// array
|
||||
return httpHeader.reduce((acc: Record<string, string>, item) => {
|
||||
const key = replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.key,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
const value = replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.value,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
const key = replaceStringVariables(item.key);
|
||||
const value = replaceStringVariables(item.value);
|
||||
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -137,24 +133,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
})();
|
||||
|
||||
const params = httpParams.reduce((acc: Record<string, string>, item) => {
|
||||
const key = replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.key,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
const value = replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.value,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
const key = replaceStringVariables(item.key);
|
||||
const value = replaceStringVariables(item.value);
|
||||
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -165,25 +145,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
if (httpContentType === ContentTypes.formData) {
|
||||
if (!Array.isArray(httpFormBody)) return {};
|
||||
httpFormBody = httpFormBody.map((item) => ({
|
||||
key: replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.key,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
),
|
||||
key: replaceStringVariables(item.key),
|
||||
type: item.type,
|
||||
value: replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.value,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
)
|
||||
value: replaceStringVariables(item.value)
|
||||
}));
|
||||
const formData = new FormData();
|
||||
for (const { key, value } of httpFormBody) {
|
||||
@@ -194,25 +158,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
if (httpContentType === ContentTypes.xWwwFormUrlencoded) {
|
||||
if (!Array.isArray(httpFormBody)) return {};
|
||||
httpFormBody = httpFormBody.map((item) => ({
|
||||
key: replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.key,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
),
|
||||
key: replaceStringVariables(item.key),
|
||||
type: item.type,
|
||||
value: replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: item.value,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
)
|
||||
value: replaceStringVariables(item.value)
|
||||
}));
|
||||
const urlSearchParams = new URLSearchParams();
|
||||
for (const { key, value } of httpFormBody) {
|
||||
@@ -228,15 +176,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
const removeSignJson = removeUndefinedSign(jsonParse);
|
||||
return removeSignJson;
|
||||
}
|
||||
httpJsonBody = replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text: httpJsonBody,
|
||||
nodes: runtimeNodes,
|
||||
variables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
httpJsonBody = replaceStringVariables(httpJsonBody);
|
||||
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
Reference in New Issue
Block a user