From 36718c39dc388bc2cc434d353b33c692c703c14e Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 6 Mar 2024 19:03:43 +0800 Subject: [PATCH] features --- .../feature-choose/feature-item/index.tsx | 41 ++++++++++++++++++- .../features/feature-choose/feature-modal.tsx | 4 +- .../base/features/feature-choose/index.tsx | 4 +- .../{config-param.tsx => index.tsx} | 0 .../base/features/feature-panel/index.tsx | 11 +++-- .../feature-panel/moderation/index.tsx | 15 +++++-- .../feature-panel/opening-statement/index.tsx | 26 +++++++++--- web/app/components/base/features/types.ts | 2 + web/app/components/workflow/features.tsx | 13 +++++- .../workflow/header/editing-title.tsx | 2 +- web/app/components/workflow/hooks.ts | 16 +++++++- web/service/workflow.ts | 4 +- 12 files changed, 112 insertions(+), 26 deletions(-) rename web/app/components/base/features/feature-panel/annotation/{config-param.tsx => index.tsx} (100%) diff --git a/web/app/components/base/features/feature-choose/feature-item/index.tsx b/web/app/components/base/features/feature-choose/feature-item/index.tsx index f485e6851d..bde757ae4a 100644 --- a/web/app/components/base/features/feature-choose/feature-item/index.tsx +++ b/web/app/components/base/features/feature-choose/feature-item/index.tsx @@ -1,10 +1,13 @@ 'use client' import type { FC } from 'react' import React, { useCallback } from 'react' +import produce from 'immer' import cn from 'classnames' import s from './style.module.css' import Switch from '@/app/components/base/switch' -import type { FeatureEnum } from '@/app/components/base/features/types' +import { FeatureEnum } from '@/app/components/base/features/types' +import { useFeaturesStore } from '@/app/components/base/features/hooks' +import { useModalContext } from '@/context/modal-context' export type IFeatureItemProps = { icon: React.ReactNode @@ -25,9 +28,43 @@ const FeatureItem: FC = ({ onChange, type, }) => { + const featuresStore = useFeaturesStore() + const { setShowModerationSettingModal } = useModalContext() + const handleChange = useCallback((newValue: boolean) => { + const { + features, + setFeatures, + } = featuresStore!.getState() + + if (newValue && !features.moderation.type && type === FeatureEnum.moderation) { + setShowModerationSettingModal({ + payload: { + enabled: true, + type: 'keywords', + config: { + keywords: '', + inputs_config: { + enabled: true, + preset_response: '', + }, + }, + }, + onSaveCallback: (newModeration) => { + setFeatures(produce(features, (draft) => { + draft.moderation = newModeration + })) + }, + onCancelCallback: () => { + setFeatures(produce(features, (draft) => { + draft.moderation = { enabled: false } + })) + }, + }) + return + } onChange(type, newValue) - }, [type, onChange]) + }, [type, onChange, featuresStore, setShowModerationSettingModal]) return (
diff --git a/web/app/components/base/features/feature-choose/feature-modal.tsx b/web/app/components/base/features/feature-choose/feature-modal.tsx index 2f3bdebb3f..5ff6d205ca 100644 --- a/web/app/components/base/features/feature-choose/feature-modal.tsx +++ b/web/app/components/base/features/feature-choose/feature-modal.tsx @@ -7,6 +7,7 @@ import { useFeatures, useFeaturesStore, } from '../hooks' +import type { OnFeaturesChange } from '../types' import FeatureGroup from './feature-group' import FeatureItem from './feature-item' import Modal from '@/app/components/base/modal' @@ -19,10 +20,9 @@ import { MessageHeartCircle, } from '@/app/components/base/icons/src/vender/solid/communication' import { FeatureEnum } from '@/app/components/base/features/types' -import type { Features } from '@/app/components/base/features/types' export type FeatureModalProps = { - onChange?: (features: Features) => void + onChange?: OnFeaturesChange showTextToSpeechItem?: boolean showSpeechToTextItem?: boolean } diff --git a/web/app/components/base/features/feature-choose/index.tsx b/web/app/components/base/features/feature-choose/index.tsx index 39a07249e0..0f63a34640 100644 --- a/web/app/components/base/features/feature-choose/index.tsx +++ b/web/app/components/base/features/feature-choose/index.tsx @@ -2,13 +2,13 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { useFeatures } from '../hooks' +import type { OnFeaturesChange } from '../types' import FeatureModal from './feature-modal' import Button from '@/app/components/base/button' import { Plus02 } from '@/app/components/base/icons/src/vender/line/general' -import type { Features } from '@/app/components/base/features/types' type ChooseFeatureProps = { - onChange?: (features: Features) => void + onChange?: OnFeaturesChange } const ChooseFeature = ({ onChange, diff --git a/web/app/components/base/features/feature-panel/annotation/config-param.tsx b/web/app/components/base/features/feature-panel/annotation/index.tsx similarity index 100% rename from web/app/components/base/features/feature-panel/annotation/config-param.tsx rename to web/app/components/base/features/feature-panel/annotation/index.tsx diff --git a/web/app/components/base/features/feature-panel/index.tsx b/web/app/components/base/features/feature-panel/index.tsx index 9f4e328d47..4aebf81964 100644 --- a/web/app/components/base/features/feature-panel/index.tsx +++ b/web/app/components/base/features/feature-panel/index.tsx @@ -3,6 +3,7 @@ import { useMemo, } from 'react' import { useTranslation } from 'react-i18next' +import type { OnFeaturesChange } from '../types' import { useFeatures } from '../hooks' import OpeningStatement from './opening-statement' import type { OpeningStatementProps } from './opening-statement' @@ -11,14 +12,16 @@ import TextToSpeech from './text-to-speech' import SpeechToText from './speech-to-text' import Citation from './citation' import Moderation from './moderation' -import Annotation from './annotation/config-param' -import type { AnnotationProps } from './annotation/config-param' +import Annotation from './annotation' +import type { AnnotationProps } from './annotation' export type FeaturePanelProps = { + onChange?: OnFeaturesChange openingStatementProps: OpeningStatementProps annotationProps: AnnotationProps } const FeaturePanel = ({ + onChange, openingStatementProps, annotationProps, }: FeaturePanelProps) => { @@ -50,7 +53,7 @@ const FeaturePanel = ({
{ features.opening.enabled && ( - + ) } { @@ -92,7 +95,7 @@ const FeaturePanel = ({
{ features.moderation.enabled && ( - + ) } { diff --git a/web/app/components/base/features/feature-panel/moderation/index.tsx b/web/app/components/base/features/feature-panel/moderation/index.tsx index b3364e538c..c5e215b560 100644 --- a/web/app/components/base/features/feature-panel/moderation/index.tsx +++ b/web/app/components/base/features/feature-panel/moderation/index.tsx @@ -7,13 +7,19 @@ import { useFeatures, useFeaturesStore, } from '../../hooks' +import type { OnFeaturesChange } from '../../types' import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import { useModalContext } from '@/context/modal-context' import { fetchCodeBasedExtensionList } from '@/service/common' import I18n from '@/context/i18n' -const Moderation = () => { +type ModerationProps = { + onChange?: OnFeaturesChange +} +const Moderation = ({ + onChange, +}: ModerationProps) => { const { t } = useTranslation() const { setShowModerationSettingModal } = useModalContext() const { locale } = useContext(I18n) @@ -33,9 +39,12 @@ const Moderation = () => { setShowModerationSettingModal({ payload: moderation as any, onSaveCallback: (newModeration) => { - setFeatures(produce(features, (draft) => { + const newFeatures = produce(features, (draft) => { draft.moderation = newModeration - })) + }) + setFeatures(newFeatures) + if (onChange) + onChange(newFeatures) }, }) } diff --git a/web/app/components/base/features/feature-panel/opening-statement/index.tsx b/web/app/components/base/features/feature-panel/opening-statement/index.tsx index 069cd6f090..4c331aa6e7 100644 --- a/web/app/components/base/features/feature-panel/opening-statement/index.tsx +++ b/web/app/components/base/features/feature-panel/opening-statement/index.tsx @@ -11,6 +11,7 @@ import { useFeatures, useFeaturesStore, } from '../../hooks' +import type { OnFeaturesChange } from '../../types' import Panel from '@/app/components/app/configuration/base/feature-panel' import Button from '@/app/components/base/button' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' @@ -24,6 +25,7 @@ import type { PromptVariable } from '@/models/debug' const MAX_QUESTION_NUM = 5 export type OpeningStatementProps = { + onChange?: OnFeaturesChange readonly?: boolean promptVariables?: PromptVariable[] onAutoAddPromptVariable: (variable: PromptVariable[]) => void @@ -33,6 +35,7 @@ export type OpeningStatementProps = { const regex = /\{\{([^}]+)\}\}/g const OpeningStatement: FC = ({ + onChange, readonly, promptVariables = [], onAutoAddPromptVariable, @@ -112,10 +115,14 @@ const OpeningStatement: FC = ({ setFeatures, } = getState() - setFeatures(produce(features, (draft) => { + const newFeatures = produce(features, (draft) => { draft.opening.opening_statement = tempValue draft.opening.suggested_questions = tempSuggestedQuestions - })) + }) + setFeatures(newFeatures) + + if (onChange) + onChange(newFeatures) } const cancelAutoAddVar = () => { @@ -125,9 +132,13 @@ const OpeningStatement: FC = ({ setFeatures, } = getState() - setFeatures(produce(features, (draft) => { + const newFeatures = produce(features, (draft) => { draft.opening.opening_statement = tempValue - })) + }) + setFeatures(newFeatures) + + if (onChange) + onChange(newFeatures) hideConfirmAddVar() setBlur() } @@ -139,9 +150,12 @@ const OpeningStatement: FC = ({ setFeatures, } = getState() - setFeatures(produce(features, (draft) => { + const newFeatures = produce(features, (draft) => { draft.opening.opening_statement = tempValue - })) + }) + setFeatures(newFeatures) + if (onChange) + onChange(newFeatures) onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))]) hideConfirmAddVar() setBlur() diff --git a/web/app/components/base/features/types.ts b/web/app/components/base/features/types.ts index c20ed36986..987367974e 100644 --- a/web/app/components/base/features/types.ts +++ b/web/app/components/base/features/types.ts @@ -51,3 +51,5 @@ export type Features = { [FeatureEnum.moderation]: SensitiveWordAvoidance [FeatureEnum.annotation]: AnnotationReply } + +export type OnFeaturesChange = (features: Features) => void diff --git a/web/app/components/workflow/features.tsx b/web/app/components/workflow/features.tsx index 6881fdc102..efb9de15cb 100644 --- a/web/app/components/workflow/features.tsx +++ b/web/app/components/workflow/features.tsx @@ -1,6 +1,10 @@ -import { memo } from 'react' +import { + memo, + useCallback, +} from 'react' import { useTranslation } from 'react-i18next' import { useStore } from './store' +import { useWorkflow } from './hooks' import { XClose } from '@/app/components/base/icons/src/vender/line/general' import { FeaturesChoose, @@ -10,13 +14,18 @@ import { const Features = () => { const { t } = useTranslation() const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel) + const { handleSyncWorkflowDraft } = useWorkflow() + + const handleFeaturesChange = useCallback(() => { + handleSyncWorkflowDraft() + }, [handleSyncWorkflowDraft]) return (
{t('workflow.common.features')}
- +
{ draftUpdatedAt && ( <> ยท - {t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt).format('HH:mm:ss')} + {t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt * 1000).format('HH:mm:ss')} ) } diff --git a/web/app/components/workflow/hooks.ts b/web/app/components/workflow/hooks.ts index 26581bb121..7164f4cca4 100644 --- a/web/app/components/workflow/hooks.ts +++ b/web/app/components/workflow/hooks.ts @@ -52,6 +52,7 @@ export const useWorkflow = () => { const appId = useAppStore.getState().appDetail?.id if (appId) { + const features = featuresStore!.getState().features syncWorkflowDraft({ url: `/apps/${appId}/workflows/draft`, params: { @@ -60,11 +61,22 @@ export const useWorkflow = () => { edges, viewport: getViewport(), }, - features: {}, + features: { + opening_statement: features.opening.opening_statement, + suggested_questions: features.opening.suggested_questions, + suggested_questions_after_answer: features.suggested, + text_to_speech: features.text2speech, + speech_to_text: features.speech2text, + retriever_resource: features.citation, + sensitive_word_avoidance: features.moderation, + annotation_reply: features.annotation, + }, }, + }).then((res) => { + useStore.setState({ draftUpdatedAt: res.updated_at }) }) } - }, [store, reactFlow]) + }, [store, reactFlow, featuresStore]) const handleLayout = useCallback(async () => { const { diff --git a/web/service/workflow.ts b/web/service/workflow.ts index 35be70912d..d60c7e940c 100644 --- a/web/service/workflow.ts +++ b/web/service/workflow.ts @@ -9,8 +9,8 @@ export const fetchWorkflowDraft: Fetcher = ( return get(url, {}, { silent: true }) } -export const syncWorkflowDraft: Fetcher }> = ({ url, params }) => { - return post(url, { body: params }) +export const syncWorkflowDraft = ({ url, params }: { url: string; params: Pick }) => { + return post(url, { body: params }) } export const fetchNodesDefaultConfigs: Fetcher = (url) => {