mirror of https://github.com/langgenius/dify.git
new style of user inputs
This commit is contained in:
parent
4554ac3ef8
commit
0eb442f954
|
|
@ -0,0 +1,109 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Select from '@/app/components/base/select'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
||||
import type { Inputs } from '@/models/debug'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
inputs: Inputs
|
||||
}
|
||||
|
||||
const ChatUserInput = ({
|
||||
inputs,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { modelConfig, setInputs } = useContext(ConfigContext)
|
||||
|
||||
const promptVariables = modelConfig.configs.prompt_variables.filter(({ key, name }) => {
|
||||
return key && key?.trim() && name && name?.trim()
|
||||
})
|
||||
|
||||
const promptVariableObj = (() => {
|
||||
const obj: Record<string, boolean> = {}
|
||||
promptVariables.forEach((input) => {
|
||||
obj[input.key] = true
|
||||
})
|
||||
return obj
|
||||
})()
|
||||
|
||||
const handleInputValueChange = (key: string, value: string) => {
|
||||
if (!(key in promptVariableObj))
|
||||
return
|
||||
|
||||
const newInputs = { ...inputs }
|
||||
promptVariables.forEach((input) => {
|
||||
if (input.key === key)
|
||||
newInputs[key] = value
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}
|
||||
|
||||
if (!promptVariables.length)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn('bg-components-panel-on-panel-item-bg rounded-xl border-[0.5px] border-components-panel-border-subtle shadow-xs z-[1]')}>
|
||||
<div className='px-4 pt-3 pb-4'>
|
||||
{promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
|
||||
<div
|
||||
key={key}
|
||||
className='mb-4 last-of-type:mb-0'
|
||||
>
|
||||
<div>
|
||||
<div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
|
||||
<div className='truncate'>{name || key}</div>
|
||||
{!required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
|
||||
</div>
|
||||
<div className='grow'>
|
||||
{type === 'string' && (
|
||||
<Input
|
||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
||||
placeholder={name}
|
||||
autoFocus={index === 0}
|
||||
maxLength={max_length || DEFAULT_VALUE_MAX_LEN}
|
||||
/>
|
||||
)}
|
||||
{type === 'paragraph' && (
|
||||
<Textarea
|
||||
className='grow h-[120px]'
|
||||
placeholder={name}
|
||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
||||
/>
|
||||
)}
|
||||
{type === 'select' && (
|
||||
<Select
|
||||
className='w-full'
|
||||
defaultValue={inputs[key] as string}
|
||||
onSelect={(i) => { handleInputValueChange(key, i.value as string) }}
|
||||
items={(options || []).map(i => ({ name: i, value: i }))}
|
||||
allowSearch={false}
|
||||
bgClassName='bg-gray-50'
|
||||
/>
|
||||
)}
|
||||
{type === 'number' && (
|
||||
<Input
|
||||
type='number'
|
||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
||||
placeholder={name}
|
||||
autoFocus={index === 0}
|
||||
maxLength={max_length || DEFAULT_VALUE_MAX_LEN}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChatUserInput
|
||||
|
|
@ -119,8 +119,8 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
|
|||
config={config}
|
||||
chatList={chatList}
|
||||
isResponding={isResponding}
|
||||
chatContainerClassName='p-6'
|
||||
chatFooterClassName='px-6 pt-10 pb-4'
|
||||
chatContainerClassName='px-3 pt-6'
|
||||
chatFooterClassName='px-3 pt-10 pb-2'
|
||||
suggestedQuestions={suggestedQuestions}
|
||||
onSend={doSend}
|
||||
onStopResponding={handleStop}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { setAutoFreeze } from 'immer'
|
|||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
RiAddLine,
|
||||
RiEqualizer2Line,
|
||||
} from '@remixicon/react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
|
|
@ -23,11 +24,15 @@ import {
|
|||
APP_CHAT_WITH_MULTIPLE_MODEL_RESTART,
|
||||
} from './types'
|
||||
import { AppType, ModelModeType, TransferMethod } from '@/types/app'
|
||||
import ChatUserInput from '@/app/components/app/configuration/debug/chat-user-input'
|
||||
import PromptValuePanel from '@/app/components/app/configuration/prompt-value-panel'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { sendCompletionMessage } from '@/service/debug'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
|
||||
import type { ModelConfig as BackendModelConfig, VisionFile } from '@/types/app'
|
||||
import { promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import TextGeneration from '@/app/components/app/text-generate/item'
|
||||
|
|
@ -391,49 +396,71 @@ const Debug: FC<IDebug> = ({
|
|||
adjustModalWidth()
|
||||
}, [])
|
||||
|
||||
const [expanded, setExpanded] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="shrink-0 pt-4 px-6">
|
||||
<div className='flex items-center justify-between mb-2'>
|
||||
<div className='h2 '>{t('appDebug.inputs.title')}</div>
|
||||
<div className="shrink-0">
|
||||
<div className='flex items-center justify-between px-4 pt-3 pb-2'>
|
||||
<div className='text-text-primary system-xl-semibold'>{t('appDebug.inputs.title')}</div>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
debugWithMultipleModel
|
||||
? (
|
||||
<>
|
||||
<Button
|
||||
variant='secondary-accent'
|
||||
variant='ghost-accent'
|
||||
onClick={() => onMultipleModelConfigsChange(true, [...multipleModelConfigs, { id: `${Date.now()}`, model: '', provider: '', parameters: {} }])}
|
||||
disabled={multipleModelConfigs.length >= 4}
|
||||
>
|
||||
<RiAddLine className='mr-1 w-3.5 h-3.5' />
|
||||
{t('common.modelProvider.addModel')}({multipleModelConfigs.length}/4)
|
||||
</Button>
|
||||
<div className='mx-2 w-[1px] h-[14px] bg-gray-200' />
|
||||
<div className='mx-2 w-[1px] h-[14px] bg-divider-regular' />
|
||||
</>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{mode !== AppType.completion && (
|
||||
<Button variant='secondary-accent' className='gap-1' onClick={clearConversation}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66663 2.66629V5.99963H3.05463M3.05463 5.99963C3.49719 4.90505 4.29041 3.98823 5.30998 3.39287C6.32954 2.7975 7.51783 2.55724 8.68861 2.70972C9.85938 2.8622 10.9465 3.39882 11.7795 4.23548C12.6126 5.07213 13.1445 6.16154 13.292 7.33296M3.05463 5.99963H5.99996M13.3333 13.333V9.99963H12.946M12.946 9.99963C12.5028 11.0936 11.7093 12.0097 10.6898 12.6045C9.67038 13.1993 8.48245 13.4393 7.31203 13.2869C6.1416 13.1344 5.05476 12.5982 4.22165 11.7621C3.38854 10.926 2.8562 9.83726 2.70796 8.66629M12.946 9.99963H9.99996" stroke="#1C64F2" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
<span className='text-primary-600 text-[13px] font-semibold'>{t('common.operation.refresh')}</span>
|
||||
</Button>
|
||||
<>
|
||||
<TooltipPlus
|
||||
popupContent={t('common.operation.refresh')}
|
||||
>
|
||||
<ActionButton onClick={clearConversation}>
|
||||
<RefreshCcw01 className='w-4 h-4' />
|
||||
</ActionButton>
|
||||
</TooltipPlus>
|
||||
<div className='relative ml-1 mr-2'>
|
||||
<TooltipPlus
|
||||
popupContent={t('workflow.panel.userInputField')}
|
||||
>
|
||||
<ActionButton state={expanded ? ActionButtonState.Active : undefined} onClick={() => setExpanded(!expanded)}>
|
||||
<RiEqualizer2Line className='w-4 h-4' />
|
||||
</ActionButton>
|
||||
</TooltipPlus>
|
||||
{expanded && <div className='absolute z-10 bottom-[-18px] right-[5px] w-3 h-3 bg-components-panel-on-panel-item-bg border-l-[0.5px] border-t-[0.5px] border-components-panel-border-subtle rotate-45'/>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<PromptValuePanel
|
||||
appType={mode as AppType}
|
||||
onSend={handleSendTextCompletion}
|
||||
inputs={inputs}
|
||||
visionConfig={{
|
||||
...visionConfig,
|
||||
image_file_size_limit: fileUploadConfigResponse?.image_file_size_limit,
|
||||
}}
|
||||
onVisionFilesChange={setCompletionFiles}
|
||||
/>
|
||||
{mode !== AppType.completion && expanded && (
|
||||
<div className='mx-3 mt-1'>
|
||||
<ChatUserInput inputs={inputs} />
|
||||
</div>
|
||||
)}
|
||||
{mode === AppType.completion && (
|
||||
<PromptValuePanel
|
||||
appType={mode as AppType}
|
||||
onSend={handleSendTextCompletion}
|
||||
inputs={inputs}
|
||||
visionConfig={{
|
||||
...visionConfig,
|
||||
image_file_size_limit: fileUploadConfigResponse?.image_file_size_limit,
|
||||
}}
|
||||
onVisionFilesChange={setCompletionFiles}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
debugWithMultipleModel && (
|
||||
|
|
@ -481,7 +508,7 @@ const Debug: FC<IDebug> = ({
|
|||
)}
|
||||
{/* Text Generation */}
|
||||
{mode === AppType.completion && (
|
||||
<div className="mt-6 px-6 pb-4">
|
||||
<div className="mt-6 px-3 pb-4">
|
||||
<GroupName name={t('appDebug.result')} />
|
||||
{(completionRes || isResponding) && (
|
||||
<TextGeneration
|
||||
|
|
|
|||
|
|
@ -858,7 +858,7 @@ const Configuration: FC = () => {
|
|||
<Config />
|
||||
</div>
|
||||
{!isMobile && <div className="relative flex flex-col w-1/2 h-full overflow-y-auto grow " style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||
<div className='flex flex-col h-0 border-t border-l grow rounded-tl-2xl bg-gray-50 '>
|
||||
<div className='grow flex flex-col h-0 border-t-[0.5px] border-l-[0.5px] rounded-tl-2xl border-components-panel-border bg-chatbot-bg '>
|
||||
<Debug
|
||||
isAPIKeySet={isAPIKeySet}
|
||||
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
|
|
|
|||
|
|
@ -222,18 +222,6 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
|||
|
||||
export default React.memo(PromptValuePanel)
|
||||
|
||||
function replaceStringWithValuesWithFormat(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
|
||||
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
||||
const name = inputs[key]
|
||||
if (name) { // has set value
|
||||
return `<div class='inline-block px-1 rounded-md text-gray-900' style='background: rgba(16, 24, 40, 0.1)'>${name}</div>`
|
||||
}
|
||||
|
||||
const valueObj: PromptVariable | undefined = promptVariables.find(v => v.key === key)
|
||||
return `<div class='inline-block px-1 rounded-md text-gray-500' style='background: rgba(16, 24, 40, 0.05)'>${valueObj ? valueObj.name : match}</div>`
|
||||
})
|
||||
}
|
||||
|
||||
export function replaceStringWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
|
||||
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
||||
const name = inputs[key]
|
||||
|
|
@ -245,8 +233,3 @@ export function replaceStringWithValues(str: string, promptVariables: PromptVari
|
|||
return valueObj ? `{{${valueObj.name}}}` : match
|
||||
})
|
||||
}
|
||||
|
||||
// \n -> br
|
||||
function format(str: string) {
|
||||
return str.replaceAll('\n', '<br>')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ const AddButton: FC<Props> = ({
|
|||
onClick,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(className, 'p-1 rounded-md cursor-pointer hover:bg-gray-200 select-none')} onClick={onClick}>
|
||||
<RiAddLine className='w-4 h-4 text-gray-500' />
|
||||
<div className={cn(className, 'p-1 rounded-md cursor-pointer hover:bg-state-base-hover select-none')} onClick={onClick}>
|
||||
<RiAddLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ const FormItem: FC<Props> = ({
|
|||
const isContext = type === InputVarType.contexts
|
||||
const isIterator = type === InputVarType.iterator
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
<div className={cn(className)}>
|
||||
{!isArrayLikeType && (
|
||||
<div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
|
||||
<div className='truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>
|
||||
|
|
@ -112,8 +112,7 @@ const FormItem: FC<Props> = ({
|
|||
|
||||
{
|
||||
type === InputVarType.number && (
|
||||
<input
|
||||
className="w-full px-3 text-sm leading-8 text-gray-900 border-0 rounded-lg grow h-8 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
|
||||
<Input
|
||||
type="number"
|
||||
value={value || ''}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
|
|
@ -157,6 +156,7 @@ const FormItem: FC<Props> = ({
|
|||
)
|
||||
}
|
||||
|
||||
{/* #TODO# file type new form */}
|
||||
{
|
||||
type === InputVarType.files && (
|
||||
<TextGenerationImageUploader
|
||||
|
|
@ -186,7 +186,7 @@ const FormItem: FC<Props> = ({
|
|||
(value as any).length > 1
|
||||
? (<RiDeleteBinLine
|
||||
onClick={handleArrayItemRemove(index)}
|
||||
className='mr-1 w-3.5 h-3.5 text-gray-500 cursor-pointer'
|
||||
className='mr-1 w-3.5 h-3.5 text-text-tertiary cursor-pointer'
|
||||
/>)
|
||||
: undefined
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ const FormItem: FC<Props> = ({
|
|||
(value as any).length > 1
|
||||
? (<RiDeleteBinLine
|
||||
onClick={handleArrayItemRemove(index)}
|
||||
className='mr-1 w-3.5 h-3.5 text-gray-500 cursor-pointer'
|
||||
className='mr-1 w-3.5 h-3.5 text-text-tertiary cursor-pointer'
|
||||
/>)
|
||||
: undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const Form: FC<Props> = ({
|
|||
<div className={cn(className, 'space-y-2')}>
|
||||
{label && (
|
||||
<div className='mb-1 flex items-center justify-between'>
|
||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500 uppercase'>{label}</div>
|
||||
<div className='flex items-center h-6 system-xs-medium-uppercase text-text-tertiary'>{label}</div>
|
||||
{isArrayLikeType && (
|
||||
<AddButton onClick={handleAddContext} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ const translation = {
|
|||
},
|
||||
},
|
||||
inputs: {
|
||||
title: 'Debug and Preview',
|
||||
title: 'Debug & Preview',
|
||||
noPrompt: 'Try write some prompt in pre-prompt input',
|
||||
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.',
|
||||
|
|
|
|||
Loading…
Reference in New Issue