From e35ce2caa04034bf0337d8c5b39c9d384ba0779e Mon Sep 17 00:00:00 2001 From: heheer <71265218+newfish-cmyk@users.noreply.github.com> Date: Sun, 19 May 2024 17:34:16 +0800 Subject: [PATCH] feat: question guide (#1508) * feat: question guide * fix * fix * fix * change interface * fix --- docSite/assets/imgs/questionGuide.png | Bin 0 -> 39822 bytes docSite/content/docs/course/custom_link.md | 43 ++ packages/global/core/app/constants.ts | 6 + packages/global/core/app/type.d.ts | 7 + packages/global/core/app/utils.ts | 11 +- packages/global/core/workflow/constants.ts | 1 + .../workflow/template/system/systemConfig.ts | 6 + packages/global/core/workflow/utils.ts | 12 +- packages/service/core/app/qGuideSchema.ts | 40 ++ .../web/components/common/Icon/constants.ts | 3 + .../web/components/common/Icon/icons/book.svg | 3 + .../Icon/icons/core/app/inputGuides.svg | 3 + .../components/common/Icon/icons/union.svg | 7 + packages/web/hooks/useScrollPagination.tsx | 9 +- projects/app/i18n/en/app.json | 8 + projects/app/i18n/en/common.json | 3 + projects/app/i18n/zh/app.json | 8 + projects/app/i18n/zh/common.json | 3 + .../src/components/ChatBox/MessageInput.tsx | 47 +- .../ChatBox/components/QustionGuide.tsx | 98 ++++ projects/app/src/components/ChatBox/index.tsx | 2 +- .../src/components/core/app/QGuidesConfig.tsx | 479 ++++++++++++++++++ .../core/workflow/Flow/ChatTest.tsx | 7 +- .../workflow/Flow/nodes/NodeSystemConfig.tsx | 32 +- projects/app/src/pages/api/core/app/del.ts | 7 + .../api/core/app/questionGuides/import.ts | 41 ++ .../pages/api/core/app/questionGuides/list.ts | 48 ++ .../app/detail/components/FlowEdit/Header.tsx | 13 + .../app/detail/components/FlowEdit/index.tsx | 2 +- .../detail/components/SimpleEdit/ChatTest.tsx | 2 +- .../detail/components/SimpleEdit/EditForm.tsx | 36 +- .../components/SimpleEdit/ToolSelectModal.tsx | 1 - projects/app/src/pages/app/detail/index.tsx | 1 + projects/app/src/pages/chat/index.tsx | 7 +- projects/app/src/pages/chat/share.tsx | 9 +- projects/app/src/pages/chat/team.tsx | 8 +- projects/app/src/web/core/app/api.ts | 23 +- .../app/src/web/core/app/store/useAppStore.ts | 4 +- projects/app/src/web/core/app/utils.ts | 44 +- projects/app/src/web/core/chat/utils.ts | 21 + 40 files changed, 1071 insertions(+), 34 deletions(-) create mode 100644 docSite/assets/imgs/questionGuide.png create mode 100644 docSite/content/docs/course/custom_link.md create mode 100644 packages/service/core/app/qGuideSchema.ts create mode 100644 packages/web/components/common/Icon/icons/book.svg create mode 100644 packages/web/components/common/Icon/icons/core/app/inputGuides.svg create mode 100644 packages/web/components/common/Icon/icons/union.svg create mode 100644 projects/app/src/components/ChatBox/components/QustionGuide.tsx create mode 100644 projects/app/src/components/core/app/QGuidesConfig.tsx create mode 100644 projects/app/src/pages/api/core/app/questionGuides/import.ts create mode 100644 projects/app/src/pages/api/core/app/questionGuides/list.ts diff --git a/docSite/assets/imgs/questionGuide.png b/docSite/assets/imgs/questionGuide.png new file mode 100644 index 0000000000000000000000000000000000000000..63130ecc7fa199041ea868ac99e1b88d2a566d1c GIT binary patch literal 39822 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 yaOGSvoV`gy>Qa8aN)%qjZxQ{jy~FSGI@Ce-?9H { return { @@ -35,7 +35,8 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => { type: 'web' }, whisper: defaultWhisperConfig, - scheduleTrigger: null + scheduleTrigger: null, + questionGuideText: defaultQuestionGuideTextConfig } }; }; @@ -109,7 +110,8 @@ export const appWorkflow2Form = ({ nodes }: { nodes: StoreNodeItemType[] }) => { questionGuide, ttsConfig, whisperConfig, - scheduledTriggerConfig + scheduledTriggerConfig, + questionGuideText } = splitGuideModule(getGuideModule(nodes)); defaultAppForm.userGuide = { @@ -118,7 +120,8 @@ export const appWorkflow2Form = ({ nodes }: { nodes: StoreNodeItemType[] }) => { questionGuide: questionGuide, tts: ttsConfig, whisper: whisperConfig, - scheduleTrigger: scheduledTriggerConfig + scheduleTrigger: scheduledTriggerConfig, + questionGuideText: questionGuideText }; } else if (node.flowNodeType === FlowNodeTypeEnum.pluginModule) { if (!node.pluginId) return; diff --git a/packages/global/core/workflow/constants.ts b/packages/global/core/workflow/constants.ts index c12741b082..46a2693059 100644 --- a/packages/global/core/workflow/constants.ts +++ b/packages/global/core/workflow/constants.ts @@ -45,6 +45,7 @@ export enum NodeInputKeyEnum { whisper = 'whisper', variables = 'variables', scheduleTrigger = 'scheduleTrigger', + questionGuideText = 'questionGuideText', // entry userChatInput = 'userChatInput', diff --git a/packages/global/core/workflow/template/system/systemConfig.ts b/packages/global/core/workflow/template/system/systemConfig.ts index cb7aa5d11a..4357746d43 100644 --- a/packages/global/core/workflow/template/system/systemConfig.ts +++ b/packages/global/core/workflow/template/system/systemConfig.ts @@ -56,6 +56,12 @@ export const SystemConfigNode: FlowNodeTemplateType = { renderTypeList: [FlowNodeInputTypeEnum.hidden], valueType: WorkflowIOValueTypeEnum.any, label: '' + }, + { + key: NodeInputKeyEnum.questionGuideText, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + valueType: WorkflowIOValueTypeEnum.any, + label: '' } ], outputs: [] diff --git a/packages/global/core/workflow/utils.ts b/packages/global/core/workflow/utils.ts index 2e72f7d24b..65cd2a7be7 100644 --- a/packages/global/core/workflow/utils.ts +++ b/packages/global/core/workflow/utils.ts @@ -11,7 +11,8 @@ import type { VariableItemType, AppTTSConfigType, AppWhisperConfigType, - AppScheduledTriggerConfigType + AppScheduledTriggerConfigType, + AppQuestionGuideTextConfigType } from '../app/type'; import { EditorVariablePickerType } from '../../../web/components/common/Textarea/PromptEditor/type'; import { defaultWhisperConfig } from '../app/constants'; @@ -59,13 +60,20 @@ export const splitGuideModule = (guideModules?: StoreNodeItemType) => { guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.scheduleTrigger)?.value ?? null; + const questionGuideText: AppQuestionGuideTextConfigType = guideModules?.inputs?.find( + (item) => item.key === NodeInputKeyEnum.questionGuideText + )?.value || { + open: false + }; + return { welcomeText, variableNodes, questionGuide, ttsConfig, whisperConfig, - scheduledTriggerConfig + scheduledTriggerConfig, + questionGuideText }; }; export const replaceAppChatConfig = ({ diff --git a/packages/service/core/app/qGuideSchema.ts b/packages/service/core/app/qGuideSchema.ts new file mode 100644 index 0000000000..4f0741a93b --- /dev/null +++ b/packages/service/core/app/qGuideSchema.ts @@ -0,0 +1,40 @@ +import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; +import { connectionMongo, type Model } from '../../common/mongo'; +const { Schema, model, models } = connectionMongo; + +export const AppQGuideCollectionName = 'app_question_guides'; + +type AppQGuideSchemaType = { + _id: string; + appId: string; + teamId: string; + text: string; +}; + +const AppQGuideSchema = new Schema({ + appId: { + type: Schema.Types.ObjectId, + ref: AppQGuideCollectionName, + required: true + }, + teamId: { + type: Schema.Types.ObjectId, + ref: TeamCollectionName, + required: true + }, + text: { + type: String, + default: '' + } +}); + +try { + AppQGuideSchema.index({ appId: 1 }); +} catch (error) { + console.log(error); +} + +export const MongoAppQGuide: Model = + models[AppQGuideCollectionName] || model(AppQGuideCollectionName, AppQGuideSchema); + +MongoAppQGuide.syncIndexes(); diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 2afda1a544..daa13392ea 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -1,6 +1,7 @@ // @ts-nocheck export const iconPaths = { + book: () => import('./icons/book.svg'), change: () => import('./icons/change.svg'), chatSend: () => import('./icons/chatSend.svg'), closeSolid: () => import('./icons/closeSolid.svg'), @@ -64,6 +65,7 @@ export const iconPaths = { 'core/app/appApiLight': () => import('./icons/core/app/appApiLight.svg'), 'core/app/customFeedback': () => import('./icons/core/app/customFeedback.svg'), 'core/app/headphones': () => import('./icons/core/app/headphones.svg'), + 'core/app/inputGuides': () => import('./icons/core/app/inputGuides.svg'), 'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'), 'core/app/markLight': () => import('./icons/core/app/markLight.svg'), 'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'), @@ -219,6 +221,7 @@ export const iconPaths = { 'support/user/userFill': () => import('./icons/support/user/userFill.svg'), 'support/user/userLight': () => import('./icons/support/user/userLight.svg'), text: () => import('./icons/text.svg'), + union: () => import('./icons/union.svg'), user: () => import('./icons/user.svg'), wx: () => import('./icons/wx.svg') }; diff --git a/packages/web/components/common/Icon/icons/book.svg b/packages/web/components/common/Icon/icons/book.svg new file mode 100644 index 0000000000..9e45d5e205 --- /dev/null +++ b/packages/web/components/common/Icon/icons/book.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/core/app/inputGuides.svg b/packages/web/components/common/Icon/icons/core/app/inputGuides.svg new file mode 100644 index 0000000000..844279379b --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/inputGuides.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/union.svg b/packages/web/components/common/Icon/icons/union.svg new file mode 100644 index 0000000000..80c6c5b67a --- /dev/null +++ b/packages/web/components/common/Icon/icons/union.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/web/hooks/useScrollPagination.tsx b/packages/web/hooks/useScrollPagination.tsx index e2222a065f..dd989351b7 100644 --- a/packages/web/hooks/useScrollPagination.tsx +++ b/packages/web/hooks/useScrollPagination.tsx @@ -85,14 +85,16 @@ export function useScrollPagination< ...props }: { children: React.ReactNode; isLoading?: boolean } & BoxProps) => { return ( - - {children} + <> + + {children} + {noMore.current && ( {t('common.No more data')} )} - + ); } ); @@ -115,6 +117,7 @@ export function useScrollPagination< return { containerRef, list, + data, isLoading, ScrollList, fetchData: loadData diff --git a/projects/app/i18n/en/app.json b/projects/app/i18n/en/app.json index 22b8ab2920..03734f220f 100644 --- a/projects/app/i18n/en/app.json +++ b/projects/app/i18n/en/app.json @@ -47,6 +47,14 @@ "type": "\"{{type}}\" type\n{{description}}" }, "modules": { + "Config Texts": "Config Texts", + "Config question guide": "Config question guide", + "Custom question guide URL": "Custom question guide URL", + "Input Guide": "Input Guide", + "Only support CSV": "Only support CSV", + "Question Guide": "Question guide", + "Question Guide Switch": "Open question guide", + "Question Guide Texts": "Texts", "Title is required": "Module name cannot be empty" } } diff --git a/projects/app/i18n/en/common.json b/projects/app/i18n/en/common.json index 41b16bf496..8f0008ce52 100644 --- a/projects/app/i18n/en/common.json +++ b/projects/app/i18n/en/common.json @@ -7,6 +7,7 @@ "Move": "Move", "Name": "Name", "New Create": "Create New", + "No data": "No data", "Rename": "Rename", "Running": "Running", "UnKnow": "Unknown", @@ -45,6 +46,7 @@ "Delete Tip": "Delete Tip", "Delete Warning": "Delete Warning", "Detail": "Detail", + "Documents": "Documents", "Done": "Done", "Edit": "Edit", "Exit": "Exit", @@ -101,6 +103,7 @@ "Search": "Search", "Select File Failed": "Select File Failed", "Select One Folder": "Select a folder", + "Select all": "Select all", "Select template": "Select template", "Set Avatar": "Click to set avatar", "Set Name": "Set a name", diff --git a/projects/app/i18n/zh/app.json b/projects/app/i18n/zh/app.json index 83c85cec35..23fdd132ac 100644 --- a/projects/app/i18n/zh/app.json +++ b/projects/app/i18n/zh/app.json @@ -46,6 +46,14 @@ "type": "\"{{type}}\"类型\n{{description}}" }, "modules": { + "Config Texts": "配置词库", + "Config question guide": "配置输入提示", + "Custom question guide URL": "自定义词库地址", + "Input Guide": "智能推荐", + "Only support CSV": "仅支持 CSV 导入,点击下载模板", + "Question Guide": "输入提示", + "Question Guide Switch": "是否开启", + "Question Guide Texts": "词库", "Title is required": "模块名不能为空" } } diff --git a/projects/app/i18n/zh/common.json b/projects/app/i18n/zh/common.json index db7ed9fb68..de5c628448 100644 --- a/projects/app/i18n/zh/common.json +++ b/projects/app/i18n/zh/common.json @@ -7,6 +7,7 @@ "Move": "移动", "Name": "名称", "New Create": "新建", + "No data": "暂无数据", "Rename": "重命名", "Running": "运行中", "UnKnow": "未知", @@ -45,6 +46,7 @@ "Delete Tip": "删除提示", "Delete Warning": "删除警告", "Detail": "详情", + "Documents": "文档", "Done": "完成", "Edit": "编辑", "Exit": "退出", @@ -102,6 +104,7 @@ "Search": "搜索", "Select File Failed": "选择文件异常", "Select One Folder": "选择一个目录", + "Select all": "全选", "Select template": "选择模板", "Set Avatar": "点击设置头像", "Set Name": "取个名字", diff --git a/projects/app/src/components/ChatBox/MessageInput.tsx b/projects/app/src/components/ChatBox/MessageInput.tsx index 6a4195271a..24f6669d4a 100644 --- a/projects/app/src/components/ChatBox/MessageInput.tsx +++ b/projects/app/src/components/ChatBox/MessageInput.tsx @@ -16,6 +16,11 @@ import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from '. import { textareaMinH } from './constants'; import { UseFormReturn, useFieldArray } from 'react-hook-form'; import { useChatProviderStore } from './Provider'; +import QuestionGuide from './components/QustionGuide'; +import { useQuery } from '@tanstack/react-query'; +import { getMyQuestionGuides } from '@/web/core/app/api'; +import { getAppQGuideCustomURL } from '@/web/core/app/utils'; +import { useAppStore } from '@/web/core/app/store/useAppStore'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); const MessageInput = ({ @@ -53,6 +58,7 @@ const MessageInput = ({ const { isPc, whisperModel } = useSystemStore(); const canvasRef = useRef(null); const { t } = useTranslation(); + const { appDetail } = useAppStore(); const havInput = !!inputValue || fileList.length > 0; const hasFileUploading = fileList.some((item) => !item.url); @@ -205,6 +211,23 @@ const MessageInput = ({ startSpeak(finishWhisperTranscription); }, [finishWhisperTranscription, isSpeaking, startSpeak, stopSpeak]); + const { data } = useQuery( + [appId, inputValue], + async () => { + if (!appId) return { list: [], total: 0 }; + return getMyQuestionGuides({ + appId, + customURL: getAppQGuideCustomURL(appDetail), + pageSize: 5, + current: 1, + searchKey: inputValue + }); + }, + { + enabled: !!appId + } + ); + return ( + {/* popup */} + {havInput && ( + setValue('input', value)} + bottom={'100%'} + top={'auto'} + left={0} + right={0} + mb={2} + overflowY={'auto'} + boxShadow={'sm'} + /> + )} + {/* file preview */} {fileList.map((item, index) => ( @@ -377,7 +415,12 @@ const MessageInput = ({ // @ts-ignore e.key === 'a' && e.ctrlKey && e.target?.select(); - if ((isPc || window !== parent) && e.keyCode === 13 && !e.shiftKey) { + if ( + (isPc || window !== parent) && + e.keyCode === 13 && + !e.shiftKey && + !(havInput && data?.list.length && data?.list.length > 0) + ) { handleSend(); e.preventDefault(); } diff --git a/projects/app/src/components/ChatBox/components/QustionGuide.tsx b/projects/app/src/components/ChatBox/components/QustionGuide.tsx new file mode 100644 index 0000000000..b71e22be3e --- /dev/null +++ b/projects/app/src/components/ChatBox/components/QustionGuide.tsx @@ -0,0 +1,98 @@ +import { Box, BoxProps, Flex } from '@chakra-ui/react'; +import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type'; +import React, { useCallback, useEffect } from 'react'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { useI18n } from '@/web/context/I18n'; + +export default function QuestionGuide({ + guides, + setDropdownValue, + ...props +}: { + guides: string[]; + setDropdownValue?: (value: string) => void; +} & BoxProps) { + const [highlightedIndex, setHighlightedIndex] = React.useState(0); + const { appT } = useI18n(); + + const handleKeyDown = useCallback( + (event: any) => { + if (event.keyCode === 38) { + setHighlightedIndex((prevIndex) => Math.max(prevIndex - 1, 0)); + } else if (event.keyCode === 40) { + setHighlightedIndex((prevIndex) => Math.min(prevIndex + 1, guides.length - 1)); + } else if (event.keyCode === 13 && guides[highlightedIndex]) { + setDropdownValue?.(guides[highlightedIndex]); + event.preventDefault(); + } + }, + [highlightedIndex, setDropdownValue, guides] + ); + + useEffect(() => { + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleKeyDown]); + + return guides.length ? ( + + + + {appT('modules.Input Guide')} + + {guides.map((item, index) => ( + { + e.preventDefault(); + + setDropdownValue?.(item); + }} + onMouseEnter={() => { + setHighlightedIndex(index); + }} + > + {item} + + ))} + + ) : null; +} diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx index 311e1a0012..f982e502e4 100644 --- a/projects/app/src/components/ChatBox/index.tsx +++ b/projects/app/src/components/ChatBox/index.tsx @@ -58,7 +58,7 @@ import ChatProvider, { useChatProviderStore } from './Provider'; import ChatItem from './components/ChatItem'; import dynamic from 'next/dynamic'; -import { useCreation, useUpdateEffect } from 'ahooks'; +import { useCreation } from 'ahooks'; const ResponseTags = dynamic(() => import('./ResponseTags')); const FeedbackModal = dynamic(() => import('./FeedbackModal')); diff --git a/projects/app/src/components/core/app/QGuidesConfig.tsx b/projects/app/src/components/core/app/QGuidesConfig.tsx new file mode 100644 index 0000000000..60cd88778f --- /dev/null +++ b/projects/app/src/components/core/app/QGuidesConfig.tsx @@ -0,0 +1,479 @@ +import MyIcon from '@fastgpt/web/components/common/Icon'; +import MyTooltip from '@/components/MyTooltip'; +import { + Box, + Button, + Flex, + ModalBody, + useDisclosure, + Switch, + Input, + Textarea, + InputGroup, + InputRightElement, + Checkbox, + useCheckboxGroup, + ModalFooter, + BoxProps +} from '@chakra-ui/react'; +import React, { ChangeEvent, useEffect, useMemo, useRef } from 'react'; +import { useTranslation } from 'next-i18next'; +import type { AppQuestionGuideTextConfigType } from '@fastgpt/global/core/app/type.d'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import { useAppStore } from '@/web/core/app/store/useAppStore'; +import MyInput from '@/components/MyInput'; +import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; +import { useI18n } from '@/web/context/I18n'; +import { fileDownload } from '@/web/common/file/utils'; +import { getDocPath } from '@/web/common/system/doc'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import { getMyQuestionGuides } from '@/web/core/app/api'; +import { getAppQGuideCustomURL } from '@/web/core/app/utils'; +import { useQuery } from '@tanstack/react-query'; + +const csvTemplate = `"第一列内容" +"必填列" +"只会将第一列内容导入,其余列会被忽略" +"AIGC发展分为几个阶段?" +`; + +const QGuidesConfig = ({ + value, + onChange +}: { + value: AppQuestionGuideTextConfigType; + onChange: (e: AppQuestionGuideTextConfigType) => void; +}) => { + const { t } = useTranslation(); + const { appT, commonT } = useI18n(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const { isOpen: isOpenTexts, onOpen: onOpenTexts, onClose: onCloseTexts } = useDisclosure(); + const isOpenQuestionGuide = value.open; + const { appDetail } = useAppStore(); + const [searchKey, setSearchKey] = React.useState(''); + + const { data } = useQuery( + [appDetail._id, searchKey], + async () => { + return getMyQuestionGuides({ + appId: appDetail._id, + customURL: getAppQGuideCustomURL(appDetail), + pageSize: 30, + current: 1, + searchKey + }); + }, + { + enabled: !!appDetail._id + } + ); + + useEffect(() => { + onChange({ + ...value, + textList: data?.list || [] + }); + }, [data]); + + const formLabel = useMemo(() => { + if (!isOpenQuestionGuide) { + return t('core.app.whisper.Close'); + } + return t('core.app.whisper.Open'); + }, [t, isOpenQuestionGuide]); + + return ( + + + {appT('modules.Question Guide')} + + + + + + + + {appT('modules.Question Guide Switch')} + { + onChange({ + ...value, + open: e.target.checked + }); + }} + /> + + {isOpenQuestionGuide && ( + <> + + {appT('modules.Question Guide Texts')} + + {value.textList.length || 0} + + + + + <> + + {appT('modules.Custom question guide URL')} + window.open(getDocPath('/docs/course/custom_link'))} + color={'primary.700'} + alignItems={'center'} + cursor={'pointer'} + > + + {commonT('common.Documents')} + + + +