feat: advanced prompt (#1330)
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: Gillian97 <jinling.sunshine@gmail.com>
This commit is contained in:
parent
42a5b3ec17
commit
5b9858a8a3
131 changed files with 5940 additions and 447 deletions
|
|
@ -53,6 +53,7 @@ export type IChatProps = {
|
|||
isShowConfigElem?: boolean
|
||||
dataSets?: DataSet[]
|
||||
isShowCitationHitInfo?: boolean
|
||||
isShowPromptLog?: boolean
|
||||
}
|
||||
|
||||
const Chat: FC<IChatProps> = ({
|
||||
|
|
@ -81,6 +82,7 @@ const Chat: FC<IChatProps> = ({
|
|||
isShowConfigElem,
|
||||
dataSets,
|
||||
isShowCitationHitInfo,
|
||||
isShowPromptLog,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
|
|
@ -186,7 +188,18 @@ const Chat: FC<IChatProps> = ({
|
|||
isShowCitationHitInfo={isShowCitationHitInfo}
|
||||
/>
|
||||
}
|
||||
return <Question key={item.id} id={item.id} content={item.content} more={item.more} useCurrentUserAvatar={useCurrentUserAvatar} />
|
||||
return (
|
||||
<Question
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
content={item.content}
|
||||
more={item.more}
|
||||
useCurrentUserAvatar={useCurrentUserAvatar}
|
||||
item={item}
|
||||
isShowPromptLog={isShowPromptLog}
|
||||
isResponsing={isResponsing}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{
|
||||
|
|
|
|||
70
web/app/components/app/chat/log/index.tsx
Normal file
70
web/app/components/app/chat/log/index.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import type { Dispatch, FC, ReactNode, RefObject, SetStateAction } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { File02 } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import PromptLogModal from '@/app/components/base/prompt-log-modal'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
export type LogData = {
|
||||
role: string
|
||||
text: string
|
||||
}
|
||||
|
||||
type LogProps = {
|
||||
containerRef: RefObject<HTMLElement>
|
||||
log: LogData[]
|
||||
children?: (v: Dispatch<SetStateAction<boolean>>) => ReactNode
|
||||
}
|
||||
const Log: FC<LogProps> = ({
|
||||
containerRef,
|
||||
children,
|
||||
log,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [width, setWidth] = useState(0)
|
||||
|
||||
const adjustModalWidth = () => {
|
||||
if (containerRef.current)
|
||||
setWidth(document.body.clientWidth - (containerRef.current?.clientWidth + 56 + 16))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
adjustModalWidth()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
children
|
||||
? children(setShowModal)
|
||||
: (
|
||||
<Tooltip selector='prompt-log-modal-trigger' content={t('common.operation.log') || ''}>
|
||||
<div className={`
|
||||
hidden absolute -left-[14px] -top-[14px] group-hover:block w-7 h-7
|
||||
p-0.5 rounded-lg border-[0.5px] border-gray-100 bg-white shadow-md cursor-pointer
|
||||
`}>
|
||||
<div
|
||||
className='flex items-center justify-center rounded-md w-full h-full hover:bg-gray-100'
|
||||
onClick={() => setShowModal(true)}
|
||||
>
|
||||
<File02 className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{
|
||||
showModal && (
|
||||
<PromptLogModal
|
||||
width={width}
|
||||
log={log}
|
||||
onCancel={() => setShowModal(false)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Log
|
||||
|
|
@ -1,22 +1,34 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useRef } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import s from '../style.module.css'
|
||||
import type { IChatItem } from '../type'
|
||||
import Log from '../log'
|
||||
import MoreInfo from '../more-info'
|
||||
import AppContext from '@/context/app-context'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
|
||||
type IQuestionProps = Pick<IChatItem, 'id' | 'content' | 'more' | 'useCurrentUserAvatar'>
|
||||
type IQuestionProps = Pick<IChatItem, 'id' | 'content' | 'more' | 'useCurrentUserAvatar'> & {
|
||||
isShowPromptLog?: boolean
|
||||
item: IChatItem
|
||||
isResponsing?: boolean
|
||||
}
|
||||
|
||||
const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar }) => {
|
||||
const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar, isShowPromptLog, item, isResponsing }) => {
|
||||
const { userProfile } = useContext(AppContext)
|
||||
const userName = userProfile?.name
|
||||
const ref = useRef(null)
|
||||
|
||||
return (
|
||||
<div className='flex items-start justify-end' key={id}>
|
||||
<div className={`flex items-start justify-end ${isShowPromptLog && 'first-of-type:pt-[14px]'}`} key={id} ref={ref}>
|
||||
<div className={s.questionWrapWrap}>
|
||||
<div className={`${s.question} relative text-sm text-gray-900`}>
|
||||
<div className={`${s.question} group relative text-sm text-gray-900`}>
|
||||
{
|
||||
isShowPromptLog && !isResponsing && (
|
||||
<Log log={item.log!} containerRef={ref} />
|
||||
)
|
||||
}
|
||||
<div
|
||||
className={'mr-2 py-3 px-4 bg-blue-500 rounded-tl-2xl rounded-b-2xl'}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export type IChatItem = {
|
|||
annotation?: Annotation
|
||||
useCurrentUserAvatar?: boolean
|
||||
isOpeningStatement?: boolean
|
||||
log?: { role: string; text: string }[]
|
||||
}
|
||||
|
||||
export type MessageEnd = {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const FeaturePanel: FC<IFeaturePanelProps> = ({
|
|||
<div className={cn('pb-2 px-3', hasHeaderBottomBorder && 'border-b border-gray-100')}>
|
||||
<div className='flex justify-between items-center h-8'>
|
||||
<div className='flex items-center space-x-1 shrink-0'>
|
||||
{headerIcon && <div className='flex items-center justify-center w-4 h-4'>{headerIcon}</div>}
|
||||
{headerIcon && <div className='flex items-center justify-center w-6 h-6'>{headerIcon}</div>}
|
||||
<div className='text-sm font-semibold text-gray-800'>{title}</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import React, { useEffect, useState } from 'react'
|
|||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean, useClickAway, useGetState } from 'ahooks'
|
||||
import { Cog8ToothIcon, InformationCircleIcon } from '@heroicons/react/24/outline'
|
||||
import { InformationCircleIcon } from '@heroicons/react/24/outline'
|
||||
import produce from 'immer'
|
||||
import ParamItem from './param-item'
|
||||
import ModelIcon from './model-icon'
|
||||
import ModelName from './model-name'
|
||||
import ModelModeTypeLabel from './model-mode-type-label'
|
||||
import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import Panel from '@/app/components/base/panel'
|
||||
import type { CompletionParams } from '@/models/debug'
|
||||
|
|
@ -25,21 +27,23 @@ import Loading from '@/app/components/base/loading'
|
|||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
export type IConfigModelProps = {
|
||||
isAdvancedMode: boolean
|
||||
mode: string
|
||||
modelId: string
|
||||
provider: ProviderEnum
|
||||
setModelId: (id: string, provider: ProviderEnum) => void
|
||||
setModel: (model: { id: string; provider: ProviderEnum; mode: ModelModeType }) => void
|
||||
completionParams: CompletionParams
|
||||
onCompletionParamsChange: (newParams: CompletionParams) => void
|
||||
disabled: boolean
|
||||
}
|
||||
|
||||
const ConfigModel: FC<IConfigModelProps> = ({
|
||||
isAdvancedMode,
|
||||
modelId,
|
||||
provider,
|
||||
setModelId,
|
||||
setModel,
|
||||
completionParams,
|
||||
onCompletionParamsChange,
|
||||
disabled,
|
||||
|
|
@ -56,6 +60,8 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
const hasEnableParams = currParams && Object.keys(currParams).some(key => currParams[key].enabled)
|
||||
const allSupportParams = ['temperature', 'top_p', 'presence_penalty', 'frequency_penalty', 'max_tokens']
|
||||
const currSupportParams = currParams ? allSupportParams.filter(key => currParams[key].enabled) : allSupportParams
|
||||
if (isAdvancedMode)
|
||||
currSupportParams.push('stop')
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
|
|
@ -115,11 +121,15 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
return adjustedValue
|
||||
}
|
||||
|
||||
const handleSelectModel = (id: string, nextProvider = ProviderEnum.openai) => {
|
||||
const handleSelectModel = ({ id, provider: nextProvider, mode }: { id: string; provider: ProviderEnum; mode: ModelModeType }) => {
|
||||
return async () => {
|
||||
const prevParamsRule = getAllParams()[provider]?.[modelId]
|
||||
|
||||
setModelId(id, nextProvider)
|
||||
setModel({
|
||||
id,
|
||||
provider: nextProvider || ProviderEnum.openai,
|
||||
mode,
|
||||
})
|
||||
|
||||
await ensureModelParamLoaded(nextProvider, id)
|
||||
|
||||
|
|
@ -211,16 +221,26 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
setToneId(matchToneId(completionParams))
|
||||
}, [completionParams])
|
||||
|
||||
const handleParamChange = (key: string, value: number) => {
|
||||
const currParamsRule = getAllParams()[provider]?.[modelId]
|
||||
let notOutRangeValue = parseFloat((value || 0).toFixed(2))
|
||||
notOutRangeValue = Math.max(currParamsRule[key].min, notOutRangeValue)
|
||||
notOutRangeValue = Math.min(currParamsRule[key].max, notOutRangeValue)
|
||||
const handleParamChange = (key: string, value: number | string[]) => {
|
||||
if (value === undefined)
|
||||
return
|
||||
|
||||
onCompletionParamsChange({
|
||||
...completionParams,
|
||||
[key]: notOutRangeValue,
|
||||
})
|
||||
if (key === 'stop') {
|
||||
onCompletionParamsChange({
|
||||
...completionParams,
|
||||
[key]: value as string[],
|
||||
})
|
||||
}
|
||||
else {
|
||||
const currParamsRule = getAllParams()[provider]?.[modelId]
|
||||
let notOutRangeValue = parseFloat((value as number).toFixed(2))
|
||||
notOutRangeValue = Math.max(currParamsRule[key].min, notOutRangeValue)
|
||||
notOutRangeValue = Math.min(currParamsRule[key].max, notOutRangeValue)
|
||||
onCompletionParamsChange({
|
||||
...completionParams,
|
||||
[key]: notOutRangeValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
const ableStyle = 'bg-indigo-25 border-[#2A87F5] cursor-pointer'
|
||||
const diabledStyle = 'bg-[#FFFCF5] border-[#F79009]'
|
||||
|
|
@ -228,7 +248,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
const getToneIcon = (toneId: number) => {
|
||||
const className = 'w-[14px] h-[14px]'
|
||||
const res = ({
|
||||
1: <Brush01 className={className}/>,
|
||||
1: <Brush01 className={className} />,
|
||||
2: <Scales02 className={className} />,
|
||||
3: <Target04 className={className} />,
|
||||
4: <Sliders02 className={className} />,
|
||||
|
|
@ -249,17 +269,19 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
return (
|
||||
<div className='relative' ref={configContentRef}>
|
||||
<div
|
||||
className={cn('flex items-center border h-8 px-2.5 space-x-2 rounded-lg', disabled ? diabledStyle : ableStyle)}
|
||||
className={cn('flex items-center border h-8 px-2 space-x-2 rounded-lg', disabled ? diabledStyle : ableStyle)}
|
||||
onClick={() => !disabled && toogleShowConfig()}
|
||||
>
|
||||
<ModelIcon
|
||||
className='!w-5 !h-5'
|
||||
modelId={modelId}
|
||||
providerName={provider}
|
||||
/>
|
||||
<div className='text-[13px] text-gray-900 font-medium'>
|
||||
<ModelName modelId={selectedModel.name} modelDisplayName={currModel?.model_display_name} />
|
||||
</div>
|
||||
{disabled ? <InformationCircleIcon className='w-3.5 h-3.5 text-[#F79009]' /> : <Cog8ToothIcon className='w-3.5 h-3.5 text-gray-500' />}
|
||||
{isAdvancedMode && <ModelModeTypeLabel type={currModel?.model_mode as ModelModeType} isHighlight />}
|
||||
{disabled ? <InformationCircleIcon className='w-4 h-4 text-[#F79009]' /> : <SlidersH className='w-4 h-4 text-indigo-600' />}
|
||||
</div>
|
||||
{isShowConfig && (
|
||||
<Panel
|
||||
|
|
@ -282,6 +304,8 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
<div className="flex items-center justify-between my-5 h-9">
|
||||
<div>{t('appDebug.modelConfig.model')}</div>
|
||||
<ModelSelector
|
||||
isShowModelModeType={isAdvancedMode}
|
||||
isShowAddModel
|
||||
popClassName='right-0'
|
||||
triggerIconSmall
|
||||
value={{
|
||||
|
|
@ -290,7 +314,11 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
}}
|
||||
modelType={ModelType.textGeneration}
|
||||
onChange={(model) => {
|
||||
handleSelectModel(model.model_name, model.model_provider.provider_name as ProviderEnum)()
|
||||
handleSelectModel({
|
||||
id: model.model_name,
|
||||
provider: model.model_provider.provider_name as ProviderEnum,
|
||||
mode: model.model_mode,
|
||||
})()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -343,20 +371,21 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
|
||||
{/* Params */}
|
||||
<div className={cn(hasEnableParams && 'mt-4', 'space-y-4', !allParams[provider]?.[modelId] && 'flex items-center min-h-[200px]')}>
|
||||
{allParams[provider]?.[modelId]
|
||||
{(allParams[provider]?.[modelId])
|
||||
? (
|
||||
currSupportParams.map(key => (<ParamItem
|
||||
key={key}
|
||||
id={key}
|
||||
name={t(`common.model.params.${key}`)}
|
||||
tip={t(`common.model.params.${key}Tip`)}
|
||||
name={t(`common.model.params.${key === 'stop' ? 'stop_sequences' : key}`)}
|
||||
tip={t(`common.model.params.${key === 'stop' ? 'stop_sequences' : key}Tip`)}
|
||||
{...currParams[key] as any}
|
||||
value={(completionParams as any)[key] as any}
|
||||
onChange={handleParamChange}
|
||||
inputType={key === 'stop' ? 'inputTag' : 'slider'}
|
||||
/>))
|
||||
)
|
||||
: (
|
||||
<Loading type='area'/>
|
||||
<Loading type='area' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
type: ModelModeType
|
||||
isHighlight?: boolean
|
||||
}
|
||||
|
||||
const ModelModeTypeLabel: FC<Props> = ({
|
||||
className,
|
||||
type,
|
||||
isHighlight,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border rounded text-xs font-semibold uppercase')}
|
||||
>
|
||||
{t(`appDebug.modelConfig.modeType.${type}`)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(ModelModeTypeLabel)
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import TagInput from '@/app/components/base/tag-input'
|
||||
|
||||
export const getFitPrecisionValue = (num: number, precision: number | null) => {
|
||||
if (!precision || !(`${num}`).includes('.'))
|
||||
|
|
@ -19,16 +21,19 @@ export type IParamIteProps = {
|
|||
id: string
|
||||
name: string
|
||||
tip: string
|
||||
value: number
|
||||
value: number | string[]
|
||||
step?: number
|
||||
min?: number
|
||||
max: number
|
||||
precision: number | null
|
||||
onChange: (key: string, value: number) => void
|
||||
onChange: (key: string, value: number | string[]) => void
|
||||
inputType?: 'inputTag' | 'slider'
|
||||
}
|
||||
|
||||
const TIMES_TEMPLATE = '1000000000000'
|
||||
const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max, precision, value, onChange }) => {
|
||||
const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max, precision, value, inputType, onChange }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const getToIntTimes = (num: number) => {
|
||||
if (precision)
|
||||
return parseInt(TIMES_TEMPLATE.slice(0, precision + 1), 10)
|
||||
|
|
@ -45,30 +50,44 @@ const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max
|
|||
}, [value, precision])
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>
|
||||
{/* Give tooltip different tip to avoiding hide bug */}
|
||||
<Tooltip htmlContent={<div className="w-[200px] whitespace-pre-wrap">{tip}</div>} position='top' selector={`param-name-tooltip-${id}`}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.66667 10.6667H8V8H7.33333M8 5.33333H8.00667M14 8C14 8.78793 13.8448 9.56815 13.5433 10.2961C13.2417 11.0241 12.7998 11.6855 12.2426 12.2426C11.6855 12.7998 11.0241 13.2417 10.2961 13.5433C9.56815 13.8448 8.78793 14 8 14C7.21207 14 6.43185 13.8448 5.7039 13.5433C4.97595 13.2417 4.31451 12.7998 3.75736 12.2426C3.20021 11.6855 2.75825 11.0241 2.45672 10.2961C2.15519 9.56815 2 8.78793 2 8C2 6.4087 2.63214 4.88258 3.75736 3.75736C4.88258 2.63214 6.4087 2 8 2C9.5913 2 11.1174 2.63214 12.2426 3.75736C13.3679 4.88258 14 6.4087 14 8Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
<div className="flex flex-col flex-shrink-0">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>
|
||||
{/* Give tooltip different tip to avoiding hide bug */}
|
||||
<Tooltip htmlContent={<div className="w-[200px] whitespace-pre-wrap">{tip}</div>} position='top' selector={`param-name-tooltip-${id}`}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.66667 10.6667H8V8H7.33333M8 5.33333H8.00667M14 8C14 8.78793 13.8448 9.56815 13.5433 10.2961C13.2417 11.0241 12.7998 11.6855 12.2426 12.2426C11.6855 12.7998 11.0241 13.2417 10.2961 13.5433C9.56815 13.8448 8.78793 14 8 14C7.21207 14 6.43185 13.8448 5.7039 13.5433C4.97595 13.2417 4.31451 12.7998 3.75736 12.2426C3.20021 11.6855 2.75825 11.0241 2.45672 10.2961C2.15519 9.56815 2 8.78793 2 8C2 6.4087 2.63214 4.88258 3.75736 3.75736C4.88258 2.63214 6.4087 2 8 2C9.5913 2 11.1174 2.63214 12.2426 3.75736C13.3679 4.88258 14 6.4087 14 8Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{inputType === 'inputTag' && <div className="text-gray-400 text-xs font-normal">{t('common.model.params.stop_sequencesPlaceholder')}</div>}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 w-[120px]">
|
||||
<Slider value={value * times} min={min * times} max={max * times} onChange={(value) => {
|
||||
onChange(id, value / times)
|
||||
}} />
|
||||
</div>
|
||||
<input type="number" min={min} max={max} step={step} className="block w-[64px] h-9 leading-9 rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600" value={value} onChange={(e) => {
|
||||
let value = getFitPrecisionValue(isNaN(parseFloat(e.target.value)) ? min : parseFloat(e.target.value), precision)
|
||||
if (value < min)
|
||||
value = min
|
||||
{inputType === 'inputTag'
|
||||
? <TagInput
|
||||
items={(value ?? []) as string[]}
|
||||
onChange={newSequences => onChange(id, newSequences)}
|
||||
customizedConfirmKey='Tab'
|
||||
/>
|
||||
: (
|
||||
<>
|
||||
<div className="mr-4 w-[120px]">
|
||||
<Slider value={value * times} min={min * times} max={max * times} onChange={(value) => {
|
||||
onChange(id, value / times)
|
||||
}} />
|
||||
</div>
|
||||
<input type="number" min={min} max={max} step={step} className="block w-[64px] h-9 leading-9 rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600" value={value} onChange={(e) => {
|
||||
let value = getFitPrecisionValue(isNaN(parseFloat(e.target.value)) ? min : parseFloat(e.target.value), precision)
|
||||
if (value < min)
|
||||
value = min
|
||||
|
||||
if (value > max)
|
||||
value = max
|
||||
onChange(id, value)
|
||||
}} />
|
||||
if (value > max)
|
||||
value = max
|
||||
onChange(id, value)
|
||||
}} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import produce from 'immer'
|
||||
import s from './style.module.css'
|
||||
import MessageTypeSelector from './message-type-selector'
|
||||
import ConfirmAddVar from './confirm-add-var'
|
||||
import type { PromptRole, PromptVariable } from '@/models/debug'
|
||||
import { HelpCircle, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Clipboard, ClipboardCheck } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { getNewVar, getVars } from '@/utils/var'
|
||||
import { AppType } from '@/types/app'
|
||||
|
||||
type Props = {
|
||||
type: PromptRole
|
||||
isChatMode: boolean
|
||||
value: string
|
||||
onTypeChange: (value: PromptRole) => void
|
||||
onChange: (value: string) => void
|
||||
canDelete: boolean
|
||||
onDelete: () => void
|
||||
promptVariables: PromptVariable[]
|
||||
}
|
||||
|
||||
const AdvancedPromptInput: FC<Props> = ({
|
||||
type,
|
||||
isChatMode,
|
||||
value,
|
||||
onChange,
|
||||
onTypeChange,
|
||||
canDelete,
|
||||
onDelete,
|
||||
promptVariables,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const {
|
||||
mode,
|
||||
hasSetBlockStatus,
|
||||
modelConfig,
|
||||
setModelConfig,
|
||||
conversationHistoriesRole,
|
||||
showHistoryModal,
|
||||
dataSets,
|
||||
showSelectDataSet,
|
||||
} = useContext(ConfigContext)
|
||||
const isChatApp = mode === AppType.chat
|
||||
const [isCopied, setIsCopied] = React.useState(false)
|
||||
|
||||
const promptVariablesObj = (() => {
|
||||
const obj: Record<string, boolean> = {}
|
||||
promptVariables.forEach((item) => {
|
||||
obj[item.key] = true
|
||||
})
|
||||
return obj
|
||||
})()
|
||||
const [newPromptVariables, setNewPromptVariables] = React.useState<PromptVariable[]>(promptVariables)
|
||||
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
|
||||
const handlePromptChange = (newValue: string) => {
|
||||
if (value === newValue)
|
||||
return
|
||||
onChange(newValue)
|
||||
}
|
||||
const handleBlur = () => {
|
||||
const keys = getVars(value)
|
||||
const newPromptVariables = keys.filter(key => !(key in promptVariablesObj)).map(key => getNewVar(key))
|
||||
if (newPromptVariables.length > 0) {
|
||||
setNewPromptVariables(newPromptVariables)
|
||||
showConfirmAddVar()
|
||||
}
|
||||
}
|
||||
|
||||
const handleAutoAdd = (isAdd: boolean) => {
|
||||
return () => {
|
||||
if (isAdd) {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...newPromptVariables]
|
||||
})
|
||||
setModelConfig(newModelConfig)
|
||||
}
|
||||
hideConfirmAddVar()
|
||||
}
|
||||
}
|
||||
|
||||
const editorHeight = isChatMode ? 'h-[200px]' : 'h-[508px]'
|
||||
|
||||
return (
|
||||
<div className={`relative ${s.gradientBorder}`}>
|
||||
<div className='rounded-xl bg-white'>
|
||||
<div className={cn(s.boxHeader, 'flex justify-between items-center h-11 pt-2 pr-3 pb-1 pl-4 rounded-tl-xl rounded-tr-xl bg-white hover:shadow-xs')}>
|
||||
{isChatMode
|
||||
? (
|
||||
<MessageTypeSelector value={type} onChange={onTypeChange} />
|
||||
)
|
||||
: (
|
||||
<div className='flex items-center space-x-1'>
|
||||
|
||||
<div className='text-sm font-semibold uppercase text-indigo-800'>{t('appDebug.pageTitle.line1')}
|
||||
</div>
|
||||
<Tooltip
|
||||
htmlContent={<div className='w-[180px]'>
|
||||
{t('appDebug.promptTip')}
|
||||
</div>}
|
||||
selector='config-prompt-tooltip'>
|
||||
<HelpCircle className='w-[14px] h-[14px] text-indigo-400' />
|
||||
</Tooltip>
|
||||
</div>)}
|
||||
<div className={cn(s.optionWrap, 'items-center space-x-1')}>
|
||||
{canDelete && (
|
||||
<Trash03 onClick={onDelete} className='h-6 w-6 p-1 text-gray-500 cursor-pointer' />
|
||||
)}
|
||||
{!isCopied
|
||||
? (
|
||||
<Clipboard className='h-6 w-6 p-1 text-gray-500 cursor-pointer' onClick={() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}} />
|
||||
)
|
||||
: (
|
||||
<ClipboardCheck className='h-6 w-6 p-1 text-gray-500' />
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(editorHeight, 'px-4 min-h-[102px] overflow-y-auto text-sm text-gray-700')}>
|
||||
<PromptEditor
|
||||
className={editorHeight}
|
||||
value={value}
|
||||
contextBlock={{
|
||||
selectable: !hasSetBlockStatus.context,
|
||||
datasets: dataSets.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
type: item.data_source_type,
|
||||
})),
|
||||
onAddContext: showSelectDataSet,
|
||||
}}
|
||||
variableBlock={{
|
||||
variables: modelConfig.configs.prompt_variables.map(item => ({
|
||||
name: item.name,
|
||||
value: item.key,
|
||||
})),
|
||||
}}
|
||||
historyBlock={{
|
||||
show: !isChatMode && isChatApp,
|
||||
selectable: !hasSetBlockStatus.history,
|
||||
history: {
|
||||
user: conversationHistoriesRole?.user_prefix,
|
||||
assistant: conversationHistoriesRole?.assistant_prefix,
|
||||
},
|
||||
onEditRole: showHistoryModal,
|
||||
}}
|
||||
queryBlock={{
|
||||
show: !isChatMode && isChatApp,
|
||||
selectable: !hasSetBlockStatus.query,
|
||||
}}
|
||||
onChange={handlePromptChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</div>
|
||||
<div className='pl-4 pb-2 flex'>
|
||||
<div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{value.length}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isShowConfirmAddVar && (
|
||||
<ConfirmAddVar
|
||||
varNameArr={newPromptVariables.map(v => v.name)}
|
||||
onConfrim={handleAutoAdd(true)}
|
||||
onCancel={handleAutoAdd(false)}
|
||||
onHide={hideConfirmAddVar}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(AdvancedPromptInput)
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
'use client'
|
||||
import React, { FC, useRef } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import React, { useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useClickAway } from 'ahooks'
|
||||
import VarHighlight from '../../base/var-highlight'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
export interface IConfirmAddVarProps {
|
||||
export type IConfirmAddVarProps = {
|
||||
varNameArr: string[]
|
||||
onConfrim: () => void
|
||||
onCancel: () => void
|
||||
|
|
@ -28,19 +28,20 @@ const ConfirmAddVar: FC<IConfirmAddVarProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const mainContentRef = useRef<HTMLDivElement>(null)
|
||||
useClickAway(() => {
|
||||
onHide()
|
||||
}, mainContentRef)
|
||||
// new prompt editor blur trigger click...
|
||||
// useClickAway(() => {
|
||||
// onHide()
|
||||
// }, mainContentRef)
|
||||
return (
|
||||
<div className='absolute inset-0 flex items-center justify-center rounded-xl'
|
||||
style={{
|
||||
backgroundColor: 'rgba(35, 56, 118, 0.2)'
|
||||
backgroundColor: 'rgba(35, 56, 118, 0.2)',
|
||||
}}>
|
||||
<div
|
||||
ref={mainContentRef}
|
||||
className='w-[420px] rounded-xl bg-gray-50 p-6'
|
||||
style={{
|
||||
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)'
|
||||
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
|
||||
}}
|
||||
>
|
||||
<div className='flex items-start space-x-3'>
|
||||
|
|
@ -48,13 +49,13 @@ const ConfirmAddVar: FC<IConfirmAddVarProps> = ({
|
|||
className='shrink-0 flex items-center justify-center h-10 w-10 rounded-xl border border-gray-100'
|
||||
style={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)'
|
||||
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
|
||||
}}
|
||||
>{VarIcon}</div>
|
||||
<div className='grow-1'>
|
||||
<div className='text-sm font-medium text-gray-900'>{t('appDebug.autoAddVar')}</div>
|
||||
<div className='flex flex-wrap mt-[15px] max-h-[66px] overflow-y-auto px-1 space-x-1'>
|
||||
{varNameArr.map((name) => (
|
||||
{varNameArr.map(name => (
|
||||
<VarHighlight key={name} name={name} />
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import type { ConversationHistoriesRole } from '@/models/debug'
|
||||
import Button from '@/app/components/base/button'
|
||||
type Props = {
|
||||
isShow: boolean
|
||||
saveLoading: boolean
|
||||
data: ConversationHistoriesRole
|
||||
onClose: () => void
|
||||
onSave: (data: any) => void
|
||||
}
|
||||
|
||||
const EditModal: FC<Props> = ({
|
||||
isShow,
|
||||
saveLoading,
|
||||
data,
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [tempData, setTempData] = useState(data)
|
||||
return (
|
||||
<Modal
|
||||
title={t('appDebug.feature.conversationHistory.editModal.title')}
|
||||
isShow={isShow}
|
||||
onClose={onClose}
|
||||
wrapperClassName='!z-[101]'
|
||||
>
|
||||
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
|
||||
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
|
||||
value={tempData.user_prefix}
|
||||
onChange={e => setTempData({
|
||||
...tempData,
|
||||
user_prefix: e.target.value,
|
||||
})}
|
||||
/>
|
||||
|
||||
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
|
||||
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
|
||||
value={tempData.assistant_prefix}
|
||||
onChange={e => setTempData({
|
||||
...tempData,
|
||||
assistant_prefix: e.target.value,
|
||||
})}
|
||||
placeholder={t('common.chat.conversationNamePlaceholder') || ''}
|
||||
/>
|
||||
|
||||
<div className='mt-10 flex justify-end'>
|
||||
<Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
|
||||
<Button type='primary' className='flex-shrink-0' onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(EditModal)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
||||
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
||||
import { MessageClockCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
|
||||
type Props = {
|
||||
showWarning: boolean
|
||||
onShowEditModal: () => void
|
||||
}
|
||||
|
||||
const HistoryPanel: FC<Props> = ({
|
||||
showWarning,
|
||||
onShowEditModal,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Panel
|
||||
className='mt-3'
|
||||
title={
|
||||
<div className='flex items-center gap-2'>
|
||||
<div>{t('appDebug.feature.conversationHistory.title')}</div>
|
||||
</div>
|
||||
}
|
||||
headerIcon={
|
||||
<div className='p-1 rounded-md bg-white shadow-xs'>
|
||||
<MessageClockCircle className='w-4 h-4 text-[#DD2590]' />
|
||||
</div>}
|
||||
headerRight={
|
||||
<div className='flex items-center'>
|
||||
<div className='text-xs text-gray-500'>{t('appDebug.feature.conversationHistory.description')}</div>
|
||||
<div className='ml-3 w-[1px] h-[14px] bg-gray-200'></div>
|
||||
<OperationBtn type="edit" onClick={onShowEditModal} />
|
||||
</div>
|
||||
}
|
||||
noBodySpacing
|
||||
>
|
||||
{showWarning && (
|
||||
<div className='flex justify-between py-2 px-3 rounded-b-xl bg-[#FFFAEB] text-xs text-gray-700'>
|
||||
{/* <div>{t('appDebug.feature.conversationHistory.tip')} <a href="https://docs.dify.ai/getting-started/readme" target='_blank' className='text-[#155EEF]'>{t('appDebug.feature.conversationHistory.learnMore')}</a></div> */}
|
||||
<div>{t('appDebug.feature.conversationHistory.tip')}</div>
|
||||
</div>
|
||||
)}
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
export default React.memo(HistoryPanel)
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import produce from 'immer'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import cn from 'classnames'
|
||||
import ConfirmAddVar from './confirm-add-var'
|
||||
import s from './style.module.css'
|
||||
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 SimplePromptInput from './simple-prompt-input'
|
||||
import AdvancedMessageInput from '@/app/components/app/configuration/config-prompt/advanced-prompt-input'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
import type { PromptItem, PromptVariable } from '@/models/debug'
|
||||
import { type AppType, ModelModeType } from '@/types/app'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { MAX_PROMPT_MESSAGE_LENGTH } from '@/config'
|
||||
export type IPromptProps = {
|
||||
mode: AppType
|
||||
promptTemplate: string
|
||||
promptVariables: PromptVariable[]
|
||||
readonly?: boolean
|
||||
onChange?: (promp: string, promptVariables: PromptVariable[]) => void
|
||||
onChange?: (prompt: string, promptVariables: PromptVariable[]) => void
|
||||
}
|
||||
|
||||
const Prompt: FC<IPromptProps> = ({
|
||||
|
|
@ -28,73 +28,114 @@ const Prompt: FC<IPromptProps> = ({
|
|||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const promptVariablesObj = (() => {
|
||||
const obj: Record<string, boolean> = {}
|
||||
promptVariables.forEach((item) => {
|
||||
obj[item.key] = true
|
||||
|
||||
const {
|
||||
isAdvancedMode,
|
||||
currentAdvancedPrompt,
|
||||
setCurrentAdvancedPrompt,
|
||||
modelModeType,
|
||||
} = useContext(ConfigContext)
|
||||
|
||||
const handleMessageTypeChange = (index: number, role: PromptRole) => {
|
||||
const newPrompt = produce(currentAdvancedPrompt as PromptItem[], (draft) => {
|
||||
draft[index].role = role
|
||||
})
|
||||
return obj
|
||||
})()
|
||||
|
||||
const [newPromptVariables, setNewPromptVariables] = React.useState<PromptVariable[]>(promptVariables)
|
||||
const [newTemplates, setNewTemplates] = React.useState('')
|
||||
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
|
||||
|
||||
const handleChange = (newTemplates: string, keys: string[]) => {
|
||||
// const hasRemovedKeysInput = promptVariables.filter(input => keys.includes(input.key))
|
||||
const newPromptVariables = keys.filter(key => !(key in promptVariablesObj)).map(key => getNewVar(key))
|
||||
if (newPromptVariables.length > 0) {
|
||||
setNewPromptVariables(newPromptVariables)
|
||||
setNewTemplates(newTemplates)
|
||||
showConfirmAddVar()
|
||||
return
|
||||
}
|
||||
onChange?.(newTemplates, [])
|
||||
setCurrentAdvancedPrompt(newPrompt)
|
||||
}
|
||||
|
||||
const handleAutoAdd = (isAdd: boolean) => {
|
||||
return () => {
|
||||
onChange?.(newTemplates, isAdd ? newPromptVariables : [])
|
||||
hideConfirmAddVar()
|
||||
const handleValueChange = (value: string, index?: number) => {
|
||||
if (modelModeType === ModelModeType.chat) {
|
||||
const newPrompt = produce(currentAdvancedPrompt as PromptItem[], (draft) => {
|
||||
draft[index as number].text = value
|
||||
})
|
||||
setCurrentAdvancedPrompt(newPrompt, true)
|
||||
}
|
||||
else {
|
||||
const prompt = currentAdvancedPrompt as PromptItem
|
||||
setCurrentAdvancedPrompt({
|
||||
...prompt,
|
||||
text: value,
|
||||
}, true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddMessage = () => {
|
||||
const currentAdvancedPromptList = currentAdvancedPrompt as PromptItem[]
|
||||
if (currentAdvancedPromptList.length === 0) {
|
||||
setCurrentAdvancedPrompt([{
|
||||
role: PromptRole.system,
|
||||
text: '',
|
||||
}])
|
||||
return
|
||||
}
|
||||
const lastMessageType = currentAdvancedPromptList[currentAdvancedPromptList.length - 1].role
|
||||
const appendMessage = {
|
||||
role: lastMessageType === PromptRole.user ? PromptRole.assistant : PromptRole.user,
|
||||
text: '',
|
||||
}
|
||||
setCurrentAdvancedPrompt([...currentAdvancedPromptList, appendMessage])
|
||||
}
|
||||
|
||||
const handlePromptDelete = (index: number) => {
|
||||
const currentAdvancedPromptList = currentAdvancedPrompt as PromptItem[]
|
||||
const newPrompt = produce(currentAdvancedPromptList, (draft) => {
|
||||
draft.splice(index, 1)
|
||||
})
|
||||
setCurrentAdvancedPrompt(newPrompt)
|
||||
}
|
||||
|
||||
if (!isAdvancedMode) {
|
||||
return (
|
||||
<SimplePromptInput
|
||||
mode={mode}
|
||||
promptTemplate={promptTemplate}
|
||||
promptVariables={promptVariables}
|
||||
readonly={readonly}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(!readonly ? `${s.gradientBorder}` : 'bg-gray-50', 'relative rounded-xl')}>
|
||||
<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">
|
||||
<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>
|
||||
<div className="h2">{mode === AppType.chat ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
|
||||
{!readonly && (
|
||||
<Tooltip
|
||||
htmlContent={<div className='w-[180px]'>
|
||||
{t('appDebug.promptTip')}
|
||||
</div>}
|
||||
selector='config-prompt-tooltip'>
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/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" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className='space-y-3'>
|
||||
{modelModeType === ModelModeType.chat
|
||||
? (
|
||||
(currentAdvancedPrompt as PromptItem[]).map((item, index) => (
|
||||
<AdvancedMessageInput
|
||||
key={index}
|
||||
isChatMode
|
||||
type={item.role as PromptRole}
|
||||
value={item.text}
|
||||
onTypeChange={type => handleMessageTypeChange(index, type)}
|
||||
canDelete={(currentAdvancedPrompt as PromptItem[]).length > 1}
|
||||
onDelete={() => handlePromptDelete(index)}
|
||||
onChange={value => handleValueChange(value, index)}
|
||||
promptVariables={promptVariables}
|
||||
/>
|
||||
))
|
||||
)
|
||||
: (
|
||||
<AdvancedMessageInput
|
||||
type={(currentAdvancedPrompt as PromptItem).role as PromptRole}
|
||||
isChatMode={false}
|
||||
value={(currentAdvancedPrompt as PromptItem).text}
|
||||
onTypeChange={type => handleMessageTypeChange(0, type)}
|
||||
canDelete={false}
|
||||
onDelete={() => handlePromptDelete(0)}
|
||||
onChange={value => handleValueChange(value)}
|
||||
promptVariables={promptVariables}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<BlockInput
|
||||
readonly={readonly}
|
||||
value={promptTemplate}
|
||||
onConfirm={(value: string, vars: string[]) => {
|
||||
handleChange(value, vars)
|
||||
}}
|
||||
/>
|
||||
|
||||
{isShowConfirmAddVar && (
|
||||
<ConfirmAddVar
|
||||
varNameArr={newPromptVariables.map(v => v.name)}
|
||||
onConfrim={handleAutoAdd(true)}
|
||||
onCancel={handleAutoAdd(false)}
|
||||
onHide={hideConfirmAddVar}
|
||||
/>
|
||||
{(modelModeType === ModelModeType.chat && (currentAdvancedPrompt as PromptItem[]).length < MAX_PROMPT_MESSAGE_LENGTH) && (
|
||||
<div
|
||||
onClick={handleAddMessage}
|
||||
className='mt-3 flex items-center h-8 justify-center bg-gray-50 rounded-lg cursor-pointer text-[13px] font-medium text-gray-700 space-x-2'>
|
||||
<Plus className='w-4 h-4' />
|
||||
<div>{t('appDebug.promptMode.operation.addMessage')}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useBoolean, useClickAway } from 'ahooks'
|
||||
import cn from 'classnames'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
import { ChevronSelectorVertical } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
type Props = {
|
||||
value: PromptRole
|
||||
onChange: (value: PromptRole) => void
|
||||
}
|
||||
|
||||
const allTypes = [PromptRole.system, PromptRole.user, PromptRole.assistant]
|
||||
const MessageTypeSelector: FC<Props> = ({
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
const [showOption, { setFalse: setHide, toggle: toggleShow }] = useBoolean(false)
|
||||
const ref = React.useRef(null)
|
||||
useClickAway(() => {
|
||||
setHide()
|
||||
}, ref)
|
||||
return (
|
||||
<div className='relative left-[-8px]' ref={ref}>
|
||||
<div
|
||||
onClick={toggleShow}
|
||||
className={cn(showOption && 'bg-indigo-100', 'flex items-center h-7 pl-1.5 pr-1 space-x-0.5 rounded-lg cursor-pointer text-indigo-800')}>
|
||||
<div className='text-sm font-semibold uppercase'>{value}</div>
|
||||
<ChevronSelectorVertical className='w-3 h-3 ' />
|
||||
</div>
|
||||
{showOption && (
|
||||
<div className='absolute z-10 top-[30px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white'>
|
||||
{allTypes.map(type => (
|
||||
<div
|
||||
key={type}
|
||||
onClick={() => {
|
||||
setHide()
|
||||
onChange(type)
|
||||
}}
|
||||
className='flex items-center h-9 min-w-[44px] px-3 rounded-lg cursor-pointer text-sm font-medium text-gray-700 uppercase hover:bg-gray-50'
|
||||
>{type}</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(MessageTypeSelector)
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
'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 produce from 'immer'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import ConfirmAddVar from './confirm-add-var'
|
||||
import s from './style.module.css'
|
||||
import type { PromptVariable } from '@/models/debug'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { AppType } from '@/types/app'
|
||||
import { getNewVar, getVars } from '@/utils/var'
|
||||
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import AutomaticBtn from '@/app/components/app/configuration/config/automatic/automatic-btn'
|
||||
import type { AutomaticRes } from '@/service/debug'
|
||||
import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
|
||||
export type ISimplePromptInput = {
|
||||
mode: AppType
|
||||
promptTemplate: string
|
||||
promptVariables: PromptVariable[]
|
||||
readonly?: boolean
|
||||
onChange?: (promp: string, promptVariables: PromptVariable[]) => void
|
||||
}
|
||||
|
||||
const Prompt: FC<ISimplePromptInput> = ({
|
||||
mode,
|
||||
promptTemplate,
|
||||
promptVariables,
|
||||
readonly = false,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
modelConfig,
|
||||
dataSets,
|
||||
setModelConfig,
|
||||
setPrevPromptConfig,
|
||||
setIntroduction,
|
||||
hasSetBlockStatus,
|
||||
showSelectDataSet,
|
||||
} = useContext(ConfigContext)
|
||||
const promptVariablesObj = (() => {
|
||||
const obj: Record<string, boolean> = {}
|
||||
promptVariables.forEach((item) => {
|
||||
obj[item.key] = true
|
||||
})
|
||||
return obj
|
||||
})()
|
||||
|
||||
const [newPromptVariables, setNewPromptVariables] = React.useState<PromptVariable[]>(promptVariables)
|
||||
const [newTemplates, setNewTemplates] = React.useState('')
|
||||
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
|
||||
|
||||
const handleChange = (newTemplates: string, keys: string[]) => {
|
||||
const newPromptVariables = keys.filter(key => !(key in promptVariablesObj)).map(key => getNewVar(key))
|
||||
if (newPromptVariables.length > 0) {
|
||||
setNewPromptVariables(newPromptVariables)
|
||||
setNewTemplates(newTemplates)
|
||||
showConfirmAddVar()
|
||||
return
|
||||
}
|
||||
onChange?.(newTemplates, [])
|
||||
}
|
||||
|
||||
const handleAutoAdd = (isAdd: boolean) => {
|
||||
return () => {
|
||||
onChange?.(newTemplates, isAdd ? newPromptVariables : [])
|
||||
hideConfirmAddVar()
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className={cn(!readonly ? `${s.gradientBorder}` : 'bg-gray-50', ' relative shadow-md')}>
|
||||
<div className='rounded-xl bg-[#EEF4FF]'>
|
||||
<div className="flex justify-between items-center h-11 px-3">
|
||||
<div className="flex items-center space-x-1">
|
||||
<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" />
|
||||
</svg>
|
||||
<div className='h2'>{mode === AppType.chat ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
|
||||
{!readonly && (
|
||||
<Tooltip
|
||||
htmlContent={<div className='w-[180px]'>
|
||||
{t('appDebug.promptTip')}
|
||||
</div>}
|
||||
selector='config-prompt-tooltip'>
|
||||
<HelpCircle className='w-[14px] h-[14px] text-indigo-400' />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<AutomaticBtn onClick={showAutomaticTrue}/>
|
||||
</div>
|
||||
<div className='px-4 py-2 min-h-[228px] max-h-[156px] overflow-y-auto bg-white rounded-xl text-sm text-gray-700'>
|
||||
<PromptEditor
|
||||
className='min-h-[210px]'
|
||||
value={promptTemplate}
|
||||
contextBlock={{
|
||||
selectable: !hasSetBlockStatus.context,
|
||||
datasets: dataSets.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
type: item.data_source_type,
|
||||
})),
|
||||
onAddContext: showSelectDataSet,
|
||||
}}
|
||||
variableBlock={{
|
||||
variables: modelConfig.configs.prompt_variables.map(item => ({
|
||||
name: item.name,
|
||||
value: item.key,
|
||||
})),
|
||||
}}
|
||||
historyBlock={{
|
||||
show: false,
|
||||
selectable: false,
|
||||
history: {
|
||||
user: '',
|
||||
assistant: '',
|
||||
},
|
||||
onEditRole: () => {},
|
||||
}}
|
||||
queryBlock={{
|
||||
show: false,
|
||||
selectable: !hasSetBlockStatus.query,
|
||||
}}
|
||||
onChange={(value) => {
|
||||
handleChange?.(value, [])
|
||||
}}
|
||||
onBlur={() => {
|
||||
handleChange(promptTemplate, getVars(promptTemplate))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isShowConfirmAddVar && (
|
||||
<ConfirmAddVar
|
||||
varNameArr={newPromptVariables.map(v => v.name)}
|
||||
onConfrim={handleAutoAdd(true)}
|
||||
onCancel={handleAutoAdd(false)}
|
||||
onHide={hideConfirmAddVar}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showAutomatic && (
|
||||
<GetAutomaticResModal
|
||||
mode={mode as AppType}
|
||||
isShow={showAutomatic}
|
||||
onClose={showAutomaticFalse}
|
||||
onFinished={handleAutomaticRes}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Prompt)
|
||||
|
|
@ -12,4 +12,12 @@
|
|||
border-radius: 12px;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.optionWrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.boxHeader:hover .optionWrap {
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -73,7 +73,6 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
|
|||
delete newItem.max_length
|
||||
delete newItem.options
|
||||
}
|
||||
console.log(newItem)
|
||||
return newItem
|
||||
}
|
||||
|
||||
|
|
@ -175,8 +174,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
|
|||
<Tooltip htmlContent={<div className='w-[180px]'>
|
||||
{t('appDebug.variableTip')}
|
||||
</div>} selector='config-var-tooltip'>
|
||||
<HelpCircle className='w-3.5 h-3.5 text-gray-400'/>
|
||||
|
||||
<HelpCircle className='w-[14px] h-[14px] text-gray-400' />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
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
|
||||
|
|
@ -22,12 +21,12 @@ const AutomaticBtn: FC<IAutomaticBtnProps> = ({
|
|||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button className='flex space-x-2 items-center !h-8'
|
||||
<div className='flex px-3 space-x-2 items-center !h-8 cursor-pointer'
|
||||
onClick={onClick}
|
||||
>
|
||||
{leftIcon}
|
||||
<span className='text-xs font-semibold text-primary-600 uppercase'>{t('appDebug.operation.automatic')}</span>
|
||||
</Button>
|
||||
<span className='text-xs font-semibold text-indigo-600 uppercase'>{t('appDebug.operation.automatic')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(AutomaticBtn)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,39 @@
|
|||
'use client'
|
||||
import React, { FC } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PlusIcon } from '@heroicons/react/24/solid'
|
||||
|
||||
export interface IAddFeatureBtnProps {
|
||||
export type IAddFeatureBtnProps = {
|
||||
toBottomHeight: number
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const ITEM_HEIGHT = 48
|
||||
|
||||
const AddFeatureBtn: FC<IAddFeatureBtnProps> = ({
|
||||
onClick
|
||||
toBottomHeight,
|
||||
onClick,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div
|
||||
className='
|
||||
flex items-center h-8 space-x-2 px-3
|
||||
border border-primary-100 rounded-lg bg-primary-25 hover:bg-primary-50 cursor-pointer
|
||||
text-xs font-semibold text-primary-600 uppercase
|
||||
'
|
||||
className='absolute z-[9] left-0 right-0 flex justify-center pb-4'
|
||||
style={{
|
||||
boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)',
|
||||
top: toBottomHeight - ITEM_HEIGHT,
|
||||
background: 'linear-gradient(180deg, rgba(255, 255, 255, 0.00) 0%, #FFF 100%)',
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className='w-4 h-4 font-semibold' />
|
||||
<div>{t('appDebug.operation.addFeature')}</div>
|
||||
<div
|
||||
className='flex items-center h-8 space-x-2 px-3
|
||||
border border-primary-100 rounded-lg bg-primary-25 hover:bg-primary-50 cursor-pointer
|
||||
text-xs font-semibold text-primary-600 uppercase
|
||||
'
|
||||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className='w-4 h-4 font-semibold' />
|
||||
<div>{t('appDebug.operation.addFeature')}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,32 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useRef } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import produce from 'immer'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useBoolean, useScroll } from 'ahooks'
|
||||
import DatasetConfig from '../dataset-config'
|
||||
import ChatGroup from '../features/chat-group'
|
||||
import ExperienceEnchanceGroup from '../features/experience-enchance-group'
|
||||
import Toolbox from '../toolbox'
|
||||
import HistoryPanel from '../config-prompt/conversation-histroy/history-panel'
|
||||
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 AdvancedModeWaring from '@/app/components/app/configuration/prompt-mode/advanced-mode-waring'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
|
||||
import ConfigVar from '@/app/components/app/configuration/config-var'
|
||||
import type { PromptVariable } from '@/models/debug'
|
||||
import { AppType } from '@/types/app'
|
||||
import { AppType, ModelModeType } from '@/types/app'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
const Config: FC = () => {
|
||||
const {
|
||||
mode,
|
||||
isAdvancedMode,
|
||||
modelModeType,
|
||||
canReturnToSimpleMode,
|
||||
hasSetBlockStatus,
|
||||
showHistoryModal,
|
||||
introduction,
|
||||
setIntroduction,
|
||||
modelConfig,
|
||||
|
|
@ -44,6 +47,7 @@ const Config: FC = () => {
|
|||
|
||||
const promptTemplate = modelConfig.configs.prompt_template
|
||||
const promptVariables = modelConfig.configs.prompt_variables
|
||||
// simple mode
|
||||
const handlePromptChange = (newTemplate: string, newVariables: PromptVariable[]) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
draft.configs.prompt_template = newTemplate
|
||||
|
|
@ -101,26 +105,29 @@ const Config: FC = () => {
|
|||
const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && !!speech2textDefaultModel) || featureConfig.citation)
|
||||
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()
|
||||
}
|
||||
const wrapRef = useRef<HTMLDivElement>(null)
|
||||
const wrapScroll = useScroll(wrapRef)
|
||||
const toBottomHeight = (() => {
|
||||
if (!wrapRef.current)
|
||||
return 999
|
||||
const elem = wrapRef.current
|
||||
const { clientHeight } = elem
|
||||
const value = (wrapScroll?.top || 0) + clientHeight
|
||||
return value
|
||||
})()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pb-[20px]">
|
||||
<div className='flex justify-between items-center mb-4'>
|
||||
<AddFeatureBtn onClick={showChooseFeatureTrue} />
|
||||
<AutomaticBtn onClick={showAutomaticTrue}/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={wrapRef}
|
||||
className="relative px-6 pb-[50px] overflow-y-auto h-full"
|
||||
>
|
||||
<AddFeatureBtn toBottomHeight={toBottomHeight} onClick={showChooseFeatureTrue} />
|
||||
{
|
||||
(isAdvancedMode && canReturnToSimpleMode) && (
|
||||
<AdvancedModeWaring />
|
||||
)
|
||||
}
|
||||
{showChooseFeature && (
|
||||
<ChooseFeature
|
||||
isShow={showChooseFeature}
|
||||
|
|
@ -131,14 +138,7 @@ const Config: FC = () => {
|
|||
showSpeechToTextItem={!!speech2textDefaultModel}
|
||||
/>
|
||||
)}
|
||||
{showAutomatic && (
|
||||
<GetAutomaticResModal
|
||||
mode={mode as AppType}
|
||||
isShow={showAutomatic}
|
||||
onClose={showAutomaticFalse}
|
||||
onFinished={handleAutomaticRes}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Template */}
|
||||
<ConfigPrompt
|
||||
mode={mode as AppType}
|
||||
|
|
@ -156,6 +156,14 @@ const Config: FC = () => {
|
|||
{/* Dataset */}
|
||||
<DatasetConfig />
|
||||
|
||||
{/* Chat History */}
|
||||
{isAdvancedMode && isChatApp && modelModeType === ModelModeType.completion && (
|
||||
<HistoryPanel
|
||||
showWarning={!hasSetBlockStatus.history}
|
||||
onShowEditModal={showHistoryModal}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ChatConifig */}
|
||||
{
|
||||
hasChatConfig && (
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue