Add polish flag to ChatDispatchProps and AppSimpleEditFormType

This commit is contained in:
刘丰瑞 2024-02-29 15:54:55 +08:00
parent b74dd2341b
commit 93cfef8aa9
No known key found for this signature in database
GPG Key ID: BAB7435C0B9927B8
25 changed files with 298 additions and 8 deletions

View File

@ -86,6 +86,7 @@ export type AppSimpleEditFormType = {
}[];
}[];
questionGuide: boolean;
polish: boolean;
tts: {
type: 'none' | 'web' | 'model';
model?: string | undefined;
@ -121,6 +122,7 @@ export type AppSimpleEditConfigTemplateType = {
welcomeText?: boolean;
variables?: boolean;
questionGuide?: boolean;
polish?: boolean;
tts?: boolean;
};
};

View File

@ -31,6 +31,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
welcomeText: '',
variables: [],
questionGuide: false,
polish: true,
tts: {
type: 'web'
}
@ -116,13 +117,14 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => {
target?.inputs?.find((item) => item.key === ModuleInputKeyEnum.answerText)?.value || '';
}
} else if (module.flowType === FlowNodeTypeEnum.userGuide) {
const { welcomeText, variableModules, questionGuide, ttsConfig } = splitGuideModule(
const { welcomeText, variableModules, questionGuide, polish, ttsConfig } = splitGuideModule(
getGuideModule(modules)
);
defaultAppForm.userGuide = {
welcomeText: welcomeText,
variables: variableModules,
questionGuide: questionGuide,
polish: polish,
tts: ttsConfig
};
}

View File

@ -33,6 +33,7 @@ export enum ModuleInputKeyEnum {
history = 'history',
userChatInput = 'userChatInput',
questionGuide = 'questionGuide',
polish = 'polish',
tts = 'tts',
answerText = 'text',
agents = 'agents', // cq agent key

View File

@ -120,6 +120,7 @@ export type ChatDispatchProps = {
responseChatItemId?: string;
histories: ChatItemType[];
variables: Record<string, any>;
polish: boolean;
stream: boolean;
detail: boolean; // response detail
};

View File

@ -24,6 +24,9 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => {
!!guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.questionGuide)?.value ||
false;
const polish: boolean =
!!guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.polish)?.value || false;
const ttsConfig: AppTTSConfigType = guideModules?.inputs?.find(
(item) => item.key === ModuleInputKeyEnum.tts
)?.value || { type: 'web' };
@ -32,6 +35,7 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => {
welcomeText,
variableModules,
questionGuide,
polish,
ttsConfig
};
};

View File

@ -118,10 +118,12 @@ export function responseWrite({
res,
write,
event,
polish = false,
data
}: {
res?: NextApiResponse;
write?: (text: string) => void;
polish?: boolean;
event?: string;
data: string;
}) {
@ -130,5 +132,8 @@ export function responseWrite({
if (!Write) return;
event && Write(`event: ${event}\n`);
Write(`data: ${data}\n\n`);
if (!polish || event !== sseResponseEventEnum.response) {
Write(`data: ${data}\n\n`);
}
}

View File

@ -62,6 +62,7 @@ export const iconPaths = {
'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'),
'core/app/markLight': () => import('./icons/core/app/markLight.svg'),
'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'),
'core/app/polish': () => import('./icons/core/app/polish.svg'),
'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'),
'core/app/simpleMode/chat': () => import('./icons/core/app/simpleMode/chat.svg'),
'core/app/simpleMode/dataset': () => import('./icons/core/app/simpleMode/dataset.svg'),

View File

@ -0,0 +1 @@
<svg t="1708948611921" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1458" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M564.672 116.608a52.736 52.736 0 1 0-105.408 0v131.84a52.736 52.736 0 0 0 105.408 0v-131.84zM116.608 459.264a52.736 52.736 0 0 0 0 105.408h131.84a52.736 52.736 0 1 0 0-105.408h-131.84zM193.6 193.536a52.736 52.736 0 0 1 74.56 0l96.64 96.64a52.736 52.736 0 0 1-74.56 74.496l-96.64-96.64a52.736 52.736 0 0 1 0-74.496zM830.336 268.096a52.736 52.736 0 0 0-74.56-74.56l-96.64 96.64a52.736 52.736 0 0 0 74.56 74.496l96.64-96.64zM364.8 659.2a52.736 52.736 0 0 1 0 74.496l-96.64 96.64a52.736 52.736 0 0 1-74.56-74.56l96.64-96.64a52.736 52.736 0 0 1 74.56 0zM512 459.264c5.888 0 11.648 0.96 17.152 2.88l394.816 131.584a52.736 52.736 0 0 1 6.912 97.152l-160 80-80 160a52.736 52.736 0 0 1-97.152-6.912l-131.584-394.88a52.608 52.608 0 0 1 49.792-69.824z m141.504 310.656l30.912-61.888a52.736 52.736 0 0 1 23.616-23.616l61.888-30.912-174.592-58.24 58.176 174.656z" fill="#d4237a" p-id="1459"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -22,7 +22,8 @@
"welcomeText": true,
"variables": false,
"questionGuide": false,
"polish": true,
"tts": true
}
}
}
}

View File

@ -262,6 +262,8 @@
"Max tokens": "回复上限",
"Name and avatar": "头像 & 名称",
"Next Step Guide": "下一步指引",
"Ai Polish": "AI 回复润色",
"Ai Polish Tip": "使用AI润色回复内容衔接多回复节点的workflow以及并使回复风格统一。",
"Question Guide": "问题引导",
"Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。",
"Quote prompt": "引用模板提示词",
@ -1501,4 +1503,4 @@
}
}
}
}
}

View File

@ -0,0 +1,23 @@
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react';
import React from 'react';
import { useTranslation } from 'next-i18next';
const PolishSwitch = (props: SwitchProps) => {
const { t } = useTranslation();
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/polish'} mr={2} w={'20px'} />
<Box>{t('core.app.Ai Polish')}</Box>
<MyTooltip label={t('core.app.Ai Polish Tip')} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Switch {...props} />
</Flex>
);
};
export default PolishSwitch;

View File

@ -14,6 +14,7 @@ import Container from '../modules/Container';
import NodeCard from '../render/NodeCard';
import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
import QGSwitch from '@/components/core/module/Flow/components/modules/QGSwitch';
import PolishSwitch from '@/components/core/module/Flow/components/modules/PolishSwitch';
import TTSSelect from '@/components/core/module/Flow/components/modules/TTSSelect';
import { splitGuideModule } from '@fastgpt/global/core/module/utils';
import { useTranslation } from 'next-i18next';
@ -34,6 +35,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
<Box mt={3} pt={3} borderTop={theme.borders.base}>
<QuestionGuide data={data} />
</Box>
<Box mt={3} pt={3} borderTop={theme.borders.base}>
<Polish data={data} />
</Box>
</Container>
</NodeCard>
</>
@ -143,6 +147,35 @@ function QuestionGuide({ data }: { data: FlowModuleItemType }) {
);
}
function Polish({ data }: { data: FlowModuleItemType }) {
const { inputs, moduleId } = data;
const polish = useMemo(
() =>
(inputs.find((item) => item.key === ModuleInputKeyEnum.polish)?.value as boolean) || false,
[inputs]
);
return (
<PolishSwitch
isChecked={polish}
size={'lg'}
onChange={(e) => {
const value = e.target.checked;
onChangeNode({
moduleId,
key: ModuleInputKeyEnum.polish,
type: 'updateInput',
value: {
...inputs.find((item) => item.key === ModuleInputKeyEnum.polish),
value
}
});
}}
/>
);
}
function TTSGuide({ data }: { data: FlowModuleItemType }) {
const { inputs, moduleId } = data;
const { ttsConfig } = splitGuideModule({ inputs } as ModuleItemType);

View File

@ -26,6 +26,7 @@ export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateTy
welcomeText: true,
variables: true,
questionGuide: true,
polish: true,
tts: true
}
}

View File

@ -60,3 +60,20 @@ Human"{{question}}"
`;
export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
export const Prompt_AIPolish = `你需要将 <对话记录></对话记录> 作为背景信息对 <本次回复></本次回复> 进行润色改写,你仅需返回改写后的结果,无需回答问题。
<润色要求>
1. <本次回复>
2. <对话记录>
3. 使Markdown<本次回复>Markdown
4. <本次回复>
</润色要求>
<对话记录>
{{text}}
</对话记录>
<本次回复>
"{{question}}"
</本次回复>
`;

View File

@ -11,12 +11,15 @@ import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { dispatchModules } from '@/service/moduleDispatch';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/module/utils';
import { aiPolish } from '@/service/events/aiPolish';
export type Props = {
history: ChatItemType[];
prompt: string;
modules: ModuleItemType[];
variables: Record<string, any>;
polish: boolean;
appId: string;
appName: string;
};
@ -31,6 +34,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
let { modules = [], history = [], prompt, variables = {}, appName, appId } = req.body as Props;
let { polish } = splitGuideModule(getGuideModule(modules));
try {
await connectToDatabase();
if (!history || !modules || !prompt) {
@ -53,7 +58,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */
const { responseData, moduleDispatchBills } = await dispatchModules({
const { responseData, answerText, moduleDispatchBills } = await dispatchModules({
res,
mode: 'test',
teamId,
@ -62,6 +67,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
appId,
modules,
variables,
polish,
histories: history,
startParams: {
userChatInput: prompt
@ -70,13 +76,34 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
detail: true
});
// ai polish
if (answerText && polish) {
await aiPolish({
res,
teamId,
tmbId,
user,
appId,
variables,
polish: false,
histories: history,
stream: true,
detail: true,
mode: 'test',
userChatInput: prompt,
answerText: answerText
});
}
responseWrite({
res,
polish,
event: sseResponseEventEnum.answer,
data: '[DONE]'
});
responseWrite({
res,
polish,
event: sseResponseEventEnum.appStreamResponse,
data: JSON.stringify(responseData)
});

View File

@ -18,6 +18,7 @@ import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink'
import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
import requestIp from 'request-ip';
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/module/utils';
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
import { selectSimpleChatResponse } from '@/utils/service/core/chat';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
@ -31,6 +32,7 @@ import { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { aiPolish } from '@/service/events/aiPolish';
type FastGptWebChatProps = {
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
@ -113,7 +115,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
throw new Error('Question is empty');
}
/*
/*
1. auth app permission
2. auth balance
3. get app
@ -150,6 +152,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
});
})();
// get polish config
const { polish } = splitGuideModule(getGuideModule(app.modules));
// get and concat history
const { history } = await getChatItems({
appId: app._id,
@ -161,7 +166,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
/* start flow controller */
const { responseData, moduleDispatchBills, answerText } = await dispatchModules({
let { responseData, moduleDispatchBills, answerText } = await dispatchModules({
res,
mode: 'chat',
user,
@ -172,6 +177,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
responseChatItemId,
modules: app.modules,
variables,
polish,
histories: concatHistories,
startParams: {
userChatInput: question.value
@ -180,6 +186,25 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
detail
});
// ai polish
if (answerText && polish) {
answerText = await aiPolish({
res,
teamId: String(teamId),
tmbId: String(tmbId),
user,
appId: String(app._id),
variables,
polish: false,
histories: history,
stream: true,
detail: true,
mode: 'test',
userChatInput: question.value,
answerText: answerText
});
}
// save chat
if (chatId) {
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
@ -189,6 +214,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
teamId,
tmbId: tmbId,
variables,
polish: polish || false,
updateUseTime: isOwnerUse, // owner update use time
shareId,
outLinkUid: outLinkUserId,
@ -227,6 +253,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (stream) {
responseWrite({
res,
polish,
event: detail ? sseResponseEventEnum.answer : undefined,
data: textAdaptGptResponse({
text: null,
@ -235,6 +262,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
});
responseWrite({
res,
polish,
event: detail ? sseResponseEventEnum.answer : undefined,
data: '[DONE]'
});
@ -242,6 +270,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (responseDetail && detail) {
responseWrite({
res,
polish,
event: sseResponseEventEnum.appStreamResponse,
data: JSON.stringify(feResponseData)
});

View File

@ -35,6 +35,7 @@ import SelectAiModel from '@/components/Select/SelectAiModel';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/module/utils';
import SearchParamsTip from '@/components/core/dataset/SearchParamsTip';
import PolishSwitch from '@/components/core/module/Flow/components/modules/PolishSwitch';
const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
@ -402,6 +403,19 @@ const EditForm = ({
}}
/>
</Box>
{/* polish guide */}
<Box {...BoxStyles} borderBottom={'none'}>
<PolishSwitch
isChecked={getValues('userGuide.polish')}
size={'lg'}
onChange={(e) => {
const value = e.target.checked;
setValue('userGuide.polish', value);
setRefresh((state) => !state);
}}
/>
</Box>
</Box>
</Box>

View File

@ -0,0 +1,63 @@
import { dispatchChatCompletion } from '../moduleDispatch/chat/oneapi';
import { Prompt_AIPolish } from '@/global/core/prompt/agent';
import { ChatDispatchProps } from '@fastgpt/global/core/module/type';
import { ModuleOutputKeyEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { replaceVariable } from '@fastgpt/global/common/string/tools';
export async function aiPolish({
res,
mode,
teamId,
tmbId,
user,
appId,
responseChatItemId,
histories,
variables,
polish,
stream,
detail,
userChatInput,
answerText
}: ChatDispatchProps & {
[ModuleInputKeyEnum.userChatInput]: string;
[ModuleOutputKeyEnum.answerText]: string;
}) {
const polishModel = global.llmModels[0].model;
userChatInput = replaceVariable(Prompt_AIPolish, {
text: `${histories.map((item) => `${item.obj}:${item.value}`).join('\n')}
Human: ${userChatInput}`,
question: answerText
});
const { answerText: polishedAnswerText } = await dispatchChatCompletion({
res,
stream,
detail,
user,
histories,
polish,
params: {
model: polishModel,
temperature: 0,
maxToken: 4000,
history: 6,
quoteQA: [],
userChatInput,
isResponseAnswerText: true,
systemPrompt: '',
quoteTemplate: '',
quotePrompt: ''
},
mode: mode,
teamId: teamId,
tmbId: tmbId,
appId: appId,
variables: variables,
outputs: [],
inputs: []
});
return polishedAnswerText;
}

View File

@ -45,6 +45,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
detail = false,
user,
histories,
polish,
module: { name, outputs },
params: {
model,
@ -157,6 +158,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
const { answer } = await streamResponse({
res,
detail,
polish,
stream: response
});
// count tokens
@ -165,7 +167,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
value: answer
});
targetResponse({ res, detail, outputs });
targetResponse({ res, polish, detail, outputs });
return {
answerText: answer,
@ -327,10 +329,12 @@ function getMaxTokens({
function targetResponse({
res,
outputs,
polish,
detail
}: {
res: NextApiResponse;
outputs: ModuleItemType['outputs'];
polish: boolean;
detail: boolean;
}) {
const targets =
@ -339,6 +343,7 @@ function targetResponse({
if (targets.length === 0) return;
responseWrite({
res,
polish,
event: detail ? sseResponseEventEnum.answer : undefined,
data: textAdaptGptResponse({
text: '\n'
@ -349,10 +354,12 @@ function targetResponse({
async function streamResponse({
res,
detail,
polish,
stream
}: {
res: NextApiResponse;
detail: boolean;
polish: boolean;
stream: StreamChatType;
}) {
const write = responseWriteController({
@ -370,6 +377,7 @@ async function streamResponse({
responseWrite({
write,
polish,
event: detail ? sseResponseEventEnum.answer : undefined,
data: textAdaptGptResponse({
text: content

View File

@ -58,6 +58,7 @@ export async function dispatchModules({
histories = [],
startParams = {},
variables = {},
polish,
user,
stream = false,
detail = false,
@ -202,6 +203,7 @@ export async function dispatchModules({
...props,
res,
variables,
polish,
histories,
user,
stream,
@ -331,16 +333,19 @@ function loadModules(
/* sse response modules staus */
export function responseStatus({
res,
polish,
status,
name
}: {
res: NextApiResponse;
polish?: boolean;
status?: 'running' | 'finish';
name?: string;
}) {
if (!name) return;
responseWrite({
res,
polish,
event: sseResponseEventEnum.moduleStatus,
data: JSON.stringify({
status: 'running',

View File

@ -15,6 +15,7 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
res,
detail,
stream,
polish,
params: { text = '' }
} = props as AnswerProps;
@ -23,6 +24,7 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
if (stream) {
responseWrite({
res,
polish,
event: detail ? sseResponseEventEnum.response : undefined,
data: textAdaptGptResponse({
text: `\n${formatText}`

View File

@ -28,6 +28,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
res,
teamId,
stream,
polish,
detail,
histories,
params: { userChatInput, history, app }
@ -50,6 +51,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
if (stream) {
responseWrite({
res,
polish,
event: detail ? sseResponseEventEnum.answer : undefined,
data: textAdaptGptResponse({
text: '\n'

View File

@ -13,6 +13,7 @@ type Props = {
teamId: string;
tmbId: string;
variables?: Record<string, any>;
polish?: boolean;
updateUseTime: boolean;
source: `${ChatSourceEnum}`;
shareId?: string;
@ -27,6 +28,7 @@ export async function saveChat({
teamId,
tmbId,
variables,
polish,
updateUseTime,
source,
shareId,
@ -78,6 +80,7 @@ export async function saveChat({
tmbId,
appId,
variables,
polish,
title,
source,
shareId,

View File

@ -52,6 +52,16 @@ export const appTemplates: (AppItemType & {
showTargetInPlugin: false,
connected: false
},
{
key: 'polish',
valueType: 'boolean',
value: true,
type: 'switch',
label: '',
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'tts',
type: 'hidden',
@ -336,6 +346,15 @@ export const appTemplates: (AppItemType & {
value: false,
connected: false
},
{
key: 'polish',
valueType: 'boolean',
type: 'switch',
label: '',
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'tts',
type: 'hidden',
@ -813,6 +832,15 @@ export const appTemplates: (AppItemType & {
value: false,
connected: false
},
{
key: 'polish',
valueType: 'boolean',
type: 'switch',
label: '',
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'tts',
type: 'hidden',
@ -1709,6 +1737,15 @@ export const appTemplates: (AppItemType & {
showTargetInPlugin: false,
connected: false
},
{
key: 'polish',
valueType: 'boolean',
type: 'switch',
label: '',
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'tts',
type: 'hidden',

View File

@ -32,6 +32,12 @@ export async function postForm2Modules(data: AppSimpleEditFormType) {
label: 'core.app.Question Guide',
value: formData.userGuide.questionGuide
},
{
key: ModuleInputKeyEnum.polish,
type: FlowNodeInputTypeEnum.hidden,
label: 'core.app.Question Guide',
value: formData.userGuide.polish
},
{
key: ModuleInputKeyEnum.tts,
type: FlowNodeInputTypeEnum.hidden,