Skip to content

Commit

Permalink
Update userselect ux (#2610)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
c121914yu authored Sep 4, 2024
1 parent 85a11d0 commit 64708ea
Show file tree
Hide file tree
Showing 21 changed files with 1,076 additions and 942 deletions.
27 changes: 14 additions & 13 deletions docSite/content/zh-cn/docs/development/upgrading/4810.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,17 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
22. 优化 - 知识库详情页 UI。
23. 优化 - 支持无网络配置情况下运行。
24. 优化 - 部分全局变量,增加数据类型约束。
25. 修复 - 全局变量 key 可能重复。
26. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
27. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
28. 修复 - 选择 Milvus 部署时,无法导出知识库。
29. 修复 - 创建 APP 副本,无法复制系统配置。
30. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
31. 修复 - 内容提取的数据类型与输出数据类型未一致。
32. 修复 - 工作流运行时间统计错误。
33. 修复 - stream 模式下,工具调用有可能出现 undefined
34. 修复 - 全局变量在 API 中无法持久化。
35. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
36. 修复 - 知识库标签重复加载。
37. 修复 - Debug 模式下,循环调用边问题。
25. 优化 - 查看工作流详情,切换 tab 时,自动滚动到顶部。
26. 修复 - 全局变量 key 可能重复。
27. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
28. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
29. 修复 - 选择 Milvus 部署时,无法导出知识库。
30. 修复 - 创建 APP 副本,无法复制系统配置。
31. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
32. 修复 - 内容提取的数据类型与输出数据类型未一致。
33. 修复 - 工作流运行时间统计错误。
34. 修复 - stream 模式下,工具调用有可能出现 undefined
35. 修复 - 全局变量在 API 中无法持久化。
36. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
37. 修复 - 知识库标签重复加载。
38. 修复 - Debug 模式下,循环调用边问题。
20 changes: 18 additions & 2 deletions packages/global/core/chat/adapt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const chats2GPTMessages = ({
tool_calls
})
.concat(toolResponse);
} else if (value.text) {
} else if (value.text?.content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
Expand All @@ -142,7 +142,7 @@ export const GPTMessages2Chats = (
messages: ChatCompletionMessageParam[],
reserveTool = true
): ChatItemType[] => {
return messages
const chatMessages = messages
.map((item) => {
const value: ChatItemType['value'] = [];
const obj = GPT2Chat[item.role];
Expand Down Expand Up @@ -288,6 +288,22 @@ export const GPTMessages2Chats = (
} as ChatItemType;
})
.filter((item) => item.value.length > 0);

// Merge data with the same dataId
const result = chatMessages.reduce((result: ChatItemType[], currentItem) => {
const lastItem = result[result.length - 1];

if (lastItem && lastItem.dataId === currentItem.dataId && lastItem.obj === currentItem.obj) {
// @ts-ignore
lastItem.value = lastItem.value.concat(currentItem.value);
} else {
result.push(currentItem);
}

return result;
}, []);

return result;
};

export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): RuntimeUserPromptType => {
Expand Down
1 change: 1 addition & 0 deletions packages/global/core/workflow/runtime/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum DispatchNodeResponseKeyEnum {
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses', // assistant response
rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite

interactive = 'INTERACTIVE' // is interactive
}
Expand Down
9 changes: 5 additions & 4 deletions packages/global/core/workflow/runtime/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import {
ChatItemType,
UserChatItemValueItemType,
ChatItemValueItemType,
ToolRunResponseItemType,
NodeOutputItemType
NodeOutputItemType,
AIChatItemValueItemType
} from '../../chat/type';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
import { StoreNodeItemType } from '../type/node';
Expand Down Expand Up @@ -173,13 +173,14 @@ export type DispatchNodeResponseType = {
updateVarResult?: any[];
};

export type DispatchNodeResultType<T> = {
export type DispatchNodeResultType<T = {}> = {
[DispatchNodeResponseKeyEnum.skipHandleId]?: string[]; // skip some edge handle id
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; // Node total usage
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[]; // Children node response
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType; // Tool response
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[]; // Assistant response(Store to db)
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // Assistant response(Store to db)
[DispatchNodeResponseKeyEnum.rewriteHistories]?: ChatItemType[];
} & T;

/* Single node props */
Expand Down
20 changes: 14 additions & 6 deletions packages/global/core/workflow/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,24 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
return limit * 2;
};

/*
Get interaction information (if any) from the last AI message.
What can be done:
1. Get the interactive data
2. Check that the workflow starts at the interaction node
*/
export const getLastInteractiveValue = (histories: ChatItemType[]) => {
const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI);

if (lastAIMessage) {
const interactiveValue = lastAIMessage.value.find(
(v) => v.type === ChatItemValueTypeEnum.interactive
);

if (interactiveValue && 'interactive' in interactiveValue) {
return interactiveValue.interactive;
const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1];

if (
lastValue &&
lastValue.type === ChatItemValueTypeEnum.interactive &&
!!lastValue.interactive
) {
return lastValue.interactive;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type InteractiveBasicType = {
type UserSelectInteractive = {
type: 'userSelect';
params: {
// description: string;
description: string;
userSelectOptions: UserSelectOptionItemType[];
userSelectedVal?: string;
};
Expand Down
3 changes: 2 additions & 1 deletion packages/service/core/ai/functions/createQuestionGuide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,6 +19,7 @@ export async function createQuestionGuide({
content: Prompt_QuestionGuide
}
];

const ai = getAIApi({
timeout: 480000
});
Expand Down
48 changes: 1 addition & 47 deletions packages/service/core/chat/controller.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand Down
88 changes: 87 additions & 1 deletion packages/service/core/chat/saveChat.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
}
);
});
};
42 changes: 41 additions & 1 deletion packages/service/core/chat/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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[];
};
Loading

0 comments on commit 64708ea

Please sign in to comment.