Perf input guide (#1557)

* perf: input guide code

* perf: input guide ui

* Chat input guide api

* Update app chat config store

* perf: app chat config field

* perf: app context

* perf: params

* fix: ts

* perf: filter private config

* perf: filter private config

* perf: import workflow

* perf: limit max tip amount
This commit is contained in:
Archer
2024-05-21 17:52:04 +08:00
committed by GitHub
parent 8e8ceb7439
commit fb368a581c
123 changed files with 2124 additions and 1805 deletions

View File

@@ -10,6 +10,7 @@ type I18nContextType = {
publishT: TFunction<['publish'], undefined>;
workflowT: TFunction<['workflow'], undefined>;
userT: TFunction<['user'], undefined>;
chatT: TFunction<['chat'], undefined>;
};
export const I18nContext = createContext<I18nContextType>({
@@ -25,6 +26,7 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
const { t: publishT } = useTranslation('publish');
const { t: workflowT } = useTranslation('workflow');
const { t: userT } = useTranslation('user');
const { t: chatT } = useTranslation('chat');
return (
<I18nContext.Provider
@@ -35,7 +37,8 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
fileT,
publishT,
workflowT,
userT
userT,
chatT
}}
>
{children}

View File

@@ -2,7 +2,7 @@ import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
import type {
AppDetailType,
AppListItemType,
AppQuestionGuideTextConfigType
ChatInputGuideConfigType
} from '@fastgpt/global/core/app/type.d';
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
import { AppUpdateParams, CreateAppParams } from '@/global/core/app/api';
@@ -27,7 +27,7 @@ export const delModelById = (id: string) => DELETE(`/core/app/del?appId=${id}`);
/**
* 根据 ID 获取模型
*/
export const getModelById = (id: string) => GET<AppDetailType>(`/core/app/detail?appId=${id}`);
export const getAppDetailById = (id: string) => GET<AppDetailType>(`/core/app/detail?appId=${id}`);
/**
* 根据 ID 更新模型
@@ -37,19 +37,3 @@ export const putAppById = (id: string, data: AppUpdateParams) =>
// =================== chat logs
export const getAppChatLogs = (data: GetAppChatLogsParams) => POST(`/core/app/getChatLogs`, data);
/**
* 导入提示词库
*/
export const importQuestionGuides = (data: {
appId: string;
textList: string[];
customURL: string;
}) => POST(`/core/app/questionGuides/import`, data);
/**
* 获取提示词库
*/
export const getMyQuestionGuides = (
data: PaginationProps<{ appId: string; customURL: string; searchKey: string }>
) => GET<PaginationResponse<string>>(`/core/app/questionGuides/list`, data);

View File

@@ -9,6 +9,7 @@ export const defaultApp: AppDetailType = {
intro: '',
updateTime: Date.now(),
modules: [],
chatConfig: {},
teamId: '',
tmbId: '',
permission: 'private',

View File

@@ -0,0 +1,114 @@
import { Dispatch, ReactNode, SetStateAction, useCallback, useState } from 'react';
import { createContext } from 'use-context-selector';
import { defaultApp } from '../constants';
import { getAppDetailById, putAppById } from '../api';
import { useRequest } from 'ahooks';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { AppDetailType } from '@fastgpt/global/core/app/type';
import { AppUpdateParams, PostPublishAppProps } from '@/global/core/app/api';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { postPublishApp } from '../versionApi';
type AppContextType = {
appDetail: AppDetailType;
setAppDetail: Dispatch<SetStateAction<AppDetailType>>;
loadingApp: boolean;
updateAppDetail: (data: AppUpdateParams) => Promise<void>;
publishApp: (data: PostPublishAppProps) => Promise<void>;
};
export const AppContext = createContext<AppContextType>({
appDetail: defaultApp,
setAppDetail: function (value: SetStateAction<AppDetailType>): void {
throw new Error('Function not implemented.');
},
loadingApp: false,
updateAppDetail: function (data: AppUpdateParams): Promise<void> {
throw new Error('Function not implemented.');
},
publishApp: function (data: PostPublishAppProps): Promise<void> {
throw new Error('Function not implemented.');
}
});
export const AppContextProvider = ({ children, appId }: { children: ReactNode; appId: string }) => {
const { t } = useTranslation();
const router = useRouter();
const { toast } = useToast();
const [appDetail, setAppDetail] = useState(defaultApp);
const { loading } = useRequest(
() => {
if (appId) {
return getAppDetailById(appId);
}
return Promise.resolve(defaultApp);
},
{
refreshDeps: [appId],
onSuccess(res) {
setAppDetail(res);
},
onError(err: any) {
toast({
title: err?.message || t('core.app.error.Get app failed'),
status: 'error'
});
router.replace('/app/list');
}
}
);
const updateAppDetail = useCallback(
async (data: AppUpdateParams) => {
try {
await putAppById(appId, data);
setAppDetail((state) => {
return {
...state,
...data,
modules: data?.nodes || state.modules
};
});
} catch (error) {
toast({
status: 'warning',
title: getErrText(error)
});
}
},
[appId, toast]
);
const publishApp = useCallback(
async (data: PostPublishAppProps) => {
try {
await postPublishApp(appId, data);
setAppDetail((state) => {
return {
...state,
...data,
modules: data?.nodes || state.modules
};
});
} catch (error) {
toast({
status: 'warning',
title: getErrText(error)
});
}
},
[appId, toast]
);
const contextValue = {
appDetail,
setAppDetail,
loadingApp: loading,
updateAppDetail,
publishApp
};
return <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>;
};

View File

@@ -1,22 +1,12 @@
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { getMyApps, getModelById, putAppById, getMyQuestionGuides } from '@/web/core/app/api';
import type { AppUpdateParams } from '@/global/core/app/api.d';
import { AppDetailType, AppListItemType } from '@fastgpt/global/core/app/type.d';
import { PostPublishAppProps } from '@/global/core/app/api';
import { postPublishApp } from '../versionApi';
import { defaultApp } from '../constants';
import { getMyApps } from '@/web/core/app/api';
import { AppListItemType } from '@fastgpt/global/core/app/type';
type State = {
export type State = {
myApps: AppListItemType[];
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
appDetail: AppDetailType;
loadAppDetail: (id: string, init?: boolean) => Promise<AppDetailType>;
updateAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
publishApp(appId: string, data: PostPublishAppProps): Promise<void>;
clearAppModules(): void;
setAppDetail(data: AppDetailType): void;
loadMyApps: () => Promise<AppListItemType[]>;
};
export const useAppStore = create<State>()(
@@ -24,57 +14,12 @@ export const useAppStore = create<State>()(
persist(
immer((set, get) => ({
myApps: [],
async loadMyApps(init = true) {
if (get().myApps.length > 0 && !init) return [];
async loadMyApps() {
const res = await getMyApps();
set((state) => {
state.myApps = res;
});
return res;
},
appDetail: defaultApp,
async loadAppDetail(id: string, init = false) {
if (id === get().appDetail._id && !init) return get().appDetail;
const res = await getModelById(id);
set((state) => {
state.appDetail = res;
});
return res;
},
async updateAppDetail(appId: string, data: AppUpdateParams) {
await putAppById(appId, data);
set((state) => {
state.appDetail = {
...state.appDetail,
...data,
modules: data?.nodes || state.appDetail.modules
};
});
},
async publishApp(appId: string, data: PostPublishAppProps) {
await postPublishApp(appId, data);
set((state) => {
state.appDetail = {
...state.appDetail,
...data,
modules: data?.nodes || state.appDetail.modules
};
});
},
setAppDetail(data: AppDetailType) {
set((state) => {
state.appDetail = data;
});
},
clearAppModules() {
set((state) => {
state.appDetail = {
...state.appDetail,
modules: []
};
});
}
})),
{

View File

@@ -1,6 +1,6 @@
import {
AppDetailType,
AppQuestionGuideTextConfigType,
ChatInputGuideConfigType,
AppSchema,
AppSimpleEditFormType
} from '@fastgpt/global/core/app/type';
@@ -33,50 +33,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
y: -486.7611729549753
},
version: '481',
inputs: [
{
key: NodeInputKeyEnum.welcomeText,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: 'core.app.Welcome Text',
value: formData.userGuide.welcomeText
},
{
key: NodeInputKeyEnum.variables,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: 'core.app.Chat Variable',
value: formData.userGuide.variables
},
{
key: NodeInputKeyEnum.questionGuide,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: 'core.app.Question Guide',
value: formData.userGuide.questionGuide
},
{
key: NodeInputKeyEnum.tts,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.userGuide.tts
},
{
key: NodeInputKeyEnum.whisper,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.userGuide.whisper
},
{
key: NodeInputKeyEnum.scheduleTrigger,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.userGuide.scheduleTrigger
},
{
key: NodeInputKeyEnum.questionGuideText,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.userGuide.questionGuideText
}
],
inputs: [],
outputs: []
};
}
@@ -773,29 +730,6 @@ export const getAppQGuideCustomURL = (appDetail: AppDetailType | AppSchema): str
return (
appDetail?.modules
.find((m) => m.flowNodeType === FlowNodeTypeEnum.systemConfig)
?.inputs.find((i) => i.key === NodeInputKeyEnum.questionGuideText)?.value.customURL || ''
?.inputs.find((i) => i.key === NodeInputKeyEnum.chatInputGuide)?.value.customUrl || ''
);
};
export const getNodesWithNoQGuide = (
nodes: StoreNodeItemType[],
questionGuideText: AppQuestionGuideTextConfigType
): StoreNodeItemType[] => {
return nodes.map((node) => {
if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) {
return {
...node,
inputs: node.inputs.map((input) => {
if (input.key === NodeInputKeyEnum.questionGuideText) {
return {
...input,
value: { ...questionGuideText, textList: [] }
};
}
return input;
})
};
}
return node;
});
};

View File

@@ -0,0 +1,39 @@
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
import type {
ChatInputGuideProps,
ChatInputGuideResponse
} from '@/pages/api/core/chat/inputGuide/list';
import type {
countChatInputGuideTotalQuery,
countChatInputGuideTotalResponse
} from '@/pages/api/core/chat/inputGuide/countTotal';
import type {
createInputGuideBody,
createInputGuideResponse
} from '@/pages/api/core/chat/inputGuide/create';
import type { updateInputGuideBody } from '@/pages/api/core/chat/inputGuide/update';
import type { deleteChatInputGuideQuery } from '@/pages/api/core/chat/inputGuide/delete';
import type {
QueryChatInputGuideProps,
QueryChatInputGuideResponse
} from '@/pages/api/core/chat/inputGuide/query';
export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery) =>
GET<countChatInputGuideTotalResponse>(`/core/chat/inputGuide/countTotal`, data);
/**
* Get chat input guide list
*/
export const getChatInputGuideList = (data: ChatInputGuideProps) =>
GET<ChatInputGuideResponse>(`/core/chat/inputGuide/list`, data);
export const queryChatInputGuideList = (
data: QueryChatInputGuideProps,
url = `/core/chat/inputGuide/query`
) => GET<QueryChatInputGuideResponse>(url, data);
export const postChatInputGuides = (data: createInputGuideBody) =>
POST<createInputGuideResponse>(`/core/chat/inputGuide/create`, data);
export const putChatInputGuide = (data: updateInputGuideBody) =>
PUT(`/core/chat/inputGuide/update`, data);
export const delChatInputGuide = (data: deleteChatInputGuideQuery) =>
POST(`/core/chat/inputGuide/delete`, data);

View File

@@ -29,20 +29,20 @@ export function checkChatSupportSelectFileByModules(modules: StoreNodeItemType[]
export function getAppQuestionGuidesByModules(modules: StoreNodeItemType[] = []) {
const systemModule = modules.find((item) => item.flowNodeType === FlowNodeTypeEnum.systemConfig);
const questionGuideText = systemModule?.inputs.find(
(item) => item.key === NodeInputKeyEnum.questionGuideText
const chatInputGuide = systemModule?.inputs.find(
(item) => item.key === NodeInputKeyEnum.chatInputGuide
)?.value;
return questionGuideText?.open ? questionGuideText?.textList : [];
return chatInputGuide?.open ? chatInputGuide?.textList : [];
}
export function getAppQuestionGuidesByUserGuideModule(
module: StoreNodeItemType,
qGuideText: string[] = []
) {
const questionGuideText = module?.inputs.find(
(item) => item.key === NodeInputKeyEnum.questionGuideText
const chatInputGuide = module?.inputs.find(
(item) => item.key === NodeInputKeyEnum.chatInputGuide
)?.value;
return questionGuideText?.open ? qGuideText : [];
return chatInputGuide?.open ? qGuideText : [];
}

View File

@@ -26,8 +26,17 @@ import {
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
import { getWorkflowGlobalVariables } from './utils';
import { TFunction } from 'next-i18next';
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
export const getGlobalVariableNode = (nodes: FlowNodeItemType[], t: TFunction) => {
export const getGlobalVariableNode = ({
nodes,
chatConfig,
t
}: {
nodes: FlowNodeItemType[];
chatConfig: AppChatConfigType;
t: TFunction;
}) => {
const template: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.globalVariable,
templateType: FlowNodeTemplateTypeEnum.other,
@@ -44,7 +53,7 @@ export const getGlobalVariableNode = (nodes: FlowNodeItemType[], t: TFunction) =
outputs: []
};
const globalVariables = getWorkflowGlobalVariables(nodes, t);
const globalVariables = getWorkflowGlobalVariables({ nodes, chatConfig, t });
const variableNode: FlowNodeItemType = {
nodeId: VARIABLE_NODE_ID,

View File

@@ -19,8 +19,8 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
import {
formatEditorVariablePickerIcon,
getGuideModule,
splitGuideModule
getAppChatConfig,
getGuideModule
} from '@fastgpt/global/core/workflow/utils';
import { getSystemVariables } from '../app/utils';
import { TFunction } from 'next-i18next';
@@ -31,6 +31,7 @@ import {
} from '@fastgpt/global/core/workflow/type/io';
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
export const nodeTemplate2FlowNode = ({
template,
@@ -114,11 +115,13 @@ export const computedNodeInputReference = ({
nodeId,
nodes,
edges,
chatConfig,
t
}: {
nodeId: string;
nodes: FlowNodeItemType[];
edges: Edge[];
chatConfig: AppChatConfigType;
t: TFunction;
}) => {
// get current node
@@ -144,23 +147,31 @@ export const computedNodeInputReference = ({
};
findSourceNode(nodeId);
sourceNodes.unshift(getGlobalVariableNode(nodes, t));
sourceNodes.unshift(
getGlobalVariableNode({
nodes,
t,
chatConfig
})
);
return sourceNodes;
};
export const getReferenceDataValueType = ({
variable,
nodeList,
chatConfig,
t
}: {
variable?: ReferenceValueProps;
nodeList: FlowNodeItemType[];
chatConfig: AppChatConfigType;
t: TFunction;
}) => {
if (!variable) return WorkflowIOValueTypeEnum.any;
const node = nodeList.find((node) => node.nodeId === variable[0]);
const systemVariables = getWorkflowGlobalVariables(nodeList, t);
const systemVariables = getWorkflowGlobalVariables({ nodes: nodeList, chatConfig, t });
if (!node) return systemVariables.find((item) => item.key === variable?.[1])?.valueType;
@@ -288,12 +299,21 @@ export const filterSensitiveNodesData = (nodes: StoreNodeItemType[]) => {
};
/* get workflowStart output to global variables */
export const getWorkflowGlobalVariables = (
nodes: FlowNodeItemType[],
t: TFunction
): EditorVariablePickerType[] => {
export const getWorkflowGlobalVariables = ({
nodes,
chatConfig,
t
}: {
nodes: FlowNodeItemType[];
chatConfig: AppChatConfigType;
t: TFunction;
}): EditorVariablePickerType[] => {
const globalVariables = formatEditorVariablePickerIcon(
splitGuideModule(getGuideModule(nodes))?.variableNodes || []
getAppChatConfig({
chatConfig,
systemConfigNode: getGuideModule(nodes),
isPublicFetch: true
})?.variables || []
).map((item) => ({
...item,
valueType: WorkflowIOValueTypeEnum.any