From 32747641e43c306bd44b2b2dc2a2bf2241e95f66 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 6 Nov 2023 19:36:32 +0800 Subject: [PATCH] feat: add api-based extension & external data tool & moderation (#1459) --- web/app/(commonLayout)/layout.tsx | 11 +- web/app/components/app/chat/answer/index.tsx | 19 +- web/app/components/app/chat/style.module.css | 5 + web/app/components/app/chat/type.ts | 7 + .../config-prompt/advanced-prompt-input.tsx | 41 +- .../config-prompt/simple-prompt-input.tsx | 41 +- .../config/feature/choose-feature/index.tsx | 14 + .../config/feature/use-feature.tsx | 8 + .../app/configuration/config/index.tsx | 41 +- .../dataset-config/card-item/item.tsx | 90 +++++ .../configuration/dataset-config/index.tsx | 13 +- .../dataset-config/settings-modal/index.tsx | 152 ++++++++ .../app/configuration/debug/index.tsx | 22 +- .../components/app/configuration/index.tsx | 55 ++- .../prompt-value-panel/index.tsx | 6 +- .../app/configuration/toolbox/index.tsx | 35 +- .../toolbox/moderation/form-generation.tsx | 78 ++++ .../toolbox/moderation/index.tsx | 81 ++++ .../toolbox/moderation/moderation-content.tsx | 72 ++++ .../moderation/moderation-setting-modal.tsx | 362 ++++++++++++++++++ .../tools/external-data-tool-modal.tsx | 294 ++++++++++++++ .../app/configuration/tools/index.tsx | 185 +++++++++ .../app/overview/apikey-info-panel/index.tsx | 17 +- .../app/text-generate/item/index.tsx | 1 + .../vender/line/development/webhooks.svg | 12 + .../vender/line/education/book-open-01.svg | 6 + .../assets/vender/line/general/edit-02.svg | 10 + .../vender/line/general/settings-01.svg | 13 + .../vender/solid/files/file-search-02.svg | 8 + .../assets/vender/solid/general/tool-03.svg | 9 + .../src/vender/line/development/Webhooks.json | 89 +++++ .../src/vender/line/development/Webhooks.tsx | 16 + .../src/vender/line/development/index.ts | 1 + .../src/vender/line/education/BookOpen01.json | 49 +++ .../src/vender/line/education/BookOpen01.tsx | 16 + .../icons/src/vender/line/education/index.ts | 1 + .../icons/src/vender/line/general/Edit02.json | 66 ++++ .../icons/src/vender/line/general/Edit02.tsx | 16 + .../src/vender/line/general/Settings01.json | 86 +++++ .../src/vender/line/general/Settings01.tsx | 16 + .../icons/src/vender/line/general/index.ts | 2 + .../src/vender/solid/files/FileSearch02.json | 57 +++ .../src/vender/solid/files/FileSearch02.tsx | 16 + .../icons/src/vender/solid/files/index.ts | 1 + .../src/vender/solid/general/Tool03.json | 62 +++ .../icons/src/vender/solid/general/Tool03.tsx | 16 + .../icons/src/vender/solid/general/index.ts | 1 + .../base/notion-page-selector/base.tsx | 17 +- .../components/base/prompt-editor/index.tsx | 10 +- .../prompt-editor/plugins/variable-picker.tsx | 105 ++++- web/app/components/datasets/create/index.tsx | 15 +- .../datasets/settings/form/index.tsx | 11 +- .../settings/index-method-radio/index.tsx | 3 + .../develop/template/template.en.mdx | 1 + .../develop/template/template.zh.mdx | 1 + .../config/plugins-config/index.tsx | 15 +- .../header/account-dropdown/index.tsx | 9 +- .../api-based-extension-page/empty.tsx | 26 ++ .../api-based-extension-page/index.tsx | 53 +++ .../api-based-extension-page/item.tsx | 75 ++++ .../api-based-extension-page/modal.tsx | 151 ++++++++ .../api-based-extension-page/selector.tsx | 119 ++++++ .../header/account-setting/index.tsx | 18 +- .../model-page/model-selector/index.tsx | 17 +- web/app/components/share/chat/index.tsx | 18 +- web/app/components/share/chatbot/index.tsx | 13 +- .../share/text-generation/index.tsx | 3 +- .../share/text-generation/result/index.tsx | 9 +- web/context/debug-configuration.ts | 30 +- web/context/modal-context.tsx | 140 +++++++ web/hooks/use-moderate.ts | 49 +++ web/i18n/lang/app-debug.en.ts | 61 +++ web/i18n/lang/app-debug.zh.ts | 61 +++ web/i18n/lang/common.en.ts | 38 +- web/i18n/lang/common.zh.ts | 38 +- web/i18n/lang/dataset-settings.en.ts | 2 + web/i18n/lang/dataset-settings.zh.ts | 2 + web/models/common.ts | 59 ++- web/models/debug.ts | 15 + web/service/base.ts | 13 +- web/service/common.ts | 49 ++- web/service/debug.ts | 12 +- web/service/share.ts | 12 +- web/types/app.ts | 5 + 84 files changed, 3327 insertions(+), 167 deletions(-) create mode 100644 web/app/components/app/configuration/dataset-config/card-item/item.tsx create mode 100644 web/app/components/app/configuration/dataset-config/settings-modal/index.tsx create mode 100644 web/app/components/app/configuration/toolbox/moderation/form-generation.tsx create mode 100644 web/app/components/app/configuration/toolbox/moderation/index.tsx create mode 100644 web/app/components/app/configuration/toolbox/moderation/moderation-content.tsx create mode 100644 web/app/components/app/configuration/toolbox/moderation/moderation-setting-modal.tsx create mode 100644 web/app/components/app/configuration/tools/external-data-tool-modal.tsx create mode 100644 web/app/components/app/configuration/tools/index.tsx create mode 100644 web/app/components/base/icons/assets/vender/line/development/webhooks.svg create mode 100644 web/app/components/base/icons/assets/vender/line/education/book-open-01.svg create mode 100644 web/app/components/base/icons/assets/vender/line/general/edit-02.svg create mode 100644 web/app/components/base/icons/assets/vender/line/general/settings-01.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/files/file-search-02.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/general/tool-03.svg create mode 100644 web/app/components/base/icons/src/vender/line/development/Webhooks.json create mode 100644 web/app/components/base/icons/src/vender/line/development/Webhooks.tsx create mode 100644 web/app/components/base/icons/src/vender/line/education/BookOpen01.json create mode 100644 web/app/components/base/icons/src/vender/line/education/BookOpen01.tsx create mode 100644 web/app/components/base/icons/src/vender/line/education/index.ts create mode 100644 web/app/components/base/icons/src/vender/line/general/Edit02.json create mode 100644 web/app/components/base/icons/src/vender/line/general/Edit02.tsx create mode 100644 web/app/components/base/icons/src/vender/line/general/Settings01.json create mode 100644 web/app/components/base/icons/src/vender/line/general/Settings01.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/files/FileSearch02.json create mode 100644 web/app/components/base/icons/src/vender/solid/files/FileSearch02.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/general/Tool03.json create mode 100644 web/app/components/base/icons/src/vender/solid/general/Tool03.tsx create mode 100644 web/app/components/header/account-setting/api-based-extension-page/empty.tsx create mode 100644 web/app/components/header/account-setting/api-based-extension-page/index.tsx create mode 100644 web/app/components/header/account-setting/api-based-extension-page/item.tsx create mode 100644 web/app/components/header/account-setting/api-based-extension-page/modal.tsx create mode 100644 web/app/components/header/account-setting/api-based-extension-page/selector.tsx create mode 100644 web/context/modal-context.tsx create mode 100644 web/hooks/use-moderate.ts diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index b1c524642e..ceeb164315 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -7,6 +7,7 @@ import HeaderWrapper from '@/app/components/header/HeaderWrapper' import Header from '@/app/components/header' import { EventEmitterContextProvider } from '@/context/event-emitter' import { ProviderContextProvider } from '@/context/provider-context' +import { ModalContextProvider } from '@/context/modal-context' const Layout = ({ children }: { children: ReactNode }) => { return ( @@ -16,10 +17,12 @@ const Layout = ({ children }: { children: ReactNode }) => { - -
- - {children} + + +
+ + {children} + diff --git a/web/app/components/app/chat/answer/index.tsx b/web/app/components/app/chat/answer/index.tsx index 396e22a3aa..9865ff3c2b 100644 --- a/web/app/components/app/chat/answer/index.tsx +++ b/web/app/components/app/chat/answer/index.tsx @@ -22,6 +22,7 @@ import { Markdown } from '@/app/components/base/markdown' import AutoHeightTextarea from '@/app/components/base/auto-height-textarea' import Button from '@/app/components/base/button' import type { DataSet } from '@/models/datasets' + const Divider: FC<{ name: string }> = ({ name }) => { const { t } = useTranslation() return
@@ -53,7 +54,22 @@ export type IAnswerProps = { isShowCitationHitInfo?: boolean } // The component needs to maintain its own state to control whether to display input component -const Answer: FC = ({ item, feedbackDisabled = false, isHideFeedbackEdit = false, onFeedback, onSubmitAnnotation, displayScene = 'web', isResponsing, answerIcon, thoughts, citation, isThinking, dataSets, isShowCitation, isShowCitationHitInfo = false }) => { +const Answer: FC = ({ + item, + feedbackDisabled = false, + isHideFeedbackEdit = false, + onFeedback, + onSubmitAnnotation, + displayScene = 'web', + isResponsing, + answerIcon, + thoughts, + citation, + isThinking, + dataSets, + isShowCitation, + isShowCitationHitInfo = false, +}) => { const { id, content, more, feedback, adminFeedback, annotation: initAnnotation } = item const [showEdit, setShowEdit] = useState(false) const [loading, setLoading] = useState(false) @@ -62,7 +78,6 @@ const Answer: FC = ({ item, feedbackDisabled = false, isHideFeedba const [localAdminFeedback, setLocalAdminFeedback] = useState(adminFeedback) const { userProfile } = useContext(AppContext) const { t } = useTranslation() - /** * Render feedback results (distinguish between users and administrators) * User reviews cannot be cancelled in Console diff --git a/web/app/components/app/chat/style.module.css b/web/app/components/app/chat/style.module.css index ccdbd793cc..b29abc78d8 100644 --- a/web/app/components/app/chat/style.module.css +++ b/web/app/components/app/chat/style.module.css @@ -59,6 +59,11 @@ max-width: 100%; } +.answer { + display: inline-block; + max-width: 100%; +} + .answerWrap:hover .copyBtn { display: block; } diff --git a/web/app/components/app/chat/type.ts b/web/app/components/app/chat/type.ts index 65dadc865a..2fbf78229e 100644 --- a/web/app/components/app/chat/type.ts +++ b/web/app/components/app/chat/type.ts @@ -73,3 +73,10 @@ export type MessageEnd = { id: string retriever_resources?: CitationItem[] } + +export type MessageReplace = { + id: string + task_id: string + answer: string + conversation_id: string +} diff --git a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx index 6c32746abb..df6255adae 100644 --- a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx @@ -19,6 +19,9 @@ import ConfigContext from '@/context/debug-configuration' import { getNewVar, getVars } from '@/utils/var' import { AppType } from '@/types/app' import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import { useModalContext } from '@/context/modal-context' +import type { ExternalDataTool } from '@/models/common' +import { useToastContext } from '@/app/components/base/toast' type Props = { type: PromptRole @@ -56,7 +59,36 @@ const AdvancedPromptInput: FC = ({ showHistoryModal, dataSets, showSelectDataSet, + externalDataToolsConfig, + setExternalDataToolsConfig, } = useContext(ConfigContext) + const { notify } = useToastContext() + const { setShowExternalDataToolModal } = useModalContext() + const handleOpenExternalDataToolModal = () => { + setShowExternalDataToolModal({ + payload: {}, + onSaveCallback: (newExternalDataTool: ExternalDataTool) => { + setExternalDataToolsConfig([...externalDataToolsConfig, newExternalDataTool]) + }, + onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => { + for (let i = 0; i < promptVariables.length; i++) { + if (promptVariables[i].key === newExternalDataTool.variable) { + notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: promptVariables[i].key }) }) + return false + } + } + + for (let i = 0; i < externalDataToolsConfig.length; i++) { + if (externalDataToolsConfig[i].variable === newExternalDataTool.variable) { + notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: externalDataToolsConfig[i].variable }) }) + return false + } + } + + return true + }, + }) + } const isChatApp = mode === AppType.chat const [isCopied, setIsCopied] = React.useState(false) @@ -76,7 +108,7 @@ const AdvancedPromptInput: FC = ({ } const handleBlur = () => { const keys = getVars(value) - const newPromptVariables = keys.filter(key => !(key in promptVariablesObj)).map(key => getNewVar(key)) + const newPromptVariables = keys.filter(key => !(key in promptVariablesObj) && !externalDataToolsConfig.find(item => item.variable === key)).map(key => getNewVar(key)) if (newPromptVariables.length > 0) { setNewPromptVariables(newPromptVariables) showConfirmAddVar() @@ -174,6 +206,13 @@ const AdvancedPromptInput: FC = ({ name: item.name, value: item.key, })), + externalTools: externalDataToolsConfig.map(item => ({ + name: item.label!, + variableName: item.variable!, + icon: item.icon, + icon_background: item.icon_background, + })), + onAddExternalTool: handleOpenExternalDataToolModal, }} historyBlock={{ show: !isChatMode && isChatApp, diff --git a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx index e61b26d127..9b0d77d4e7 100644 --- a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx @@ -18,6 +18,9 @@ import type { AutomaticRes } from '@/service/debug' import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' import PromptEditor from '@/app/components/base/prompt-editor' import ConfigContext from '@/context/debug-configuration' +import { useModalContext } from '@/context/modal-context' +import type { ExternalDataTool } from '@/models/common' +import { useToastContext } from '@/app/components/base/toast' export type ISimplePromptInput = { mode: AppType @@ -43,7 +46,36 @@ const Prompt: FC = ({ setIntroduction, hasSetBlockStatus, showSelectDataSet, + externalDataToolsConfig, + setExternalDataToolsConfig, } = useContext(ConfigContext) + const { notify } = useToastContext() + const { setShowExternalDataToolModal } = useModalContext() + const handleOpenExternalDataToolModal = () => { + setShowExternalDataToolModal({ + payload: {}, + onSaveCallback: (newExternalDataTool: ExternalDataTool) => { + setExternalDataToolsConfig([...externalDataToolsConfig, newExternalDataTool]) + }, + onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => { + for (let i = 0; i < promptVariables.length; i++) { + if (promptVariables[i].key === newExternalDataTool.variable) { + notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: promptVariables[i].key }) }) + return false + } + } + + for (let i = 0; i < externalDataToolsConfig.length; i++) { + if (externalDataToolsConfig[i].variable === newExternalDataTool.variable) { + notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: externalDataToolsConfig[i].variable }) }) + return false + } + } + + return true + }, + }) + } const promptVariablesObj = (() => { const obj: Record = {} promptVariables.forEach((item) => { @@ -57,7 +89,7 @@ const Prompt: FC = ({ const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false) const handleChange = (newTemplates: string, keys: string[]) => { - const newPromptVariables = keys.filter(key => !(key in promptVariablesObj)).map(key => getNewVar(key)) + const newPromptVariables = keys.filter(key => !(key in promptVariablesObj) && !externalDataToolsConfig.find(item => item.variable === key)).map(key => getNewVar(key)) if (newPromptVariables.length > 0) { setNewPromptVariables(newPromptVariables) setNewTemplates(newTemplates) @@ -127,6 +159,13 @@ const Prompt: FC = ({ name: item.name, value: item.key, })), + externalTools: externalDataToolsConfig.map(item => ({ + name: item.label!, + variableName: item.variable!, + icon: item.icon, + icon_background: item.icon_background, + })), + onAddExternalTool: handleOpenExternalDataToolModal, }} historyBlock={{ show: false, diff --git a/web/app/components/app/configuration/config/feature/choose-feature/index.tsx b/web/app/components/app/configuration/config/feature/choose-feature/index.tsx index c9afda5c23..55005d0d68 100644 --- a/web/app/components/app/configuration/config/feature/choose-feature/index.tsx +++ b/web/app/components/app/configuration/config/feature/choose-feature/index.tsx @@ -9,12 +9,14 @@ import Modal from '@/app/components/base/modal' import SuggestedQuestionsAfterAnswerIcon from '@/app/components/app/configuration/base/icons/suggested-questions-after-answer-icon' import { Microphone01 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { Citations } from '@/app/components/base/icons/src/vender/solid/editor' +import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files' type IConfig = { openingStatement: boolean moreLikeThis: boolean suggestedQuestionsAfterAnswer: boolean speechToText: boolean citation: boolean + moderation: boolean } export type IChooseFeatureProps = { @@ -114,6 +116,18 @@ const ChooseFeature: FC = ({ )} + + <> + } + previewImgClassName='' + title={t('appDebug.feature.moderation.title')} + description={t('appDebug.feature.moderation.description')} + value={config.moderation} + onChange={value => onChange('moderation', value)} + /> + +
diff --git a/web/app/components/app/configuration/config/feature/use-feature.tsx b/web/app/components/app/configuration/config/feature/use-feature.tsx index e13bce8b1c..3e52b0920e 100644 --- a/web/app/components/app/configuration/config/feature/use-feature.tsx +++ b/web/app/components/app/configuration/config/feature/use-feature.tsx @@ -11,6 +11,8 @@ function useFeature({ setSpeechToText, citation, setCitation, + moderation, + setModeration, }: { introduction: string setIntroduction: (introduction: string) => void @@ -22,6 +24,8 @@ function useFeature({ setSpeechToText: (speechToText: boolean) => void citation: boolean setCitation: (citation: boolean) => void + moderation: boolean + setModeration: (moderation: boolean) => void }) { const [tempshowOpeningStatement, setTempShowOpeningStatement] = React.useState(!!introduction) useEffect(() => { @@ -41,6 +45,7 @@ function useFeature({ suggestedQuestionsAfterAnswer, speechToText, citation, + moderation, } const handleFeatureChange = (key: string, value: boolean) => { switch (key) { @@ -61,6 +66,9 @@ function useFeature({ break case 'citation': setCitation(value) + break + case 'moderation': + setModeration(value) } } return { diff --git a/web/app/components/app/configuration/config/index.tsx b/web/app/components/app/configuration/config/index.tsx index 95919bd729..5901feb78f 100644 --- a/web/app/components/app/configuration/config/index.tsx +++ b/web/app/components/app/configuration/config/index.tsx @@ -5,6 +5,7 @@ import { useContext } from 'use-context-selector' import produce from 'immer' import { useBoolean, useScroll } from 'ahooks' import DatasetConfig from '../dataset-config' +import Tools from '../tools' import ChatGroup from '../features/chat-group' import ExperienceEnchanceGroup from '../features/experience-enchance-group' import Toolbox from '../toolbox' @@ -19,6 +20,8 @@ import ConfigVar from '@/app/components/app/configuration/config-var' import type { PromptVariable } from '@/models/debug' import { AppType, ModelModeType } from '@/types/app' import { useProviderContext } from '@/context/provider-context' +import { useModalContext } from '@/context/modal-context' + const Config: FC = () => { const { mode, @@ -41,9 +44,12 @@ const Config: FC = () => { setSpeechToTextConfig, citationConfig, setCitationConfig, + moderationConfig, + setModerationConfig, } = useContext(ConfigContext) const isChatApp = mode === AppType.chat const { speech2textDefaultModel } = useProviderContext() + const { setShowModerationSettingModal } = useModalContext() const promptTemplate = modelConfig.configs.prompt_template const promptVariables = modelConfig.configs.prompt_variables @@ -100,6 +106,35 @@ const Config: FC = () => { draft.enabled = value })) }, + moderation: moderationConfig.enabled, + setModeration: (value) => { + setModerationConfig(produce(moderationConfig, (draft) => { + draft.enabled = value + })) + if (value && !moderationConfig.type) { + setShowModerationSettingModal({ + payload: { + enabled: true, + type: 'keywords', + config: { + keywords: '', + inputs_config: { + enabled: true, + preset_response: '', + }, + }, + }, + onSaveCallback: setModerationConfig, + onCancelCallback: () => { + setModerationConfig(produce(moderationConfig, (draft) => { + draft.enabled = false + showChooseFeatureTrue() + })) + }, + }) + showChooseFeatureFalse() + } + }, }) const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && !!speech2textDefaultModel) || featureConfig.citation) @@ -156,6 +191,8 @@ const Config: FC = () => { {/* Dataset */} + + {/* Chat History */} {isAdvancedMode && isChatApp && modelModeType === ModelModeType.completion && ( { {/* Toolbox */} { - hasToolbox && ( - + moderationConfig.enabled && ( + ) } diff --git a/web/app/components/app/configuration/dataset-config/card-item/item.tsx b/web/app/components/app/configuration/dataset-config/card-item/item.tsx new file mode 100644 index 0000000000..2687e77622 --- /dev/null +++ b/web/app/components/app/configuration/dataset-config/card-item/item.tsx @@ -0,0 +1,90 @@ +'use client' +import type { FC } from 'react' +import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' +import SettingsModal from '../settings-modal' +import type { DataSet } from '@/models/datasets' +import { DataSourceType } from '@/models/datasets' +import { formatNumber } from '@/utils/format' +import FileIcon from '@/app/components/base/file-icon' +import { Settings01, Trash03 } from '@/app/components/base/icons/src/vender/line/general' +import { Folder } from '@/app/components/base/icons/src/vender/solid/files' + +type ItemProps = { + className?: string + config: DataSet + onRemove: (id: string) => void + readonly?: boolean + onSave: (newDataset: DataSet) => void +} + +const Item: FC = ({ + config, + onSave, + onRemove, +}) => { + const { t } = useTranslation() + const [showSettingsModal, setShowSettingsModal] = useState(false) + + const handleSave = (newDataset: DataSet) => { + onSave(newDataset) + setShowSettingsModal(false) + } + + return ( +
+ { + config.data_source_type === DataSourceType.FILE && ( +
+ +
+ ) + } + { + config.data_source_type === DataSourceType.NOTION && ( +
+ +
+ ) + } +
+
+
{config.name}
+
+ {formatNumber(config.word_count)} {t('appDebug.feature.dataSet.words')} · {formatNumber(config.document_count)} {t('appDebug.feature.dataSet.textBlocks')} +
+
+ {/* { + config.description && ( +
{config.description}
+ ) + } */} +
+
+
setShowSettingsModal(true)} + > + +
+
onRemove(config.id)} + > + +
+
+ { + showSettingsModal && ( + setShowSettingsModal(false)} + onSave={handleSave} + /> + ) + } +
+ ) +} + +export default Item diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index 036533bef3..7bb141db28 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -6,11 +6,12 @@ import { useContext } from 'use-context-selector' import produce from 'immer' import FeaturePanel from '../base/feature-panel' import OperationBtn from '../base/operation-btn' -import CardItem from './card-item' +import CardItem from './card-item/item' import ParamsConfig from './params-config' import ContextVar from './context-var' import ConfigContext from '@/context/debug-configuration' import { AppType } from '@/types/app' +import type { DataSet } from '@/models/datasets' const Icon = ( @@ -30,7 +31,6 @@ const DatasetConfig: FC = () => { setModelConfig, showSelectDataSet, } = useContext(ConfigContext) - const selectedIds = dataSet.map(item => item.id) const hasData = dataSet.length > 0 @@ -39,6 +39,13 @@ const DatasetConfig: FC = () => { setFormattingChanged(true) } + const handleSave = (newDataset: DataSet) => { + const index = dataSet.findIndex(item => item.id === newDataset.id) + + setDataSet([...dataSet.slice(0, index), newDataset, ...dataSet.slice(index + 1)]) + setFormattingChanged(true) + } + const promptVariables = modelConfig.configs.prompt_variables const promptVariablesToSelect = promptVariables.map(item => ({ name: item.name, @@ -77,10 +84,10 @@ const DatasetConfig: FC = () => {
{dataSet.map(item => ( ))}
diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx new file mode 100644 index 0000000000..f75cdcf596 --- /dev/null +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -0,0 +1,152 @@ +import type { FC } from 'react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio' +import Modal from '@/app/components/base/modal' +import Button from '@/app/components/base/button' +import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' +import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' +import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' +import type { DataSet } from '@/models/datasets' +import { useToastContext } from '@/app/components/base/toast' +import { updateDatasetSetting } from '@/service/datasets' +import { useModalContext } from '@/context/modal-context' + +type SettingsModalProps = { + currentDataset: DataSet + onCancel: () => void + onSave: (newDataset: DataSet) => void +} +const SettingsModal: FC = ({ + currentDataset, + onCancel, + onSave, +}) => { + const { t } = useTranslation() + const { notify } = useToastContext() + const { setShowAccountSettingModal } = useModalContext() + const [loading, setLoading] = useState(false) + const [localeCurrentDataset, setLocaleCurrentDataset] = useState({ ...currentDataset }) + + const handleValueChange = (type: string, value: string) => { + setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value }) + } + + const handleSave = async () => { + if (loading) + return + if (!localeCurrentDataset.name?.trim()) { + notify({ type: 'error', message: t('datasetSettings.form.nameError') }) + return + } + try { + setLoading(true) + const { id, name, description, indexing_technique } = localeCurrentDataset + await updateDatasetSetting({ + datasetId: id, + body: { + name, + description, + indexing_technique, + }, + }) + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + onSave(localeCurrentDataset) + } + catch (e) { + notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) + } + finally { + setLoading(false) + } + } + + return ( + {}} + className='!p-8 !pb-6 !max-w-none !w-[640px]' + > +
+ {t('datasetSettings.title')} +
+
+
+ {t('datasetSettings.form.name')} +
+ handleValueChange('name', e.target.value)} + className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none' + placeholder={t('datasetSettings.form.namePlaceholder') || ''} + /> +
+
+
+ {t('datasetSettings.form.desc')} +
+
+ {t('datasetSettings.form.descInfo')}{t('common.operation.learnMore')} +
+