Feat/auto rule generate (#300)

This commit is contained in:
Joel 2023-06-06 10:52:02 +08:00 committed by GitHub
parent e61c84ca72
commit 6483beb096
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 808 additions and 460 deletions

View File

@ -1,26 +1,29 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React 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 BlockInput from '@/app/components/base/block-input'
import type { PromptVariable } from '@/models/debug' import type { PromptVariable } from '@/models/debug'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import { AppType } from '@/types/app' import { AppType } from '@/types/app'
import { getNewVar } from '@/utils/var' import { getNewVar } from '@/utils/var'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import ConfirmAddVar from './confirm-add-var'
export type IPromptProps = { export type IPromptProps = {
mode: AppType mode: AppType
promptTemplate: string promptTemplate: string
promptVariables: PromptVariable[] promptVariables: PromptVariable[]
onChange: (promp: string, promptVariables: PromptVariable[]) => void readonly?: boolean
onChange?: (promp: string, promptVariables: PromptVariable[]) => void
} }
const Prompt: FC<IPromptProps> = ({ const Prompt: FC<IPromptProps> = ({
mode, mode,
promptTemplate, promptTemplate,
promptVariables, promptVariables,
readonly = false,
onChange, onChange,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -45,35 +48,39 @@ const Prompt: FC<IPromptProps> = ({
showConfirmAddVar() showConfirmAddVar()
return return
} }
onChange(newTemplates, []) onChange?.(newTemplates, [])
} }
const handleAutoAdd = (isAdd: boolean) => { const handleAutoAdd = (isAdd: boolean) => {
return () => { return () => {
onChange(newTemplates, isAdd ? newPromptVariables : []) onChange?.(newTemplates, isAdd ? newPromptVariables : [])
hideConfirmAddVar() hideConfirmAddVar()
} }
} }
return ( return (
<div className='relative rounded-xl border border-[#2D0DEE] bg-gray-25'> <div className={cn(!readonly ? 'border border-[#2D0DEE] bg-gray-25' : 'bg-gray-50', 'relative rounded-xl')}>
<div className="flex items-center h-11 pl-3 gap-1"> <div className="flex items-center h-11 pl-3 gap-1">
<svg width="14" height="13" viewBox="0 0 14 13" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="13" viewBox="0 0 14 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M3.00001 0.100098C3.21218 0.100098 3.41566 0.184383 3.56569 0.334412C3.71572 0.484441 3.80001 0.687924 3.80001 0.900098V1.7001H4.60001C4.81218 1.7001 5.01566 1.78438 5.16569 1.93441C5.31572 2.08444 5.40001 2.28792 5.40001 2.5001C5.40001 2.71227 5.31572 2.91575 5.16569 3.06578C5.01566 3.21581 4.81218 3.3001 4.60001 3.3001H3.80001V4.1001C3.80001 4.31227 3.71572 4.51575 3.56569 4.66578C3.41566 4.81581 3.21218 4.9001 3.00001 4.9001C2.78783 4.9001 2.58435 4.81581 2.43432 4.66578C2.28429 4.51575 2.20001 4.31227 2.20001 4.1001V3.3001H1.40001C1.18783 3.3001 0.98435 3.21581 0.834321 3.06578C0.684292 2.91575 0.600006 2.71227 0.600006 2.5001C0.600006 2.28792 0.684292 2.08444 0.834321 1.93441C0.98435 1.78438 1.18783 1.7001 1.40001 1.7001H2.20001V0.900098C2.20001 0.687924 2.28429 0.484441 2.43432 0.334412C2.58435 0.184383 2.78783 0.100098 3.00001 0.100098ZM3.00001 8.1001C3.21218 8.1001 3.41566 8.18438 3.56569 8.33441C3.71572 8.48444 3.80001 8.68792 3.80001 8.9001V9.7001H4.60001C4.81218 9.7001 5.01566 9.78438 5.16569 9.93441C5.31572 10.0844 5.40001 10.2879 5.40001 10.5001C5.40001 10.7123 5.31572 10.9158 5.16569 11.0658C5.01566 11.2158 4.81218 11.3001 4.60001 11.3001H3.80001V12.1001C3.80001 12.3123 3.71572 12.5158 3.56569 12.6658C3.41566 12.8158 3.21218 12.9001 3.00001 12.9001C2.78783 12.9001 2.58435 12.8158 2.43432 12.6658C2.28429 12.5158 2.20001 12.3123 2.20001 12.1001V11.3001H1.40001C1.18783 11.3001 0.98435 11.2158 0.834321 11.0658C0.684292 10.9158 0.600006 10.7123 0.600006 10.5001C0.600006 10.2879 0.684292 10.0844 0.834321 9.93441C0.98435 9.78438 1.18783 9.7001 1.40001 9.7001H2.20001V8.9001C2.20001 8.68792 2.28429 8.48444 2.43432 8.33441C2.58435 8.18438 2.78783 8.1001 3.00001 8.1001ZM8.60001 0.100098C8.77656 0.100041 8.94817 0.158388 9.0881 0.266047C9.22802 0.373706 9.32841 0.52463 9.37361 0.695298L10.3168 4.2601L13 5.8073C13.1216 5.87751 13.2226 5.9785 13.2928 6.10011C13.363 6.22173 13.4 6.35967 13.4 6.5001C13.4 6.64052 13.363 6.77847 13.2928 6.90008C13.2226 7.02169 13.1216 7.12268 13 7.1929L10.3168 8.7409L9.37281 12.3049C9.32753 12.4754 9.22716 12.6262 9.08732 12.7337C8.94748 12.8413 8.77602 12.8996 8.59961 12.8996C8.42319 12.8996 8.25173 12.8413 8.11189 12.7337C7.97205 12.6262 7.87169 12.4754 7.82641 12.3049L6.88321 8.7401L4.20001 7.1929C4.0784 7.12268 3.97742 7.02169 3.90721 6.90008C3.837 6.77847 3.80004 6.64052 3.80004 6.5001C3.80004 6.35967 3.837 6.22173 3.90721 6.10011C3.97742 5.9785 4.0784 5.87751 4.20001 5.8073L6.88321 4.2593L7.82721 0.695298C7.87237 0.524762 7.97263 0.373937 8.1124 0.266291C8.25216 0.158646 8.42359 0.100217 8.60001 0.100098Z" fill="#5850EC" /> <path fillRule="evenodd" clipRule="evenodd" d="M3.00001 0.100098C3.21218 0.100098 3.41566 0.184383 3.56569 0.334412C3.71572 0.484441 3.80001 0.687924 3.80001 0.900098V1.7001H4.60001C4.81218 1.7001 5.01566 1.78438 5.16569 1.93441C5.31572 2.08444 5.40001 2.28792 5.40001 2.5001C5.40001 2.71227 5.31572 2.91575 5.16569 3.06578C5.01566 3.21581 4.81218 3.3001 4.60001 3.3001H3.80001V4.1001C3.80001 4.31227 3.71572 4.51575 3.56569 4.66578C3.41566 4.81581 3.21218 4.9001 3.00001 4.9001C2.78783 4.9001 2.58435 4.81581 2.43432 4.66578C2.28429 4.51575 2.20001 4.31227 2.20001 4.1001V3.3001H1.40001C1.18783 3.3001 0.98435 3.21581 0.834321 3.06578C0.684292 2.91575 0.600006 2.71227 0.600006 2.5001C0.600006 2.28792 0.684292 2.08444 0.834321 1.93441C0.98435 1.78438 1.18783 1.7001 1.40001 1.7001H2.20001V0.900098C2.20001 0.687924 2.28429 0.484441 2.43432 0.334412C2.58435 0.184383 2.78783 0.100098 3.00001 0.100098ZM3.00001 8.1001C3.21218 8.1001 3.41566 8.18438 3.56569 8.33441C3.71572 8.48444 3.80001 8.68792 3.80001 8.9001V9.7001H4.60001C4.81218 9.7001 5.01566 9.78438 5.16569 9.93441C5.31572 10.0844 5.40001 10.2879 5.40001 10.5001C5.40001 10.7123 5.31572 10.9158 5.16569 11.0658C5.01566 11.2158 4.81218 11.3001 4.60001 11.3001H3.80001V12.1001C3.80001 12.3123 3.71572 12.5158 3.56569 12.6658C3.41566 12.8158 3.21218 12.9001 3.00001 12.9001C2.78783 12.9001 2.58435 12.8158 2.43432 12.6658C2.28429 12.5158 2.20001 12.3123 2.20001 12.1001V11.3001H1.40001C1.18783 11.3001 0.98435 11.2158 0.834321 11.0658C0.684292 10.9158 0.600006 10.7123 0.600006 10.5001C0.600006 10.2879 0.684292 10.0844 0.834321 9.93441C0.98435 9.78438 1.18783 9.7001 1.40001 9.7001H2.20001V8.9001C2.20001 8.68792 2.28429 8.48444 2.43432 8.33441C2.58435 8.18438 2.78783 8.1001 3.00001 8.1001ZM8.60001 0.100098C8.77656 0.100041 8.94817 0.158388 9.0881 0.266047C9.22802 0.373706 9.32841 0.52463 9.37361 0.695298L10.3168 4.2601L13 5.8073C13.1216 5.87751 13.2226 5.9785 13.2928 6.10011C13.363 6.22173 13.4 6.35967 13.4 6.5001C13.4 6.64052 13.363 6.77847 13.2928 6.90008C13.2226 7.02169 13.1216 7.12268 13 7.1929L10.3168 8.7409L9.37281 12.3049C9.32753 12.4754 9.22716 12.6262 9.08732 12.7337C8.94748 12.8413 8.77602 12.8996 8.59961 12.8996C8.42319 12.8996 8.25173 12.8413 8.11189 12.7337C7.97205 12.6262 7.87169 12.4754 7.82641 12.3049L6.88321 8.7401L4.20001 7.1929C4.0784 7.12268 3.97742 7.02169 3.90721 6.90008C3.837 6.77847 3.80004 6.64052 3.80004 6.5001C3.80004 6.35967 3.837 6.22173 3.90721 6.10011C3.97742 5.9785 4.0784 5.87751 4.20001 5.8073L6.88321 4.2593L7.82721 0.695298C7.87237 0.524762 7.97263 0.373937 8.1124 0.266291C8.25216 0.158646 8.42359 0.100217 8.60001 0.100098Z" fill="#5850EC" />
</svg> </svg>
<div className="h2">{mode === AppType.chat ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div> <div className="h2">{mode === AppType.chat ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
<Tooltip {!readonly && (
htmlContent={<div className='w-[180px]'> <Tooltip
{t('appDebug.promptTip')} htmlContent={<div className='w-[180px]'>
</div>} {t('appDebug.promptTip')}
selector='config-prompt-tooltip'> </div>}
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg"> selector='config-prompt-tooltip'>
<path d="M8.66667 11.1667H8V8.5H7.33333M8 5.83333H8.00667M14 8.5C14 9.28793 13.8448 10.0681 13.5433 10.7961C13.2417 11.5241 12.7998 12.1855 12.2426 12.7426C11.6855 13.2998 11.0241 13.7417 10.2961 14.0433C9.56815 14.3448 8.78793 14.5 8 14.5C7.21207 14.5 6.43185 14.3448 5.7039 14.0433C4.97595 13.7417 4.31451 13.2998 3.75736 12.7426C3.20021 12.1855 2.75825 11.5241 2.45672 10.7961C2.15519 10.0681 2 9.28793 2 8.5C2 6.9087 2.63214 5.38258 3.75736 4.25736C4.88258 3.13214 6.4087 2.5 8 2.5C9.5913 2.5 11.1174 3.13214 12.2426 4.25736C13.3679 5.38258 14 6.9087 14 8.5Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> <svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
</svg> <path d="M8.66667 11.1667H8V8.5H7.33333M8 5.83333H8.00667M14 8.5C14 9.28793 13.8448 10.0681 13.5433 10.7961C13.2417 11.5241 12.7998 12.1855 12.2426 12.7426C11.6855 13.2998 11.0241 13.7417 10.2961 14.0433C9.56815 14.3448 8.78793 14.5 8 14.5C7.21207 14.5 6.43185 14.3448 5.7039 14.0433C4.97595 13.7417 4.31451 13.2998 3.75736 12.7426C3.20021 12.1855 2.75825 11.5241 2.45672 10.7961C2.15519 10.0681 2 9.28793 2 8.5C2 6.9087 2.63214 5.38258 3.75736 4.25736C4.88258 3.13214 6.4087 2.5 8 2.5C9.5913 2.5 11.1174 3.13214 12.2426 4.25736C13.3679 5.38258 14 6.9087 14 8.5Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</Tooltip> </svg>
</Tooltip>
)}
</div> </div>
<BlockInput <BlockInput
readonly={readonly}
value={promptTemplate} value={promptTemplate}
onConfirm={(value: string, vars: string[]) => { onConfirm={(value: string, vars: string[]) => {
handleChange(value, vars) handleChange(value, vars)
@ -82,7 +89,7 @@ const Prompt: FC<IPromptProps> = ({
{isShowConfirmAddVar && ( {isShowConfirmAddVar && (
<ConfirmAddVar <ConfirmAddVar
varNameArr={newPromptVariables.map((v) => v.name)} varNameArr={newPromptVariables.map(v => v.name)}
onConfrim={handleAutoAdd(true)} onConfrim={handleAutoAdd(true)}
onCancel={handleAutoAdd(false)} onCancel={handleAutoAdd(false)}
onHide={hideConfirmAddVar} onHide={hideConfirmAddVar}

View File

@ -2,29 +2,28 @@
import type { FC } from 'react' import type { FC } from 'react'
import React, { useState } from 'react' import React, { useState } from 'react'
import { useTranslation } from 'react-i18next' 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 { Cog8ToothIcon, TrashIcon } from '@heroicons/react/24/outline'
import { useBoolean } from 'ahooks' import { useBoolean } from 'ahooks'
import EditModel from './config-model' import Panel from '../base/feature-panel'
import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config'
import { getNewVar } from '@/utils/var'
import OperationBtn from '../base/operation-btn' 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 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 = { export type IConfigVarProps = {
promptVariables: PromptVariable[] promptVariables: PromptVariable[]
onPromptVariablesChange: (promptVariables: PromptVariable[]) => void readonly?: boolean
onPromptVariablesChange?: (promptVariables: PromptVariable[]) => void
} }
const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChange }) => { const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVariablesChange }) => {
const { t } = useTranslation() const { t } = useTranslation()
const hasVar = promptVariables.length > 0 const hasVar = promptVariables.length > 0
const promptVariableObj = (() => { const promptVariableObj = (() => {
@ -39,16 +38,17 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
if (!(key in promptVariableObj)) if (!(key in promptVariableObj))
return return
const newPromptVariables = promptVariables.map((item) => { const newPromptVariables = promptVariables.map((item) => {
if (item.key === key) if (item.key === key) {
return { return {
...item, ...item,
[updateKey]: newValue [updateKey]: newValue,
} }
}
return item return item
}) })
onPromptVariablesChange(newPromptVariables) onPromptVariablesChange?.(newPromptVariables)
} }
const batchUpdatePromptVariable = (key: string, updateKeys: string[], newValues: any[]) => { const batchUpdatePromptVariable = (key: string, updateKeys: string[], newValues: any[]) => {
@ -66,53 +66,55 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
return item return item
}) })
onPromptVariablesChange(newPromptVariables) onPromptVariablesChange?.(newPromptVariables)
} }
const updatePromptKey = (index: number, newKey: string) => { const updatePromptKey = (index: number, newKey: string) => {
const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true)
if (!isValid) { if (!isValid) {
Toast.notify({ Toast.notify({
type: 'error', type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }) message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
}) })
return return
} }
const newPromptVariables = promptVariables.map((item, i) => { const newPromptVariables = promptVariables.map((item, i) => {
if (i === index) if (i === index) {
return { return {
...item, ...item,
key: newKey, key: newKey,
} }
}
return item return item
}) })
onPromptVariablesChange(newPromptVariables) onPromptVariablesChange?.(newPromptVariables)
} }
const updatePromptNameIfNameEmpty = (index: number, newKey: string) => { const updatePromptNameIfNameEmpty = (index: number, newKey: string) => {
if (!newKey) return if (!newKey)
return
const newPromptVariables = promptVariables.map((item, i) => { const newPromptVariables = promptVariables.map((item, i) => {
if (i === index && !item.name) if (i === index && !item.name) {
return { return {
...item, ...item,
name: newKey, name: newKey,
} }
}
return item return item
}) })
onPromptVariablesChange(newPromptVariables) onPromptVariablesChange?.(newPromptVariables)
} }
const handleAddVar = () => { const handleAddVar = () => {
const newVar = getNewVar('') const newVar = getNewVar('')
onPromptVariablesChange([...promptVariables, newVar]) onPromptVariablesChange?.([...promptVariables, newVar])
} }
const handleRemoveVar = (index: number) => { const handleRemoveVar = (index: number) => {
onPromptVariablesChange(promptVariables.filter((_, i) => i !== index)) onPromptVariablesChange?.(promptVariables.filter((_, i) => i !== index))
} }
const [currKey, setCurrKey] = useState<string | null>(null) const [currKey, setCurrKey] = useState<string | null>(null)
@ -132,16 +134,18 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
title={ title={
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<div>{t('appDebug.variableTitle')}</div> <div>{t('appDebug.variableTitle')}</div>
<Tooltip htmlContent={<div className='w-[180px]'> {!readonly && (
{t('appDebug.variableTip')} <Tooltip htmlContent={<div className='w-[180px]'>
</div>} selector='config-var-tooltip'> {t('appDebug.variableTip')}
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg"> </div>} selector='config-var-tooltip'>
<path d="M8.66667 11.1667H8V8.5H7.33333M8 5.83333H8.00667M14 8.5C14 9.28793 13.8448 10.0681 13.5433 10.7961C13.2417 11.5241 12.7998 12.1855 12.2426 12.7426C11.6855 13.2998 11.0241 13.7417 10.2961 14.0433C9.56815 14.3448 8.78793 14.5 8 14.5C7.21207 14.5 6.43185 14.3448 5.7039 14.0433C4.97595 13.7417 4.31451 13.2998 3.75736 12.7426C3.20021 12.1855 2.75825 11.5241 2.45672 10.7961C2.15519 10.0681 2 9.28793 2 8.5C2 6.9087 2.63214 5.38258 3.75736 4.25736C4.88258 3.13214 6.4087 2.5 8 2.5C9.5913 2.5 11.1174 3.13214 12.2426 4.25736C13.3679 5.38258 14 6.9087 14 8.5Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> <svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
</svg> <path d="M8.66667 11.1667H8V8.5H7.33333M8 5.83333H8.00667M14 8.5C14 9.28793 13.8448 10.0681 13.5433 10.7961C13.2417 11.5241 12.7998 12.1855 12.2426 12.7426C11.6855 13.2998 11.0241 13.7417 10.2961 14.0433C9.56815 14.3448 8.78793 14.5 8 14.5C7.21207 14.5 6.43185 14.3448 5.7039 14.0433C4.97595 13.7417 4.31451 13.2998 3.75736 12.7426C3.20021 12.1855 2.75825 11.5241 2.45672 10.7961C2.15519 10.0681 2 9.28793 2 8.5C2 6.9087 2.63214 5.38258 3.75736 4.25736C4.88258 3.13214 6.4087 2.5 8 2.5C9.5913 2.5 11.1174 3.13214 12.2426 4.25736C13.3679 5.38258 14 6.9087 14 8.5Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</Tooltip> </svg>
</Tooltip>
)}
</div> </div>
} }
headerRight={<OperationBtn type="add" onClick={handleAddVar} />} headerRight={!readonly ? <OperationBtn type="add" onClick={handleAddVar} /> : null}
> >
{!hasVar && ( {!hasVar && (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div> <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
@ -153,8 +157,13 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
<tr className='uppercase'> <tr className='uppercase'>
<td>{t('appDebug.variableTable.key')}</td> <td>{t('appDebug.variableTable.key')}</td>
<td>{t('appDebug.variableTable.name')}</td> <td>{t('appDebug.variableTable.name')}</td>
<td>{t('appDebug.variableTable.optional')}</td> {!readonly && (
<td>{t('appDebug.variableTable.action')}</td> <>
<td>{t('appDebug.variableTable.optional')}</td>
<td>{t('appDebug.variableTable.action')}</td>
</>
)}
</tr> </tr>
</thead> </thead>
<tbody className="text-gray-700"> <tbody className="text-gray-700">
@ -163,42 +172,57 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
<td className="w-[160px] border-b border-gray-100 pl-3"> <td className="w-[160px] border-b border-gray-100 pl-3">
<div className='flex items-center space-x-1'> <div className='flex items-center space-x-1'>
<IconTypeIcon type={type} /> <IconTypeIcon type={type} />
<input {!readonly
type="text" ? (
placeholder="key" <input
value={key} type="text"
onChange={e => updatePromptKey(index, e.target.value)} placeholder="key"
onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)} value={key}
maxLength={getMaxVarNameLength(name)} onChange={e => updatePromptKey(index, e.target.value)}
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" 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"
/>
)
: (
<div className='h-6 leading-6 text-[13px] text-gray-700'>{key}</div>
)}
</div> </div>
</td> </td>
<td className="py-1 border-b border-gray-100"> <td className="py-1 border-b border-gray-100">
<input {!readonly
type="text" ? (
placeholder={key} <input
value={name} type="text"
onChange={e => updatePromptVariable(key, 'name', e.target.value)} placeholder={key}
maxLength={getMaxVarNameLength(name)} value={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" onChange={e => updatePromptVariable(key, 'name', e.target.value)}
/> maxLength={getMaxVarNameLength(name)}
</td> 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"
<td className='w-[84px] border-b border-gray-100'> />)
<div className='flex items-center h-full'> : (
<Switch defaultValue={!required} size='md' onChange={(value) => updatePromptVariable(key, 'required', !value)} /> <div className='h-6 leading-6 text-[13px] text-gray-700'>{name}</div>
</div> )}
</td>
<td className='w-20 border-b border-gray-100'>
<div className='flex h-full items-center space-x-1'>
<div className='flex items-center justify-items-center w-6 h-6 text-gray-500 cursor-pointer' onClick={() => handleConfig(key)}>
<Cog8ToothIcon width={16} height={16} />
</div>
<div className='flex items-center justify-items-center w-6 h-6 text-gray-500 cursor-pointer' onClick={() => handleRemoveVar(index)} >
<TrashIcon width={16} height={16} />
</div>
</div>
</td> </td>
{!readonly && (
<>
<td className='w-[84px] border-b border-gray-100'>
<div className='flex items-center h-full'>
<Switch defaultValue={!required} size='md' onChange={value => updatePromptVariable(key, 'required', !value)} />
</div>
</td>
<td className='w-20 border-b border-gray-100'>
<div className='flex h-full items-center space-x-1'>
<div className='flex items-center justify-items-center w-6 h-6 text-gray-500 cursor-pointer' onClick={() => handleConfig(key)}>
<Cog8ToothIcon width={16} height={16} />
</div>
<div className='flex items-center justify-items-center w-6 h-6 text-gray-500 cursor-pointer' onClick={() => handleRemoveVar(index)} >
<TrashIcon width={16} height={16} />
</div>
</div>
</td>
</>
)}
</tr> </tr>
))} ))}
</tbody> </tbody>
@ -212,11 +236,12 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, onPromptVariablesChan
isShow={isShowEditModal} isShow={isShowEditModal}
onClose={hideEditModal} onClose={hideEditModal}
onConfirm={({ type, value }) => { onConfirm={({ type, value }) => {
if (type === 'string') { if (type === 'string')
batchUpdatePromptVariable(currKey as string, ['type', 'max_length'], [type, value || DEFAULT_VALUE_MAX_LEN]) batchUpdatePromptVariable(currKey as string, ['type', 'max_length'], [type, value || DEFAULT_VALUE_MAX_LEN])
} else {
else
batchUpdatePromptVariable(currKey as string, ['type', 'options'], [type, value || []]) batchUpdatePromptVariable(currKey as string, ['type', 'options'], [type, value || []])
}
hideEditModal() hideEditModal()
}} }}
/> />

View File

@ -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 = (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.31346 0.905711C4.21464 0.708087 4.01266 0.583252 3.79171 0.583252C3.57076 0.583252 3.36877 0.708087 3.26996 0.905711L2.81236 1.82091C2.64757 2.15048 2.59736 2.24532 2.53635 2.32447C2.47515 2.40386 2.40398 2.47503 2.32459 2.53623C2.24544 2.59724 2.1506 2.64745 1.82103 2.81224L0.905833 3.26984C0.708209 3.36865 0.583374 3.57064 0.583374 3.79159C0.583374 4.01254 0.708209 4.21452 0.905833 4.31333L1.82103 4.77094C2.1506 4.93572 2.24544 4.98593 2.32459 5.04694C2.40398 5.10814 2.47515 5.17931 2.53635 5.2587C2.59736 5.33785 2.64758 5.43269 2.81236 5.76226L3.26996 6.67746C3.36877 6.87508 3.57076 6.99992 3.79171 6.99992C4.01266 6.99992 4.21465 6.87508 4.31346 6.67746L4.77106 5.76226C4.93584 5.43269 4.98605 5.33786 5.04707 5.2587C5.10826 5.17931 5.17943 5.10814 5.25883 5.04694C5.33798 4.98593 5.43282 4.93572 5.76238 4.77094L6.67758 4.31333C6.87521 4.21452 7.00004 4.01254 7.00004 3.79159C7.00004 3.57064 6.87521 3.36865 6.67758 3.26984L5.76238 2.81224C5.43282 2.64745 5.33798 2.59724 5.25883 2.53623C5.17943 2.47503 5.10826 2.40386 5.04707 2.32447C4.98605 2.24532 4.93584 2.15048 4.77106 1.82091L4.31346 0.905711Z" fill="#444CE7"/>
<path d="M11.375 1.74992C11.375 1.42775 11.1139 1.16659 10.7917 1.16659C10.4695 1.16659 10.2084 1.42775 10.2084 1.74992V2.62492H9.33337C9.01121 2.62492 8.75004 2.88609 8.75004 3.20825C8.75004 3.53042 9.01121 3.79159 9.33337 3.79159H10.2084V4.66659C10.2084 4.98875 10.4695 5.24992 10.7917 5.24992C11.1139 5.24992 11.375 4.98875 11.375 4.66659V3.79159H12.25C12.5722 3.79159 12.8334 3.53042 12.8334 3.20825C12.8334 2.88609 12.5722 2.62492 12.25 2.62492H11.375V1.74992Z" fill="#444CE7"/>
<path d="M3.79171 9.33325C3.79171 9.01109 3.53054 8.74992 3.20837 8.74992C2.88621 8.74992 2.62504 9.01109 2.62504 9.33325V10.2083H1.75004C1.42787 10.2083 1.16671 10.4694 1.16671 10.7916C1.16671 11.1138 1.42787 11.3749 1.75004 11.3749H2.62504V12.2499C2.62504 12.5721 2.88621 12.8333 3.20837 12.8333C3.53054 12.8333 3.79171 12.5721 3.79171 12.2499V11.3749H4.66671C4.98887 11.3749 5.25004 11.1138 5.25004 10.7916C5.25004 10.4694 4.98887 10.2083 4.66671 10.2083H3.79171V9.33325Z" fill="#444CE7"/>
<path d="M10.4385 6.73904C10.3396 6.54142 10.1377 6.41659 9.91671 6.41659C9.69576 6.41659 9.49377 6.54142 9.39496 6.73904L8.84014 7.84869C8.67535 8.17826 8.62514 8.27309 8.56413 8.35225C8.50293 8.43164 8.43176 8.50281 8.35237 8.56401C8.27322 8.62502 8.17838 8.67523 7.84881 8.84001L6.73917 9.39484C6.54154 9.49365 6.41671 9.69564 6.41671 9.91659C6.41671 10.1375 6.54154 10.3395 6.73917 10.4383L7.84881 10.9932C8.17838 11.1579 8.27322 11.2082 8.35237 11.2692C8.43176 11.3304 8.50293 11.4015 8.56413 11.4809C8.62514 11.5601 8.67535 11.6549 8.84014 11.9845L9.39496 13.0941C9.49377 13.2918 9.69576 13.4166 9.91671 13.4166C10.1377 13.4166 10.3396 13.2918 10.4385 13.0941L10.9933 11.9845C11.1581 11.6549 11.2083 11.5601 11.2693 11.4809C11.3305 11.4015 11.4017 11.3304 11.481 11.2692C11.5602 11.2082 11.655 11.1579 11.9846 10.9932L13.0942 10.4383C13.2919 10.3395 13.4167 10.1375 13.4167 9.91659C13.4167 9.69564 13.2919 9.49365 13.0942 9.39484L11.9846 8.84001C11.655 8.67523 11.5602 8.62502 11.481 8.56401C11.4017 8.50281 11.3305 8.43164 11.2693 8.35225C11.2083 8.27309 11.1581 8.17826 10.9933 7.84869L10.4385 6.73904Z" fill="#444CE7"/>
</svg>
)
const AutomaticBtn: FC<IAutomaticBtnProps> = ({
onClick,
}) => {
const { t } = useTranslation()
return (
<Button className='flex space-x-2 items-center !h-8'
onClick={onClick}
>
{leftIcon}
<span className='text-xs font-semibold text-primary-600 uppercase'>{t('appDebug.operation.automatic')}</span>
</Button>
)
}
export default React.memo(AutomaticBtn)

View File

@ -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 = (
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.4998 51.3333V39.6666M10.4998 16.3333V4.66663M4.6665 10.5H16.3332M4.6665 45.5H16.3332M30.3332 6.99996L26.2868 17.5206C25.6287 19.2315 25.2997 20.0869 24.7881 20.8065C24.3346 21.4442 23.7774 22.0014 23.1397 22.4549C22.4202 22.9665 21.5647 23.2955 19.8538 23.9535L9.33317 28L19.8539 32.0464C21.5647 32.7044 22.4202 33.0334 23.1397 33.5451C23.7774 33.9985 24.3346 34.5557 24.7881 35.1934C25.2997 35.913 25.6287 36.7684 26.2868 38.4793L30.3332 49L34.3796 38.4793C35.0376 36.7684 35.3666 35.913 35.8783 35.1934C36.3317 34.5557 36.8889 33.9985 37.5266 33.5451C38.2462 33.0334 39.1016 32.7044 40.8125 32.0464L51.3332 28L40.8125 23.9535C39.1016 23.2955 38.2462 22.9665 37.5266 22.4549C36.8889 22.0014 36.3317 21.4442 35.8783 20.8065C35.3666 20.0869 35.0376 19.2315 34.3796 17.5206L30.3332 6.99996Z" stroke="#EAECF0" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
)
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 = (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.6665 1.33332C3.6665 0.965133 3.36803 0.666656 2.99984 0.666656C2.63165 0.666656 2.33317 0.965133 2.33317 1.33332V2.33332H1.33317C0.964981 2.33332 0.666504 2.6318 0.666504 2.99999C0.666504 3.36818 0.964981 3.66666 1.33317 3.66666H2.33317V4.66666C2.33317 5.03485 2.63165 5.33332 2.99984 5.33332C3.36803 5.33332 3.6665 5.03485 3.6665 4.66666V3.66666H4.6665C5.03469 3.66666 5.33317 3.36818 5.33317 2.99999C5.33317 2.6318 5.03469 2.33332 4.6665 2.33332H3.6665V1.33332Z" fill="white"/>
<path d="M3.6665 11.3333C3.6665 10.9651 3.36803 10.6667 2.99984 10.6667C2.63165 10.6667 2.33317 10.9651 2.33317 11.3333V12.3333H1.33317C0.964981 12.3333 0.666504 12.6318 0.666504 13C0.666504 13.3682 0.964981 13.6667 1.33317 13.6667H2.33317V14.6667C2.33317 15.0348 2.63165 15.3333 2.99984 15.3333C3.36803 15.3333 3.6665 15.0348 3.6665 14.6667V13.6667H4.6665C5.03469 13.6667 5.33317 13.3682 5.33317 13C5.33317 12.6318 5.03469 12.3333 4.6665 12.3333H3.6665V11.3333Z" fill="white"/>
<path d="M9.28873 1.76067C9.18971 1.50321 8.94235 1.33332 8.6665 1.33332C8.39066 1.33332 8.1433 1.50321 8.04427 1.76067L6.88815 4.76658C6.68789 5.28727 6.62495 5.43732 6.53887 5.55838C6.4525 5.67986 6.34637 5.78599 6.2249 5.87236C6.10384 5.95844 5.95379 6.02137 5.43309 6.22164L2.42718 7.37776C2.16972 7.47678 1.99984 7.72414 1.99984 7.99999C1.99984 8.27584 2.16972 8.5232 2.42718 8.62222L5.43309 9.77834C5.95379 9.97861 6.10384 10.0415 6.2249 10.1276C6.34637 10.214 6.4525 10.3201 6.53887 10.4416C6.62495 10.5627 6.68789 10.7127 6.88816 11.2334L8.04427 14.2393C8.1433 14.4968 8.39066 14.6667 8.6665 14.6667C8.94235 14.6667 9.18971 14.4968 9.28873 14.2393L10.4449 11.2334C10.6451 10.7127 10.7081 10.5627 10.7941 10.4416C10.8805 10.3201 10.9866 10.214 11.1081 10.1276C11.2292 10.0415 11.3792 9.97861 11.8999 9.77834L14.9058 8.62222C15.1633 8.5232 15.3332 8.27584 15.3332 7.99999C15.3332 7.72414 15.1633 7.47678 14.9058 7.37776L11.8999 6.22164C11.3792 6.02137 11.2292 5.95844 11.1081 5.87236C10.9866 5.78599 10.8805 5.67986 10.7941 5.55838C10.7081 5.43732 10.6451 5.28727 10.4449 4.76658L9.28873 1.76067Z" fill="white"/>
</svg>
)
const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
mode,
isShow,
onClose,
// appId,
onFinished,
}) => {
const { t } = useTranslation()
const [audiences, setAudiences] = React.useState<string>('')
const [hopingToSolve, setHopingToSolve] = React.useState<string>('')
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<AutomaticRes | null>(null)
const renderLoading = (
<div className='grow flex flex-col items-center justify-center h-full space-y-3'>
<Loading />
<div className='text-[13px] text-gray-400'>{t('appDebug.automatic.loading')}</div>
</div>
)
const renderNoData = (
<div className='grow flex flex-col items-center justify-center h-full space-y-3'>
{noDataIcon}
<div className='text-[13px] text-gray-400'>{t('appDebug.automatic.noData')}</div>
</div>
)
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 (
<Modal
isShow={isShow}
onClose={onClose}
className='min-w-[1120px] !p-0'
closable
>
<div className='flex h-[680px]'>
<div className='w-[480px] shrink-0 px-8 py-6 h-full overflow-y-auto border-r border-gray-100'>
<div>
<div className='mb-1 text-xl font-semibold text-primary-600'>{t('appDebug.automatic.title')}</div>
<div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div>
</div>
{/* inputs */}
<div className='mt-12 space-y-5'>
<div className='space-y-2'>
<div className='text-[13px] font-medium text-gray-900'>{t('appDebug.automatic.intendedAudience')}</div>
<input className="w-full h-8 px-3 text-[13px] font-normal bg-gray-50 rounded-lg" placeholder={t('appDebug.automatic.intendedAudiencePlaceHolder') as string} value={audiences} onChange={e => setAudiences(e.target.value)} />
</div>
<div className='space-y-2'>
<div className='text-[13px] font-medium text-gray-900'>{t('appDebug.automatic.solveProblem')}</div>
<textarea className="w-full h-[200px] overflow-y-auto p-3 text-[13px] font-normal bg-gray-50 rounded-lg" placeholder={t('appDebug.automatic.solveProblemPlaceHolder') as string} value={hopingToSolve} onChange={e => setHopingToSolve(e.target.value)} />
</div>
<div className='mt-6 flex justify-end'>
<Button
className='flex space-x-2 items-center !h-8'
type='primary'
onClick={onGenerate}
disabled={isLoading}
>
{genIcon}
<span className='text-xs font-semibold text-white uppercase'>{t('appDebug.automatic.generate')}</span>
</Button>
</div>
</div>
</div>
{(!isLoading && res) && (
<div className='grow px-8 pt-6 h-full overflow-y-auto'>
<div className='mb-4 w-1/2 text-lg font-medium text-gray-900'>{t('appDebug.automatic.resTitle')}</div>
<ConfigPrompt
mode={mode}
promptTemplate={res?.prompt || ''}
promptVariables={[]}
readonly
/>
{(res?.variables?.length && res?.variables?.length > 0)
? (
<ConfigVar
promptVariables={res?.variables.map(key => ({ key, name: key, type: 'string', required: true })) || []}
readonly
/>
)
: ''}
{(mode === AppType.chat && res?.opening_statement) && (
<div className='mt-7'>
<GroupName name={t('appDebug.feature.groupChat.title')} />
<OpeningStatement
value={res?.opening_statement || ''}
readonly
/>
</div>
)}
<div className='sticky bottom-0 flex justify-end right-0 py-4'>
<Button onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button type='primary' className='ml-2' onClick={() => {
setShowConfirmOverwrite(true)
}}>{t('appDebug.automatic.apply')}</Button>
</div>
</div>
)}
{isLoading && renderLoading}
{(!isLoading && !res) && renderNoData}
{showConfirmOverwrite && (
<Confirm
title={t('appDebug.automatic.overwriteTitle')}
content={t('appDebug.automatic.overwriteMessage')}
isShow={showConfirmOverwrite}
onClose={() => setShowConfirmOverwrite(false)}
onConfirm={() => {
setShowConfirmOverwrite(false)
onFinished(res as AutomaticRes)
}}
onCancel={() => setShowConfirmOverwrite(false)}
/>
)}
</div>
</Modal>
)
}
export default React.memo(GetAutomaticRes)

View File

@ -3,19 +3,22 @@ import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import produce from 'immer' import produce from 'immer'
import AddFeatureBtn from './feature/add-feature-btn' import { useBoolean } from 'ahooks'
import ChooseFeature from './feature/choose-feature'
import useFeature from './feature/use-feature'
import ConfigContext from '@/context/debug-configuration'
import DatasetConfig from '../dataset-config' import DatasetConfig from '../dataset-config'
import ChatGroup from '../features/chat-group' import ChatGroup from '../features/chat-group'
import ExperienceEnchanceGroup from '../features/experience-enchance-group' import ExperienceEnchanceGroup from '../features/experience-enchance-group'
import Toolbox from '../toolbox' import Toolbox from '../toolbox'
import AddFeatureBtn from './feature/add-feature-btn'
import AutomaticBtn from './automatic/automatic-btn'
import type { AutomaticRes } from './automatic/get-automatic-res'
import GetAutomaticResModal from './automatic/get-automatic-res'
import ChooseFeature from './feature/choose-feature'
import useFeature from './feature/use-feature'
import ConfigContext from '@/context/debug-configuration'
import ConfigPrompt from '@/app/components/app/configuration/config-prompt' import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
import ConfigVar from '@/app/components/app/configuration/config-var' import ConfigVar from '@/app/components/app/configuration/config-var'
import type { PromptVariable } from '@/models/debug' import type { PromptVariable } from '@/models/debug'
import { AppType } from '@/types/app' import { AppType } from '@/types/app'
import { useBoolean } from 'ahooks'
const Config: FC = () => { const Config: FC = () => {
const { const {
@ -29,7 +32,7 @@ const Config: FC = () => {
moreLikeThisConifg, moreLikeThisConifg,
setMoreLikeThisConifg, setMoreLikeThisConifg,
suggestedQuestionsAfterAnswerConfig, suggestedQuestionsAfterAnswerConfig,
setSuggestedQuestionsAfterAnswerConfig setSuggestedQuestionsAfterAnswerConfig,
} = useContext(ConfigContext) } = useContext(ConfigContext)
const isChatApp = mode === AppType.chat const isChatApp = mode === AppType.chat
@ -41,9 +44,8 @@ const Config: FC = () => {
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...newVariables] draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...newVariables]
}) })
if (modelConfig.configs.prompt_template !== newTemplate) { if (modelConfig.configs.prompt_template !== newTemplate)
setFormattingChanged(true) setFormattingChanged(true)
}
setPrevPromptConfig(modelConfig.configs) setPrevPromptConfig(modelConfig.configs)
setModelConfig(newModelConfig) setModelConfig(newModelConfig)
@ -59,7 +61,7 @@ const Config: FC = () => {
const [showChooseFeature, { const [showChooseFeature, {
setTrue: showChooseFeatureTrue, setTrue: showChooseFeatureTrue,
setFalse: showChooseFeatureFalse setFalse: showChooseFeatureFalse,
}] = useBoolean(false) }] = useBoolean(false)
const { featureConfig, handleFeatureChange } = useFeature({ const { featureConfig, handleFeatureChange } = useFeature({
introduction, introduction,
@ -81,14 +83,24 @@ const Config: FC = () => {
const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer) const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer)
const hasToolbox = false const hasToolbox = false
const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false)
const handleAutomaticRes = (res: AutomaticRes) => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.configs.prompt_template = res.prompt
draft.configs.prompt_variables = res.variables.map(key => ({ key, name: key, type: 'string', required: true }))
})
setModelConfig(newModelConfig)
setPrevPromptConfig(modelConfig.configs)
if (mode === AppType.chat)
setIntroduction(res.opening_statement)
showAutomaticFalse()
}
return ( return (
<> <>
<div className="pb-[20px]"> <div className="pb-[20px]">
<div className='flex justify-between items-center mb-4'> <div className='flex justify-between items-center mb-4'>
<AddFeatureBtn onClick={showChooseFeatureTrue} /> <AddFeatureBtn onClick={showChooseFeatureTrue} />
<div> <AutomaticBtn onClick={showAutomaticTrue}/>
{/* AutoMatic */}
</div>
</div> </div>
{showChooseFeature && ( {showChooseFeature && (
@ -100,6 +112,14 @@ const Config: FC = () => {
onChange={handleFeatureChange} onChange={handleFeatureChange}
/> />
)} )}
{showAutomatic && (
<GetAutomaticResModal
mode={mode as AppType}
isShow={showAutomatic}
onClose={showAutomaticFalse}
onFinished={handleAutomaticRes}
/>
)}
{/* Template */} {/* Template */}
<ConfigPrompt <ConfigPrompt
mode={mode as AppType} mode={mode as AppType}
@ -124,9 +144,8 @@ const Config: FC = () => {
isShowOpeningStatement={featureConfig.openingStatement} isShowOpeningStatement={featureConfig.openingStatement}
openingStatementConfig={ openingStatementConfig={
{ {
promptTemplate,
value: introduction, value: introduction,
onChange: setIntroduction onChange: setIntroduction,
} }
} }
isShowSuggestedQuestionsAfterAnswer={featureConfig.suggestedQuestionsAfterAnswer} isShowSuggestedQuestionsAfterAnswer={featureConfig.suggestedQuestionsAfterAnswer}
@ -139,7 +158,6 @@ const Config: FC = () => {
<ExperienceEnchanceGroup /> <ExperienceEnchanceGroup />
)} )}
{/* Toolbox */} {/* Toolbox */}
{ {
hasToolbox && ( hasToolbox && (

View File

@ -17,9 +17,9 @@ import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight' import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
export type IOpeningStatementProps = { export type IOpeningStatementProps = {
promptTemplate: string
value: string value: string
onChange: (value: string) => void readonly?: boolean
onChange?: (value: string) => void
} }
// regex to match the {{}} and replace it with a span // regex to match the {{}} and replace it with a span
@ -27,6 +27,7 @@ const regex = /\{\{([^}]+)\}\}/g
const OpeningStatement: FC<IOpeningStatementProps> = ({ const OpeningStatement: FC<IOpeningStatementProps> = ({
value = '', value = '',
readonly,
onChange, onChange,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -64,6 +65,8 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
.replace(/\n/g, '<br />') .replace(/\n/g, '<br />')
const handleEdit = () => { const handleEdit = () => {
if (readonly)
return
setFocus() setFocus()
} }
@ -93,11 +96,11 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
return return
} }
setBlur() setBlur()
onChange(tempValue) onChange?.(tempValue)
} }
const cancelAutoAddVar = () => { const cancelAutoAddVar = () => {
onChange(tempValue) onChange?.(tempValue)
hideConfirmAddVar() hideConfirmAddVar()
setBlur() setBlur()
} }
@ -106,15 +109,15 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
const newModelConfig = produce(modelConfig, (draft) => { const newModelConfig = produce(modelConfig, (draft) => {
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map(key => getNewVar(key))] draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map(key => getNewVar(key))]
}) })
onChange(tempValue) onChange?.(tempValue)
setModelConfig(newModelConfig) setModelConfig(newModelConfig)
hideConfirmAddVar() hideConfirmAddVar()
setBlur() setBlur()
} }
const headerRight = ( const headerRight = !readonly ? (
<OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpner') as string} onClick={handleEdit} /> <OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpner') as string} onClick={handleEdit} />
) ) : null
return ( return (
<Panel <Panel
@ -130,30 +133,28 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
isFocus={isFocus} isFocus={isFocus}
> >
<div className='text-gray-700 text-sm'> <div className='text-gray-700 text-sm'>
{(hasValue || (!hasValue && isFocus)) {(hasValue || (!hasValue && isFocus)) ? (
? ( <>
<> {isFocus
{isFocus ? (
? ( <textarea
<textarea ref={inputRef}
ref={inputRef} value={tempValue}
value={tempValue} rows={3}
rows={3} onChange={e => setTempValue(e.target.value)}
onChange={e => setTempValue(e.target.value)} className="w-full px-0 text-sm border-0 bg-transparent focus:outline-none "
className="w-full px-0 text-sm border-0 bg-transparent focus:outline-none " placeholder={t('appDebug.openingStatement.placeholder') as string}
placeholder={t('appDebug.openingStatement.placeholder') as string} >
> </textarea>
</textarea> )
) : (
: ( <div dangerouslySetInnerHTML={{
<div dangerouslySetInnerHTML={{ __html: coloredContent,
__html: coloredContent, }}></div>
}}></div> )}
)}
{/* Operation Bar */} {/* Operation Bar */}
{isFocus {isFocus && (
&& (
<div className='mt-2 flex items-center justify-between'> <div className='mt-2 flex items-center justify-between'>
<div className='text-xs text-gray-500'>{t('appDebug.openingStatement.varTip')}</div> <div className='text-xs text-gray-500'>{t('appDebug.openingStatement.varTip')}</div>
@ -164,9 +165,9 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
</div> </div>
)} )}
</>) : ( </>) : (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div> <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div>
)} )}
{isShowConfirmAddVar && ( {isShowConfirmAddVar && (
<ConfirmAddVar <ConfirmAddVar

View File

@ -33,12 +33,14 @@ export type IBlockInputProps = {
value: string value: string
className?: string // wrapper class className?: string // wrapper class
highLightClassName?: string // class for the highlighted text default is text-blue-500 highLightClassName?: string // class for the highlighted text default is text-blue-500
readonly?: boolean
onConfirm?: (value: string, keys: string[]) => void onConfirm?: (value: string, keys: string[]) => void
} }
const BlockInput: FC<IBlockInputProps> = ({ const BlockInput: FC<IBlockInputProps> = ({
value = '', value = '',
className, className,
readonly = false,
onConfirm, onConfirm,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -113,7 +115,7 @@ const BlockInput: FC<IBlockInputProps> = ({
const editAreaClassName = 'focus:outline-none bg-transparent text-sm' const editAreaClassName = 'focus:outline-none bg-transparent text-sm'
const textAreaContent = ( const textAreaContent = (
<div className='h-[180px] overflow-y-auto' onClick={() => setIsEditing(true)}> <div className={classNames(readonly ? 'max-h-[180px] pb-5' : 'h-[180px]', ' overflow-y-auto')} onClick={() => !readonly && setIsEditing(true)}>
{isEditing {isEditing
? <div className='h-full px-4 py-1'> ? <div className='h-full px-4 py-1'>
<textarea <textarea
@ -141,35 +143,37 @@ const BlockInput: FC<IBlockInputProps> = ({
<div className={classNames('block-input w-full overflow-y-auto border-none rounded-lg')}> <div className={classNames('block-input w-full overflow-y-auto border-none rounded-lg')}>
{textAreaContent} {textAreaContent}
{/* footer */} {/* footer */}
<div className='flex item-center h-14 px-4'> {!readonly && (
{isContentChanged <div className='flex item-center h-14 px-4'>
? ( {isContentChanged
<div className='flex items-center justify-between w-full'> ? (
<div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{currentValue.length}</div> <div className='flex items-center justify-between w-full'>
<div className='flex space-x-2'> <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{currentValue?.length}</div>
<Button <div className='flex space-x-2'>
onClick={handleCancel} <Button
className='w-20 !h-8 !text-[13px]' onClick={handleCancel}
> className='w-20 !h-8 !text-[13px]'
{t('common.operation.cancel')} >
</Button> {t('common.operation.cancel')}
<Button </Button>
onClick={handleSubmit} <Button
type="primary" onClick={handleSubmit}
className='w-20 !h-8 !text-[13px]' type="primary"
> className='w-20 !h-8 !text-[13px]'
{t('common.operation.confirm')} >
</Button> {t('common.operation.confirm')}
</div> </Button>
</div>
</div> </div>
) )
: ( : (
<p className="leading-5 text-xs text-gray-500"> <p className="leading-5 text-xs text-gray-500">
{t('appDebug.promptTip')} {t('appDebug.promptTip')}
</p> </p>
)} )}
</div> </div>
)}
</div> </div>
) )

View File

@ -1,36 +1,36 @@
/* eslint-disable no-mixed-operators */
'use client' 'use client'
import React, { useState, useRef, useEffect, useLayoutEffect } from 'react' import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks' import { useBoolean } from 'ahooks'
import type { File, PreProcessingRule, Rules, FileIndexingEstimateResponse as IndexingEstimateResponse } from '@/models/datasets'
import {
fetchDefaultProcessRule,
createFirstDocument,
createDocument,
fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
} from '@/service/datasets'
import type { CreateDocumentReq, createDocumentResponse, FullDocumentDetail } from '@/models/datasets'
import Button from '@/app/components/base/button'
import PreviewItem from './preview-item'
import Loading from '@/app/components/base/loading'
import { XMarkIcon } from '@heroicons/react/20/solid' import { XMarkIcon } from '@heroicons/react/20/solid'
import cn from 'classnames' import cn from 'classnames'
import s from './index.module.css'
import Link from 'next/link' import Link from 'next/link'
import PreviewItem from './preview-item'
import s from './index.module.css'
import type { CreateDocumentReq, File, FullDocumentDetail, FileIndexingEstimateResponse as IndexingEstimateResponse, PreProcessingRule, Rules, createDocumentResponse } from '@/models/datasets'
import {
createDocument,
createFirstDocument,
fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
fetchDefaultProcessRule,
} from '@/service/datasets'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import { formatNumber } from '@/utils/format' import { formatNumber } from '@/utils/format'
type StepTwoProps = { type StepTwoProps = {
isSetting?: boolean, isSetting?: boolean
documentDetail?: FullDocumentDetail documentDetail?: FullDocumentDetail
hasSetAPIKEY: boolean, hasSetAPIKEY: boolean
onSetting: () => void, onSetting: () => void
datasetId?: string, datasetId?: string
indexingType?: string, indexingType?: string
file?: File, file?: File
onStepChange?: (delta: number) => void, onStepChange?: (delta: number) => void
updateIndexingTypeCache?: (type: string) => void, updateIndexingTypeCache?: (type: string) => void
updateResultCache?: (res: createDocumentResponse) => void updateResultCache?: (res: createDocumentResponse) => void
onSave?: () => void onSave?: () => void
onCancel?: () => void onCancel?: () => void
@ -71,8 +71,10 @@ const StepTwo = ({
const [defaultConfig, setDefaultConfig] = useState<Rules>() const [defaultConfig, setDefaultConfig] = useState<Rules>()
const hasSetIndexType = !!indexingType const hasSetIndexType = !!indexingType
const [indexType, setIndexType] = useState<IndexingType>( const [indexType, setIndexType] = useState<IndexingType>(
indexingType || indexingType
hasSetAPIKEY ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL || hasSetAPIKEY
? IndexingType.QUALIFIED
: IndexingType.ECONOMICAL,
) )
const [showPreview, { setTrue: setShowPreview, setFalse: hidePreview }] = useBoolean() const [showPreview, { setTrue: setShowPreview, setFalse: hidePreview }] = useBoolean()
const [customFileIndexingEstimate, setCustomFileIndexingEstimate] = useState<IndexingEstimateResponse | null>(null) const [customFileIndexingEstimate, setCustomFileIndexingEstimate] = useState<IndexingEstimateResponse | null>(null)
@ -82,19 +84,19 @@ const StepTwo = ({
})() })()
const scrollHandle = (e: any) => { const scrollHandle = (e: any) => {
if (e.target.scrollTop > 0) { if (e.target.scrollTop > 0)
setScrolled(true) setScrolled(true)
} else {
else
setScrolled(false) setScrolled(false)
}
} }
const previewScrollHandle = (e: any) => { const previewScrollHandle = (e: any) => {
if (e.target.scrollTop > 0) { if (e.target.scrollTop > 0)
setPreviewScrolled(true) setPreviewScrolled(true)
} else {
else
setPreviewScrolled(false) setPreviewScrolled(false)
}
} }
const getFileName = (name: string) => { const getFileName = (name: string) => {
const arr = name.split('.') const arr = name.split('.')
@ -102,18 +104,17 @@ const StepTwo = ({
} }
const getRuleName = (key: string) => { const getRuleName = (key: string) => {
if (key === 'remove_extra_spaces') { if (key === 'remove_extra_spaces')
return t('datasetCreation.stepTwo.removeExtraSpaces') return t('datasetCreation.stepTwo.removeExtraSpaces')
}
if (key === 'remove_urls_emails') { if (key === 'remove_urls_emails')
return t('datasetCreation.stepTwo.removeUrlEmails') return t('datasetCreation.stepTwo.removeUrlEmails')
}
if (key === 'remove_stopwords') { if (key === 'remove_stopwords')
return t('datasetCreation.stepTwo.removeStopwords') return t('datasetCreation.stepTwo.removeStopwords')
}
} }
const ruleChangeHandle = (id: string) => { const ruleChangeHandle = (id: string) => {
const newRules = rules.map(rule => { const newRules = rules.map((rule) => {
if (rule.id === id) { if (rule.id === id) {
return { return {
id: rule.id, id: rule.id,
@ -132,13 +133,23 @@ const StepTwo = ({
} }
} }
const fetchFileIndexingEstimate = async () => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const res = await didFetchFileIndexingEstimate(getFileIndexingEstimateParams())
if (segmentationType === SegmentType.CUSTOM)
setCustomFileIndexingEstimate(res)
else
setAutomaticFileIndexingEstimate(res)
}
const confirmChangeCustomConfig = async () => { const confirmChangeCustomConfig = async () => {
setCustomFileIndexingEstimate(null) setCustomFileIndexingEstimate(null)
setShowPreview() setShowPreview()
await fetchFileIndexingEstimate() await fetchFileIndexingEstimate()
} }
const getIndexing_technique = () => indexingType ? indexingType : indexType const getIndexing_technique = () => indexingType || indexType
const getProcessRule = () => { const getProcessRule = () => {
const processRule: any = { const processRule: any = {
@ -168,16 +179,6 @@ const StepTwo = ({
return params return params
} }
const fetchFileIndexingEstimate = async () => {
const res = await didFetchFileIndexingEstimate(getFileIndexingEstimateParams())
if (segmentationType === SegmentType.CUSTOM) {
setCustomFileIndexingEstimate(res)
}
else {
setAutomaticFileIndexingEstimate(res)
}
}
const getCreationParams = () => { const getCreationParams = () => {
let params let params
if (isSetting) { if (isSetting) {
@ -185,7 +186,8 @@ const StepTwo = ({
original_document_id: documentDetail?.id, original_document_id: documentDetail?.id,
process_rule: getProcessRule(), process_rule: getProcessRule(),
} as CreateDocumentReq } as CreateDocumentReq
} else { }
else {
params = { params = {
data_source: { data_source: {
type: 'upload_file', type: 'upload_file',
@ -226,25 +228,25 @@ const StepTwo = ({
} }
const getDefaultMode = () => { const getDefaultMode = () => {
if (documentDetail) { if (documentDetail)
setSegmentationType(documentDetail.dataset_process_rule.mode) setSegmentationType(documentDetail.dataset_process_rule.mode)
}
} }
const createHandle = async () => { const createHandle = async () => {
try { try {
let res; let res
const params = getCreationParams() const params = getCreationParams()
if (!datasetId) { if (!datasetId) {
res = await createFirstDocument({ res = await createFirstDocument({
body: params body: params,
}) })
updateIndexingTypeCache && updateIndexingTypeCache(indexType) updateIndexingTypeCache && updateIndexingTypeCache(indexType)
updateResultCache && updateResultCache(res) updateResultCache && updateResultCache(res)
} else { }
else {
res = await createDocument({ res = await createDocument({
datasetId, datasetId,
body: params body: params,
}) })
updateIndexingTypeCache && updateIndexingTypeCache(indexType) updateIndexingTypeCache && updateIndexingTypeCache(indexType)
updateResultCache && updateResultCache({ updateResultCache && updateResultCache({
@ -257,7 +259,7 @@ const StepTwo = ({
catch (err) { catch (err) {
Toast.notify({ Toast.notify({
type: 'error', type: 'error',
message: err + '', message: `${err}`,
}) })
} }
} }
@ -266,35 +268,36 @@ const StepTwo = ({
// fetch rules // fetch rules
if (!isSetting) { if (!isSetting) {
getRules() getRules()
} else { }
else {
getRulesFromDetail() getRulesFromDetail()
getDefaultMode() getDefaultMode()
} }
}, []) }, [])
useEffect(() => { useEffect(() => {
scrollRef.current?.addEventListener('scroll', scrollHandle); scrollRef.current?.addEventListener('scroll', scrollHandle)
return () => { return () => {
scrollRef.current?.removeEventListener('scroll', scrollHandle); scrollRef.current?.removeEventListener('scroll', scrollHandle)
} }
}, []) }, [])
useLayoutEffect(() => { useLayoutEffect(() => {
if (showPreview) { if (showPreview) {
previewScrollRef.current?.addEventListener('scroll', previewScrollHandle); previewScrollRef.current?.addEventListener('scroll', previewScrollHandle)
return () => { return () => {
previewScrollRef.current?.removeEventListener('scroll', previewScrollHandle); previewScrollRef.current?.removeEventListener('scroll', previewScrollHandle)
} }
} }
}, [showPreview]) }, [showPreview])
useEffect(() => { useEffect(() => {
// get indexing type by props // get indexing type by props
if (indexingType) { if (indexingType)
setIndexType(indexingType as IndexingType) setIndexType(indexingType as IndexingType)
} else {
else
setIndexType(hasSetAPIKEY ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL) setIndexType(hasSetAPIKEY ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL)
}
}, [hasSetAPIKEY, indexingType, datasetId]) }, [hasSetAPIKEY, indexingType, datasetId])
useEffect(() => { useEffect(() => {
@ -302,7 +305,8 @@ const StepTwo = ({
setAutomaticFileIndexingEstimate(null) setAutomaticFileIndexingEstimate(null)
setShowPreview() setShowPreview()
fetchFileIndexingEstimate() fetchFileIndexingEstimate()
} else { }
else {
hidePreview() hidePreview()
setCustomFileIndexingEstimate(null) setCustomFileIndexingEstimate(null)
} }
@ -320,7 +324,7 @@ const StepTwo = ({
className={cn( className={cn(
s.radioItem, s.radioItem,
s.segmentationItem, s.segmentationItem,
segmentationType === SegmentType.AUTO && s.active segmentationType === SegmentType.AUTO && s.active,
)} )}
onClick={() => setSegmentationType(SegmentType.AUTO)} onClick={() => setSegmentationType(SegmentType.AUTO)}
> >
@ -355,7 +359,7 @@ const StepTwo = ({
type="text" type="text"
className={s.input} className={s.input}
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier} placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier}
onChange={(e) => setSegmentIdentifier(e.target.value)} onChange={e => setSegmentIdentifier(e.target.value)}
/> />
</div> </div>
</div> </div>
@ -366,7 +370,7 @@ const StepTwo = ({
type="number" type="number"
className={s.input} className={s.input}
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={max} placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={max}
onChange={(e) => setMax(Number(e.target.value))} onChange={e => setMax(Number(e.target.value))}
/> />
</div> </div>
</div> </div>
@ -403,9 +407,8 @@ const StepTwo = ({
hasSetIndexType && '!w-full', hasSetIndexType && '!w-full',
)} )}
onClick={() => { onClick={() => {
if (hasSetAPIKEY) { if (hasSetAPIKEY)
setIndexType(IndexingType.QUALIFIED) setIndexType(IndexingType.QUALIFIED)
}
}} }}
> >
<span className={cn(s.typeIcon, s.qualified)} /> <span className={cn(s.typeIcon, s.qualified)} />
@ -418,11 +421,13 @@ const StepTwo = ({
<div className={s.tip}>{t('datasetCreation.stepTwo.qualifiedTip')}</div> <div className={s.tip}>{t('datasetCreation.stepTwo.qualifiedTip')}</div>
<div className='pb-0.5 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateCost')}</div> <div className='pb-0.5 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateCost')}</div>
{ {
!!fileIndexingEstimate ? ( fileIndexingEstimate
<div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.tokens)} tokens(<span className='text-yellow-500'>${formatNumber(fileIndexingEstimate.total_price)}</span>)</div> ? (
) : ( <div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.tokens)} tokens(<span className='text-yellow-500'>${formatNumber(fileIndexingEstimate.total_price)}</span>)</div>
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div> )
) : (
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
)
} }
</div> </div>
{!hasSetAPIKEY && ( {!hasSetAPIKEY && (
@ -434,7 +439,6 @@ const StepTwo = ({
</div> </div>
)} )}
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.ECONOMICAL)) && ( {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.ECONOMICAL)) && (
<div <div
className={cn( className={cn(
@ -476,51 +480,58 @@ const StepTwo = ({
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateSegment')}</div> <div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateSegment')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'> <div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
{ {
!!fileIndexingEstimate ? ( fileIndexingEstimate
<div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.total_segments)} </div> ? (
) : ( <div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.total_segments)} </div>
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div> )
) : (
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
)
} }
</div> </div>
</div> </div>
</div> </div>
{!isSetting ? ( {!isSetting
<div className='flex items-center mt-8 py-2'> ? (
<Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button> <div className='flex items-center mt-8 py-2'>
<div className={s.divider} /> <Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button>
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button> <div className={s.divider} />
</div> <Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
) : ( </div>
<div className='flex items-center mt-8 py-2'> )
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.save')}</Button> : (
<Button className='ml-2' onClick={onCancel}>{t('datasetCreation.stepTwo.cancel')}</Button> <div className='flex items-center mt-8 py-2'>
</div> <Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.save')}</Button>
)} <Button className='ml-2' onClick={onCancel}>{t('datasetCreation.stepTwo.cancel')}</Button>
</div>
)}
</div> </div>
</div> </div>
</div> </div>
{(showPreview) ? ( {(showPreview)
<div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}> ? (
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`, ' flex items-center justify-between px-8')}> <div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}>
<span>{t('datasetCreation.stepTwo.previewTitle')}</span> <div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`, ' flex items-center justify-between px-8')}>
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}> <span>{t('datasetCreation.stepTwo.previewTitle')}</span>
<XMarkIcon className='h-4 w-4'></XMarkIcon> <div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
</div>
<div className='my-4 px-8 space-y-4'>
{fileIndexingEstimate?.preview
? (
<>
{fileIndexingEstimate?.preview.map((item, index) => (
<PreviewItem key={item} content={item} index={index + 1} />
))}
</>
)
: <div className='flex items-center justify-center h-[200px]'><Loading type='area'></Loading></div>
}
</div> </div>
</div> </div>
<div className='my-4 px-8 space-y-4'> )
{fileIndexingEstimate?.preview ? ( : (<div className={cn(s.sideTip)}>
<>
{fileIndexingEstimate?.preview.map((item, index) => (
<PreviewItem key={item} content={item} index={index + 1} />
))}
</>
) : <div className='flex items-center justify-center h-[200px]'><Loading type='area'></Loading></div>
}
</div>
</div>
) :
(<div className={cn(s.sideTip)}>
<div className={s.tipCard}> <div className={s.tipCard}>
<span className={s.icon} /> <span className={s.icon} />
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div> <div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>

View File

@ -1,5 +1,5 @@
'use client' 'use client'
import React, { useState, useCallback, useEffect } from 'react' import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks' import { useBoolean } from 'ahooks'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
@ -7,7 +7,8 @@ import { useRouter } from 'next/navigation'
import DatasetDetailContext from '@/context/dataset-detail' import DatasetDetailContext from '@/context/dataset-detail'
import type { FullDocumentDetail } from '@/models/datasets' import type { FullDocumentDetail } from '@/models/datasets'
import { fetchTenantInfo } from '@/service/common' import { fetchTenantInfo } from '@/service/common'
import { fetchDocumentDetail, MetadataType } from '@/service/datasets' import type { MetadataType } from '@/service/datasets'
import { fetchDocumentDetail } from '@/service/datasets'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import StepTwo from '@/app/components/datasets/create/step-two' import StepTwo from '@/app/components/datasets/create/step-two'
@ -15,8 +16,8 @@ import AccountSetting from '@/app/components/header/account-setting'
import AppUnavailable from '@/app/components/base/app-unavailable' import AppUnavailable from '@/app/components/base/app-unavailable'
type DocumentSettingsProps = { type DocumentSettingsProps = {
datasetId: string; datasetId: string
documentId: string; documentId: string
} }
const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => { const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
@ -48,18 +49,18 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
const detail = await fetchDocumentDetail({ const detail = await fetchDocumentDetail({
datasetId, datasetId,
documentId, documentId,
params: { metadata: 'without' as MetadataType } params: { metadata: 'without' as MetadataType },
}) })
setDocumentDetail(detail) setDocumentDetail(detail)
} catch (e) { }
catch (e) {
setHasError(true) setHasError(true)
} }
})() })()
}, [datasetId, documentId]) }, [datasetId, documentId])
if (hasError) { if (hasError)
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} /> return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
}
return ( return (
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}> <div className='flex' style={{ height: 'calc(100vh - 56px)' }}>

View File

@ -1,16 +1,17 @@
const translation = { const translation = {
pageTitle: "Prompt Engineering", pageTitle: 'Prompt Engineering',
operation: { operation: {
applyConfig: "Publish", applyConfig: 'Publish',
resetConfig: "Reset", resetConfig: 'Reset',
addFeature: "Add Feature", addFeature: 'Add Feature',
stopResponding: "Stop responding", automatic: 'Automatic',
stopResponding: 'Stop responding',
}, },
notSetAPIKey: { notSetAPIKey: {
title: "LLM provider key has not been set", title: 'LLM provider key has not been set',
trailFinished: "Trail finished", trailFinished: 'Trail finished',
description: "The LLM provider key has not been set, and it needs to be set before debugging.", description: 'The LLM provider key has not been set, and it needs to be set before debugging.',
settingBtn: "Go to settings", settingBtn: 'Go to settings',
}, },
trailUseGPT4Info: { trailUseGPT4Info: {
title: 'Does not support gpt-4 now', title: 'Does not support gpt-4 now',
@ -19,14 +20,14 @@ const translation = {
feature: { feature: {
groupChat: { groupChat: {
title: 'Chat enhance', title: 'Chat enhance',
description: 'Add pre-conversation settings for apps can enhance user experience.' description: 'Add pre-conversation settings for apps can enhance user experience.',
}, },
groupExperience: { groupExperience: {
title: 'Experience enhance', title: 'Experience enhance',
}, },
conversationOpener: { conversationOpener: {
title: "Conversation remakers", title: 'Conversation remakers',
description: "In a chat app, the first sentence that the AI actively speaks to the user is usually used as a welcome." description: 'In a chat app, the first sentence that the AI actively speaks to the user is usually used as a welcome.',
}, },
suggestedQuestionsAfterAnswer: { suggestedQuestionsAfterAnswer: {
title: 'Follow-up', title: 'Follow-up',
@ -35,105 +36,122 @@ const translation = {
tryToAsk: 'Try to ask', tryToAsk: 'Try to ask',
}, },
moreLikeThis: { moreLikeThis: {
title: "More like this", title: 'More like this',
description: "Generate multiple texts at once, and then edit and continue to generate", description: 'Generate multiple texts at once, and then edit and continue to generate',
generateNumTip: "Number of each generated times", generateNumTip: 'Number of each generated times',
tip: "Using this feature will incur additional tokens overhead" tip: 'Using this feature will incur additional tokens overhead',
}, },
dataSet: { dataSet: {
title: "Context", title: 'Context',
noData: "You can import datasets as context", noData: 'You can import datasets as context',
words: "Words", words: 'Words',
textBlocks: "Text Blocks", textBlocks: 'Text Blocks',
selectTitle: "Select reference dataset", selectTitle: 'Select reference dataset',
selected: "Datasets selected", selected: 'Datasets selected',
noDataSet: "No dataset found", noDataSet: 'No dataset found',
toCreate: "Go to create", toCreate: 'Go to create',
notSupportSelectMulti: 'Currently only support one dataset' notSupportSelectMulti: 'Currently only support one dataset',
} },
},
automatic: {
title: 'Automated application orchestration',
description: 'Describe your scenario, Dify will orchestrate an application for you.',
intendedAudience: 'Who is the intended audience?',
intendedAudiencePlaceHolder: 'e.g. Student',
solveProblem: 'What problems do they hope AI can solve for them?',
solveProblemPlaceHolder: 'e.g. Assessing academic performance',
generate: 'Generate',
audiencesRequired: 'Audiences required',
problemRequired: 'Problem required',
resTitle: 'We have orchestrated the following application for you.',
apply: 'Apply this orchestration',
noData: 'Describe your use case on the left, the orchestration preview will show here.',
loading: 'Orchestrating the application for you...',
overwriteTitle: 'Override existing configuration?',
overwriteMessage: 'Applying this orchestration will override existing configuration.',
}, },
resetConfig: { resetConfig: {
title: "Confirm reset?", title: 'Confirm reset?',
message: message:
"Reset discards changes, restoring the last published configuration.", 'Reset discards changes, restoring the last published configuration.',
}, },
errorMessage: { errorMessage: {
nameOfKeyRequired: "name of the key: {{key}} required", nameOfKeyRequired: 'name of the key: {{key}} required',
valueOfVarRequired: "Variables value can not be empty", valueOfVarRequired: 'Variables value can not be empty',
queryRequired: "Request text is required.", queryRequired: 'Request text is required.',
waitForResponse: waitForResponse:
"Please wait for the response to the previous message to complete.", 'Please wait for the response to the previous message to complete.',
}, },
chatSubTitle: "Pre Prompt", chatSubTitle: 'Pre Prompt',
completionSubTitle: "Prefix Prompt", completionSubTitle: 'Prefix Prompt',
promptTip: promptTip:
"Prompts guide AI responses with instructions and constraints. Insert variables like {{input}}. This prompt won't be visible to users.", 'Prompts guide AI responses with instructions and constraints. Insert variables like {{input}}. This prompt won\'t be visible to users.',
formattingChangedTitle: "Formatting changed", formattingChangedTitle: 'Formatting changed',
formattingChangedText: formattingChangedText:
"Modifying the formatting will reset the debug area, are you sure?", 'Modifying the formatting will reset the debug area, are you sure?',
variableTitle: "Variables", variableTitle: 'Variables',
variableTip: variableTip:
"Users fill variables in a form, automatically replacing variables in the prompt.", 'Users fill variables in a form, automatically replacing variables in the prompt.',
notSetVar: "Variables allow users to introduce prompt words or opening remarks when filling out forms. You can try entering \"{{input}}\" in the prompt words.", notSetVar: 'Variables allow users to introduce prompt words or opening remarks when filling out forms. You can try entering "{{input}}" in the prompt words.',
autoAddVar: "Undefined variables referenced in pre-prompt, are you want to add them in user input form?", autoAddVar: 'Undefined variables referenced in pre-prompt, are you want to add them in user input form?',
variableTable: { variableTable: {
key: "Variable Key", key: 'Variable Key',
name: "User Input Field Name", name: 'User Input Field Name',
optional: "Optional", optional: 'Optional',
type: "Input Type", type: 'Input Type',
action: "Actions", action: 'Actions',
typeString: "String", typeString: 'String',
typeSelect: "Select", typeSelect: 'Select',
}, },
varKeyError: { varKeyError: {
canNoBeEmpty: "Variable key can not be empty", canNoBeEmpty: 'Variable key can not be empty',
tooLong: "Variable key: {{key}} too length. Can not be longer then 16 characters", tooLong: 'Variable key: {{key}} too length. Can not be longer then 16 characters',
notValid: "Variable key: {{key}} is invalid. Can only contain letters, numbers, and underscores", notValid: 'Variable key: {{key}} is invalid. Can only contain letters, numbers, and underscores',
notStartWithNumber: "Variable key: {{key}} can not start with a number", notStartWithNumber: 'Variable key: {{key}} can not start with a number',
}, },
variableConig: { variableConig: {
modalTitle: "Field settings", modalTitle: 'Field settings',
description: "Setting for variable {{varName}}", description: 'Setting for variable {{varName}}',
fieldType: 'Field type', fieldType: 'Field type',
string: 'Text', string: 'Text',
select: 'Select', select: 'Select',
notSet: 'Not set, try typing {{input}} in the prefix prompt', notSet: 'Not set, try typing {{input}} in the prefix prompt',
stringTitle: "Form text box options", stringTitle: 'Form text box options',
maxLength: "Max length", maxLength: 'Max length',
options: "Options", options: 'Options',
addOption: "Add option", addOption: 'Add option',
}, },
openingStatement: { openingStatement: {
title: "Opening remarks", title: 'Opening remarks',
add: "Add", add: 'Add',
writeOpner: "Write remarks", writeOpner: 'Write remarks',
placeholder: "Write your remarks message here", placeholder: 'Write your remarks message here',
noDataPlaceHolder: noDataPlaceHolder:
"Starting the conversation with the user can help AI establish a closer connection with them in conversational applications.", 'Starting the conversation with the user can help AI establish a closer connection with them in conversational applications.',
varTip: 'You can use variables, try type {{variable}}', varTip: 'You can use variables, try type {{variable}}',
tooShort: "At least 20 words of initial prompt are required to generate an opening remarks for the conversation.", tooShort: 'At least 20 words of initial prompt are required to generate an opening remarks for the conversation.',
notIncludeKey: "The initial prompt does not include the variable: {{key}}. Please add it to the initial prompt.", notIncludeKey: 'The initial prompt does not include the variable: {{key}}. Please add it to the initial prompt.',
}, },
modelConfig: { modelConfig: {
model: "Model", model: 'Model',
setTone: "Set tone of responses", setTone: 'Set tone of responses',
title: "Model and Parameters", title: 'Model and Parameters',
}, },
inputs: { inputs: {
title: "Debugging and Previewing", title: 'Debugging and Previewing',
noPrompt: "Try write some prompt in pre-prompt input", noPrompt: 'Try write some prompt in pre-prompt input',
userInputField: "User Input Field", userInputField: 'User Input Field',
noVar: "Fill in the value of the variable, which will be automatically replaced in the prompt word every time a new session is started.", noVar: 'Fill in the value of the variable, which will be automatically replaced in the prompt word every time a new session is started.',
chatVarTip: chatVarTip:
"Fill in the value of the variable, which will be automatically replaced in the prompt word every time a new session is started", 'Fill in the value of the variable, which will be automatically replaced in the prompt word every time a new session is started',
completionVarTip: completionVarTip:
"Fill in the value of the variable, which will be automatically replaced in the prompt words every time a question is submitted.", 'Fill in the value of the variable, which will be automatically replaced in the prompt words every time a question is submitted.',
previewTitle: "Prompt preview", previewTitle: 'Prompt preview',
queryTitle: "Query content", queryTitle: 'Query content',
queryPlaceholder: "Please enter the request text.", queryPlaceholder: 'Please enter the request text.',
run: "RUN", run: 'RUN',
}, },
result: "Output Text", result: 'Output Text',
}; }
export default translation; export default translation

View File

@ -1,16 +1,17 @@
const translation = { const translation = {
pageTitle: "提示词编排", pageTitle: '提示词编排',
operation: { operation: {
applyConfig: "发布", applyConfig: '发布',
resetConfig: "重置", resetConfig: '重置',
addFeature: "添加功能", addFeature: '添加功能',
stopResponding: "停止响应", automatic: '自动编排',
stopResponding: '停止响应',
}, },
notSetAPIKey: { notSetAPIKey: {
title: "LLM 提供者的密钥未设置", title: 'LLM 提供者的密钥未设置',
trailFinished: "试用已结束", trailFinished: '试用已结束',
description: "在调试之前需要设置 LLM 提供者的密钥。", description: '在调试之前需要设置 LLM 提供者的密钥。',
settingBtn: "去设置", settingBtn: '去设置',
}, },
trailUseGPT4Info: { trailUseGPT4Info: {
title: '当前不支持使用 gpt-4', title: '当前不支持使用 gpt-4',
@ -19,14 +20,14 @@ const translation = {
feature: { feature: {
groupChat: { groupChat: {
title: '聊天增强', title: '聊天增强',
description: '为聊天型应用添加预对话设置,可以提升用户体验。' description: '为聊天型应用添加预对话设置,可以提升用户体验。',
}, },
groupExperience: { groupExperience: {
title: '体验增强', title: '体验增强',
}, },
conversationOpener: { conversationOpener: {
title: "对话开场白", title: '对话开场白',
description: "在对话型应用中,让 AI 主动说第一段话可以拉近与用户间的距离。" description: '在对话型应用中,让 AI 主动说第一段话可以拉近与用户间的距离。',
}, },
suggestedQuestionsAfterAnswer: { suggestedQuestionsAfterAnswer: {
title: '下一步问题建议', title: '下一步问题建议',
@ -35,100 +36,117 @@ const translation = {
tryToAsk: '试着问问', tryToAsk: '试着问问',
}, },
moreLikeThis: { moreLikeThis: {
title: "更多类似的", title: '更多类似的',
description: '一次生成多条文本,可在此基础上编辑并继续生成', description: '一次生成多条文本,可在此基础上编辑并继续生成',
generateNumTip: "每次生成数", generateNumTip: '每次生成数',
tip: "使用此功能将会额外消耗 tokens" tip: '使用此功能将会额外消耗 tokens',
}, },
dataSet: { dataSet: {
title: "上下文", title: '上下文',
noData: "您可以导入数据集作为上下文", noData: '您可以导入数据集作为上下文',
words: "词", words: '词',
textBlocks: "文本块", textBlocks: '文本块',
selectTitle: "选择引用数据集", selectTitle: '选择引用数据集',
selected: "个数据集被选中", selected: '个数据集被选中',
noDataSet: "未找到数据集", noDataSet: '未找到数据集',
toCreate: "去创建", toCreate: '去创建',
notSupportSelectMulti: '目前只支持引用一个数据集' notSupportSelectMulti: '目前只支持引用一个数据集',
} },
},
automatic: {
title: '自动编排',
description: '描述您的场景Dify 将为您编排一个应用。',
intendedAudience: '目标用户是谁?',
intendedAudiencePlaceHolder: '例如:学生',
solveProblem: '希望 AI 为他们解决什么问题?',
solveProblemPlaceHolder: '例如:评估学业水平',
generate: '生成',
audiencesRequired: '目标用户必填',
problemRequired: '解决问题必填',
resTitle: '我们为您编排了以下应用程序',
apply: '应用',
noData: '在左侧描述您的用例,编排预览将在此处显示。',
loading: '为您编排应用程序中…',
overwriteTitle: '覆盖现有配置?',
overwriteMessage: '应用此编排将覆盖现有配置。',
}, },
resetConfig: { resetConfig: {
title: "确认重置?", title: '确认重置?',
message: "重置将丢失当前页面所有修改,恢复至上次发布时的配置", message: '重置将丢失当前页面所有修改,恢复至上次发布时的配置',
}, },
errorMessage: { errorMessage: {
nameOfKeyRequired: "变量 {{key}} 对应的名称必填", nameOfKeyRequired: '变量 {{key}} 对应的名称必填',
valueOfVarRequired: "变量值必填", valueOfVarRequired: '变量值必填',
queryRequired: "主要文本必填", queryRequired: '主要文本必填',
waitForResponse: "请等待上条信息响应完成", waitForResponse: '请等待上条信息响应完成',
}, },
chatSubTitle: "对话前提示词", chatSubTitle: '对话前提示词',
completionSubTitle: "前缀提示词", completionSubTitle: '前缀提示词',
promptTip: promptTip:
"提示词用于对 AI 的回复做出一系列指令和约束。可插入表单变量,例如 {{input}}。这段提示词不会被最终用户所看到。", '提示词用于对 AI 的回复做出一系列指令和约束。可插入表单变量,例如 {{input}}。这段提示词不会被最终用户所看到。',
formattingChangedTitle: "编排已改变", formattingChangedTitle: '编排已改变',
formattingChangedText: "修改编排将重置调试区域,确定吗?", formattingChangedText: '修改编排将重置调试区域,确定吗?',
variableTitle: "变量", variableTitle: '变量',
notSetVar: "变量能使用户输入表单引入提示词或开场白,你可以试试在提示词中输入输入 {{input}}", notSetVar: '变量能使用户输入表单引入提示词或开场白,你可以试试在提示词中输入输入 {{input}}',
variableTip: variableTip:
"变量将以表单形式让用户在对话前填写,用户填写的表单内容将自动替换提示词中的变量。", '变量将以表单形式让用户在对话前填写,用户填写的表单内容将自动替换提示词中的变量。',
autoAddVar: "提示词中引用了未定义的变量,是否自动添加到用户输入表单中?", autoAddVar: '提示词中引用了未定义的变量,是否自动添加到用户输入表单中?',
variableTable: { variableTable: {
key: "变量 Key", key: '变量 Key',
name: "字段名称", name: '字段名称',
optional: "可选", optional: '可选',
type: "类型", type: '类型',
action: "操作", action: '操作',
typeString: "文本", typeString: '文本',
typeSelect: "下拉选项", typeSelect: '下拉选项',
}, },
varKeyError: { varKeyError: {
canNoBeEmpty: "变量不能为空", canNoBeEmpty: '变量不能为空',
tooLong: "变量: {{key}} 长度太长。不能超过 16 个字符", tooLong: '变量: {{key}} 长度太长。不能超过 16 个字符',
notValid: "变量: {{key}} 非法。只能包含英文字符,数字和下划线", notValid: '变量: {{key}} 非法。只能包含英文字符,数字和下划线',
notStartWithNumber: "变量: {{key}} 不能以数字开头", notStartWithNumber: '变量: {{key}} 不能以数字开头',
}, },
variableConig: { variableConig: {
modalTitle: "变量设置", modalTitle: '变量设置',
description: "设置变量 {{varName}}", description: '设置变量 {{varName}}',
fieldType: '字段类型', fieldType: '字段类型',
string: '文本', string: '文本',
select: '下拉选项', select: '下拉选项',
notSet: '未设置,在 Prompt 中输入 {{input}} 试试', notSet: '未设置,在 Prompt 中输入 {{input}} 试试',
stringTitle: "文本框设置", stringTitle: '文本框设置',
maxLength: "最大长度", maxLength: '最大长度',
options: "选项", options: '选项',
addOption: "添加选项", addOption: '添加选项',
}, },
openingStatement: { openingStatement: {
title: "对话开场白", title: '对话开场白',
add: "添加开场白", add: '添加开场白',
writeOpner: "编写开场白", writeOpner: '编写开场白',
placeholder: "请在这里输入开场白", placeholder: '请在这里输入开场白',
noDataPlaceHolder: noDataPlaceHolder:
"在对话型应用中,让 AI 主动说第一段话可以拉近与用户间的距离。", '在对话型应用中,让 AI 主动说第一段话可以拉近与用户间的距离。',
varTip: '你可以使用变量, 试试输入 {{variable}}', varTip: '你可以使用变量, 试试输入 {{variable}}',
tooShort: "对话前提示词至少 20 字才能生成开场白", tooShort: '对话前提示词至少 20 字才能生成开场白',
notIncludeKey: "前缀提示词中不包含变量 {{key}}。请在前缀提示词中添加该变量", notIncludeKey: '前缀提示词中不包含变量 {{key}}。请在前缀提示词中添加该变量',
}, },
modelConfig: { modelConfig: {
model: "语言模型", model: '语言模型',
setTone: "模型设置", setTone: '模型设置',
title: "模型及参数", title: '模型及参数',
}, },
inputs: { inputs: {
title: "调试与预览", title: '调试与预览',
noPrompt: "尝试在对话前提示框中编写一些提示词", noPrompt: '尝试在对话前提示框中编写一些提示词',
userInputField: "用户输入", userInputField: '用户输入',
noVar: "填入变量的值,每次启动新会话时该变量将自动替换提示词中的变量。", noVar: '填入变量的值,每次启动新会话时该变量将自动替换提示词中的变量。',
chatVarTip: "填入变量的值,该值将在每次开启一个新会话时自动替换到提示词中", chatVarTip: '填入变量的值,该值将在每次开启一个新会话时自动替换到提示词中',
completionVarTip: "填入变量的值,该值将在每次提交问题时自动替换到提示词中", completionVarTip: '填入变量的值,该值将在每次提交问题时自动替换到提示词中',
previewTitle: "提示词预览", previewTitle: '提示词预览',
queryTitle: "查询内容", queryTitle: '查询内容',
queryPlaceholder: "请输入文本内容", queryPlaceholder: '请输入文本内容',
run: "运行", run: '运行',
}, },
result: "结果", result: '结果',
}; }
export default translation; export default translation

View File

@ -102,8 +102,8 @@ const translation = {
sideTipContent: 'After the document finishes indexing, the dataset can be integrated into the application as context, you can find the context setting in the prompt orchestration page. You can also create it as an independent ChatGPT indexing plugin for release.', sideTipContent: 'After the document finishes indexing, the dataset can be integrated into the application as context, you can find the context setting in the prompt orchestration page. You can also create it as an independent ChatGPT indexing plugin for release.',
modelTitle: 'Are you sure to stop embedding?', modelTitle: 'Are you sure to stop embedding?',
modelContent: 'If you need to resume processing later, you will continue from where you left off.', modelContent: 'If you need to resume processing later, you will continue from where you left off.',
modelButtonConfirm: "Confirm", modelButtonConfirm: 'Confirm',
modelButtonCancel: 'Cancel' modelButtonCancel: 'Cancel',
}, },
} }

View File

@ -101,9 +101,9 @@ const translation = {
sideTipTitle: '接下来做什么', sideTipTitle: '接下来做什么',
sideTipContent: '当文档完成索引处理后,数据集即可集成至应用内作为上下文使用,你可以在提示词编排页找到上下文设置。你也可以创建成可独立使用的 ChatGPT 索引插件发布。', sideTipContent: '当文档完成索引处理后,数据集即可集成至应用内作为上下文使用,你可以在提示词编排页找到上下文设置。你也可以创建成可独立使用的 ChatGPT 索引插件发布。',
modelTitle: '确认停止索引过程吗?', modelTitle: '确认停止索引过程吗?',
modelContent:'如果您需要稍后恢复处理,则从停止处继续。', modelContent: '如果您需要稍后恢复处理,则从停止处继续。',
modelButtonConfirm: "确认停止", modelButtonConfirm: '确认停止',
modelButtonCancel: '取消' modelButtonCancel: '取消',
}, },
} }

View File

@ -1,16 +1,17 @@
import { ssePost, get, IOnData, IOnCompleted, IOnError } from './base' import type { IOnCompleted, IOnData, IOnError } from './base'
import { get, post, ssePost } from './base'
export const sendChatMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError, getAbortController }: { export const sendChatMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError, getAbortController }: {
onData: IOnData onData: IOnData
onCompleted: IOnCompleted onCompleted: IOnCompleted
onError: IOnError, onError: IOnError
getAbortController?: (abortController: AbortController) => void getAbortController?: (abortController: AbortController) => void
}) => { }) => {
return ssePost(`apps/${appId}/chat-messages`, { return ssePost(`apps/${appId}/chat-messages`, {
body: { body: {
...body, ...body,
response_mode: 'streaming' response_mode: 'streaming',
} },
}, { onData, onCompleted, onError, getAbortController }) }, { onData, onCompleted, onError, getAbortController })
} }
@ -22,8 +23,8 @@ export const sendCompletionMessage = async (appId: string, body: Record<string,
return ssePost(`apps/${appId}/completion-messages`, { return ssePost(`apps/${appId}/completion-messages`, {
body: { body: {
...body, ...body,
response_mode: 'streaming' response_mode: 'streaming',
} },
}, { onData, onCompleted, onError }) }, { onData, onCompleted, onError })
} }
@ -34,7 +35,13 @@ export const fetchSuggestedQuestions = (appId: string, messageId: string) => {
export const fetchConvesationMessages = (appId: string, conversation_id: string) => { export const fetchConvesationMessages = (appId: string, conversation_id: string) => {
return get(`apps/${appId}/chat-messages`, { return get(`apps/${appId}/chat-messages`, {
params: { params: {
conversation_id conversation_id,
} },
})
}
export const generateRule = (body: Record<string, any>) => {
return post('/rule-generate', {
body,
}) })
} }