From 6483beb09677c0f7e3c3f5fbe7631b37562d038b Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 6 Jun 2023 10:52:02 +0800 Subject: [PATCH] Feat/auto rule generate (#300) --- .../app/configuration/config-prompt/index.tsx | 41 +-- .../app/configuration/config-var/index.tsx | 169 +++++++------ .../config/automatic/automatic-btn.tsx | 33 +++ .../config/automatic/get-automatic-res.tsx | 205 +++++++++++++++ .../app/configuration/config/index.tsx | 48 ++-- .../chat-group/opening-statement/index.tsx | 67 ++--- web/app/components/base/block-input/index.tsx | 62 ++--- .../datasets/create/step-two/index.tsx | 235 +++++++++--------- .../documents/detail/settings/index.tsx | 17 +- web/i18n/lang/app-debug.en.ts | 180 ++++++++------ web/i18n/lang/app-debug.zh.ts | 178 +++++++------ web/i18n/lang/dataset-creation.en.ts | 4 +- web/i18n/lang/dataset-creation.zh.ts | 6 +- web/service/debug.ts | 23 +- 14 files changed, 808 insertions(+), 460 deletions(-) create mode 100644 web/app/components/app/configuration/config/automatic/automatic-btn.tsx create mode 100644 web/app/components/app/configuration/config/automatic/get-automatic-res.tsx diff --git a/web/app/components/app/configuration/config-prompt/index.tsx b/web/app/components/app/configuration/config-prompt/index.tsx index b18d148c4c..bf727c70ed 100644 --- a/web/app/components/app/configuration/config-prompt/index.tsx +++ b/web/app/components/app/configuration/config-prompt/index.tsx @@ -1,26 +1,29 @@ 'use client' import type { FC } from 'react' import React from 'react' +import { useTranslation } from 'react-i18next' +import { useBoolean } from 'ahooks' +import cn from 'classnames' +import ConfirmAddVar from './confirm-add-var' import BlockInput from '@/app/components/base/block-input' import type { PromptVariable } from '@/models/debug' import Tooltip from '@/app/components/base/tooltip' import { AppType } from '@/types/app' import { getNewVar } from '@/utils/var' -import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import ConfirmAddVar from './confirm-add-var' export type IPromptProps = { mode: AppType promptTemplate: string promptVariables: PromptVariable[] - onChange: (promp: string, promptVariables: PromptVariable[]) => void + readonly?: boolean + onChange?: (promp: string, promptVariables: PromptVariable[]) => void } const Prompt: FC = ({ mode, promptTemplate, promptVariables, + readonly = false, onChange, }) => { const { t } = useTranslation() @@ -45,35 +48,39 @@ const Prompt: FC = ({ showConfirmAddVar() return } - onChange(newTemplates, []) + onChange?.(newTemplates, []) } const handleAutoAdd = (isAdd: boolean) => { return () => { - onChange(newTemplates, isAdd ? newPromptVariables : []) + onChange?.(newTemplates, isAdd ? newPromptVariables : []) hideConfirmAddVar() } } return ( -
+
{mode === AppType.chat ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}
- - {t('appDebug.promptTip')} -
} - selector='config-prompt-tooltip'> - - - - + {!readonly && ( + + {t('appDebug.promptTip')} +
} + selector='config-prompt-tooltip'> + + + + + )} +
{ handleChange(value, vars) @@ -82,7 +89,7 @@ const Prompt: FC = ({ {isShowConfirmAddVar && ( v.name)} + varNameArr={newPromptVariables.map(v => v.name)} onConfrim={handleAutoAdd(true)} onCancel={handleAutoAdd(false)} onHide={hideConfirmAddVar} diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 45411cc8a5..1c706d8a63 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -2,29 +2,28 @@ import type { FC } from 'react' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import Panel from '../base/feature-panel' -import Tooltip from '@/app/components/base/tooltip' -import type { PromptVariable } from '@/models/debug' import { Cog8ToothIcon, TrashIcon } from '@heroicons/react/24/outline' import { useBoolean } from 'ahooks' -import EditModel from './config-model' -import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config' -import { getNewVar } from '@/utils/var' +import Panel from '../base/feature-panel' import OperationBtn from '../base/operation-btn' -import Switch from '@/app/components/base/switch' -import IconTypeIcon from './input-type-icon' -import { checkKeys } from '@/utils/var' -import Toast from '@/app/components/base/toast' - -import s from './style.module.css' import VarIcon from '../base/icons/var-icon' +import EditModel from './config-model' +import IconTypeIcon from './input-type-icon' +import s from './style.module.css' +import Tooltip from '@/app/components/base/tooltip' +import type { PromptVariable } from '@/models/debug' +import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config' +import { checkKeys, getNewVar } from '@/utils/var' +import Switch from '@/app/components/base/switch' +import Toast from '@/app/components/base/toast' export type IConfigVarProps = { promptVariables: PromptVariable[] - onPromptVariablesChange: (promptVariables: PromptVariable[]) => void + readonly?: boolean + onPromptVariablesChange?: (promptVariables: PromptVariable[]) => void } -const ConfigVar: FC = ({ promptVariables, onPromptVariablesChange }) => { +const ConfigVar: FC = ({ promptVariables, readonly, onPromptVariablesChange }) => { const { t } = useTranslation() const hasVar = promptVariables.length > 0 const promptVariableObj = (() => { @@ -39,16 +38,17 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan if (!(key in promptVariableObj)) return const newPromptVariables = promptVariables.map((item) => { - if (item.key === key) + if (item.key === key) { return { ...item, - [updateKey]: newValue + [updateKey]: newValue, } + } return item }) - onPromptVariablesChange(newPromptVariables) + onPromptVariablesChange?.(newPromptVariables) } const batchUpdatePromptVariable = (key: string, updateKeys: string[], newValues: any[]) => { @@ -66,53 +66,55 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan return item }) - onPromptVariablesChange(newPromptVariables) + onPromptVariablesChange?.(newPromptVariables) } - const updatePromptKey = (index: number, newKey: string) => { const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }) + message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), }) return } const newPromptVariables = promptVariables.map((item, i) => { - if (i === index) + if (i === index) { return { ...item, key: newKey, } + } return item }) - onPromptVariablesChange(newPromptVariables) + onPromptVariablesChange?.(newPromptVariables) } const updatePromptNameIfNameEmpty = (index: number, newKey: string) => { - if (!newKey) return + if (!newKey) + return const newPromptVariables = promptVariables.map((item, i) => { - if (i === index && !item.name) + if (i === index && !item.name) { return { ...item, name: newKey, } + } return item }) - onPromptVariablesChange(newPromptVariables) + onPromptVariablesChange?.(newPromptVariables) } const handleAddVar = () => { const newVar = getNewVar('') - onPromptVariablesChange([...promptVariables, newVar]) + onPromptVariablesChange?.([...promptVariables, newVar]) } const handleRemoveVar = (index: number) => { - onPromptVariablesChange(promptVariables.filter((_, i) => i !== index)) + onPromptVariablesChange?.(promptVariables.filter((_, i) => i !== index)) } const [currKey, setCurrKey] = useState(null) @@ -132,16 +134,18 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan title={
{t('appDebug.variableTitle')}
- - {t('appDebug.variableTip')} -
} selector='config-var-tooltip'> - - - - + {!readonly && ( + + {t('appDebug.variableTip')} + } selector='config-var-tooltip'> + + + + + )} } - headerRight={} + headerRight={!readonly ? : null} > {!hasVar && (
{t('appDebug.notSetVar')}
@@ -153,8 +157,13 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan {t('appDebug.variableTable.key')} {t('appDebug.variableTable.name')} - {t('appDebug.variableTable.optional')} - {t('appDebug.variableTable.action')} + {!readonly && ( + <> + {t('appDebug.variableTable.optional')} + {t('appDebug.variableTable.action')} + + )} + @@ -163,42 +172,57 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan
- updatePromptKey(index, e.target.value)} - onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)} - maxLength={getMaxVarNameLength(name)} - className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" - /> + {!readonly + ? ( + updatePromptKey(index, e.target.value)} + onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)} + maxLength={getMaxVarNameLength(name)} + className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" + /> + ) + : ( +
{key}
+ )}
- updatePromptVariable(key, 'name', e.target.value)} - maxLength={getMaxVarNameLength(name)} - className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" - /> - - -
- updatePromptVariable(key, 'required', !value)} /> -
- - -
-
handleConfig(key)}> - -
-
handleRemoveVar(index)} > - -
-
+ {!readonly + ? ( + updatePromptVariable(key, 'name', e.target.value)} + maxLength={getMaxVarNameLength(name)} + className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" + />) + : ( +
{name}
+ )} + {!readonly && ( + <> + +
+ updatePromptVariable(key, 'required', !value)} /> +
+ + +
+
handleConfig(key)}> + +
+
handleRemoveVar(index)} > + +
+
+ + + )} ))} @@ -212,11 +236,12 @@ const ConfigVar: FC = ({ promptVariables, onPromptVariablesChan isShow={isShowEditModal} onClose={hideEditModal} onConfirm={({ type, value }) => { - if (type === 'string') { + if (type === 'string') batchUpdatePromptVariable(currKey as string, ['type', 'max_length'], [type, value || DEFAULT_VALUE_MAX_LEN]) - } else { + + else batchUpdatePromptVariable(currKey as string, ['type', 'options'], [type, value || []]) - } + hideEditModal() }} /> diff --git a/web/app/components/app/configuration/config/automatic/automatic-btn.tsx b/web/app/components/app/configuration/config/automatic/automatic-btn.tsx new file mode 100644 index 0000000000..d5ba091e60 --- /dev/null +++ b/web/app/components/app/configuration/config/automatic/automatic-btn.tsx @@ -0,0 +1,33 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' + +export type IAutomaticBtnProps = { + onClick: () => void +} + +const leftIcon = ( + + + + + + +) +const AutomaticBtn: FC = ({ + onClick, +}) => { + const { t } = useTranslation() + + return ( + + ) +} +export default React.memo(AutomaticBtn) diff --git a/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx new file mode 100644 index 0000000000..1928dd8f90 --- /dev/null +++ b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx @@ -0,0 +1,205 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useBoolean } from 'ahooks' +import Modal from '@/app/components/base/modal' +import Button from '@/app/components/base/button' +import Toast from '@/app/components/base/toast' +import { generateRule } from '@/service/debug' +import ConfigPrompt from '@/app/components/app/configuration/config-prompt' +import { AppType } from '@/types/app' +import ConfigVar from '@/app/components/app/configuration/config-var' +import OpeningStatement from '@/app/components/app/configuration/features/chat-group/opening-statement' +import GroupName from '@/app/components/app/configuration/base/group-name' +import Loading from '@/app/components/base/loading' +import Confirm from '@/app/components/base/confirm' + +const noDataIcon = ( + + + +) + +export type AutomaticRes = { + prompt: string + variables: string[] + opening_statement: string +} + +export type IGetAutomaticResProps = { + mode: AppType + isShow: boolean + onClose: () => void + onFinished: (res: AutomaticRes) => void +} + +const genIcon = ( + + + + + +) + +const GetAutomaticRes: FC = ({ + mode, + isShow, + onClose, + // appId, + onFinished, +}) => { + const { t } = useTranslation() + + const [audiences, setAudiences] = React.useState('') + const [hopingToSolve, setHopingToSolve] = React.useState('') + const isValid = () => { + if (audiences.trim() === '') { + Toast.notify({ + type: 'error', + message: t('appDebug.automatic.audiencesRequired'), + }) + return false + } + if (hopingToSolve.trim() === '') { + Toast.notify({ + type: 'error', + message: t('appDebug.automatic.problemRequired'), + }) + return false + } + return true + } + const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false) + const [res, setRes] = React.useState(null) + + const renderLoading = ( +
+ +
{t('appDebug.automatic.loading')}
+
+ ) + + const renderNoData = ( +
+ {noDataIcon} +
{t('appDebug.automatic.noData')}
+
+ ) + + const onGenerate = async () => { + if (!isValid()) + return + if (isLoading) + return + setLoadingTrue() + try { + const res = await generateRule({ + audiences, + hoping_to_solve: hopingToSolve, + }) + setRes(res as AutomaticRes) + } + finally { + setLoadingFalse() + } + } + + const [showConfirmOverwrite, setShowConfirmOverwrite] = React.useState(false) + + return ( + +
+
+
+
{t('appDebug.automatic.title')}
+
{t('appDebug.automatic.description')}
+
+ {/* inputs */} +
+
+
{t('appDebug.automatic.intendedAudience')}
+ setAudiences(e.target.value)} /> +
+
+
{t('appDebug.automatic.solveProblem')}
+ - ) - : ( -
- )} + {(hasValue || (!hasValue && isFocus)) ? ( + <> + {isFocus + ? ( + + ) + : ( +
+ )} - {/* Operation Bar */} - {isFocus - && ( + {/* Operation Bar */} + {isFocus && (
{t('appDebug.openingStatement.varTip')}
@@ -164,9 +165,9 @@ const OpeningStatement: FC = ({
)} - ) : ( -
{t('appDebug.openingStatement.noDataPlaceHolder')}
- )} + ) : ( +
{t('appDebug.openingStatement.noDataPlaceHolder')}
+ )} {isShowConfirmAddVar && ( void } const BlockInput: FC = ({ value = '', className, + readonly = false, onConfirm, }) => { const { t } = useTranslation() @@ -113,7 +115,7 @@ const BlockInput: FC = ({ const editAreaClassName = 'focus:outline-none bg-transparent text-sm' const textAreaContent = ( -
setIsEditing(true)}> +
!readonly && setIsEditing(true)}> {isEditing ?