mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 19:59:10 +08:00
completion debug & preview
This commit is contained in:
parent
65a6265ff6
commit
8c785e268b
@ -8,6 +8,7 @@ import { useBoolean } from 'ahooks'
|
|||||||
import {
|
import {
|
||||||
RiAddLine,
|
RiAddLine,
|
||||||
RiEqualizer2Line,
|
RiEqualizer2Line,
|
||||||
|
RiSparklingFill,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
@ -47,6 +48,7 @@ import { useProviderContext } from '@/context/provider-context'
|
|||||||
import AgentLogModal from '@/app/components/base/agent-log-modal'
|
import AgentLogModal from '@/app/components/base/agent-log-modal'
|
||||||
import PromptLogModal from '@/app/components/base/prompt-log-modal'
|
import PromptLogModal from '@/app/components/base/prompt-log-modal'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||||
|
|
||||||
type IDebug = {
|
type IDebug = {
|
||||||
isAPIKeySet: boolean
|
isAPIKeySet: boolean
|
||||||
@ -82,8 +84,8 @@ const Debug: FC<IDebug> = ({
|
|||||||
speechToTextConfig,
|
speechToTextConfig,
|
||||||
textToSpeechConfig,
|
textToSpeechConfig,
|
||||||
citationConfig,
|
citationConfig,
|
||||||
moderationConfig,
|
// moderationConfig,
|
||||||
moreLikeThisConfig,
|
// moreLikeThisConfig,
|
||||||
formattingChanged,
|
formattingChanged,
|
||||||
setFormattingChanged,
|
setFormattingChanged,
|
||||||
dataSets,
|
dataSets,
|
||||||
@ -200,6 +202,7 @@ const Debug: FC<IDebug> = ({
|
|||||||
|
|
||||||
const [completionRes, setCompletionRes] = useState('')
|
const [completionRes, setCompletionRes] = useState('')
|
||||||
const [messageId, setMessageId] = useState<string | null>(null)
|
const [messageId, setMessageId] = useState<string | null>(null)
|
||||||
|
const features = useFeatures(s => s.features)
|
||||||
|
|
||||||
const sendTextCompletion = async () => {
|
const sendTextCompletion = async () => {
|
||||||
if (isResponding) {
|
if (isResponding) {
|
||||||
@ -230,36 +233,33 @@ const Debug: FC<IDebug> = ({
|
|||||||
completion_prompt_config: {},
|
completion_prompt_config: {},
|
||||||
user_input_form: promptVariablesToUserInputsForm(modelConfig.configs.prompt_variables),
|
user_input_form: promptVariablesToUserInputsForm(modelConfig.configs.prompt_variables),
|
||||||
dataset_query_variable: contextVar || '',
|
dataset_query_variable: contextVar || '',
|
||||||
opening_statement: introduction,
|
|
||||||
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
|
|
||||||
speech_to_text: speechToTextConfig,
|
|
||||||
retriever_resource: citationConfig,
|
|
||||||
sensitive_word_avoidance: moderationConfig,
|
|
||||||
more_like_this: moreLikeThisConfig,
|
|
||||||
model: {
|
|
||||||
provider: modelConfig.provider,
|
|
||||||
name: modelConfig.model_id,
|
|
||||||
mode: modelConfig.mode,
|
|
||||||
completion_params: completionParams as any,
|
|
||||||
},
|
|
||||||
text_to_speech: {
|
|
||||||
enabled: false,
|
|
||||||
voice: '',
|
|
||||||
language: '',
|
|
||||||
},
|
|
||||||
agent_mode: {
|
|
||||||
enabled: false,
|
|
||||||
tools: [],
|
|
||||||
},
|
|
||||||
dataset_configs: {
|
dataset_configs: {
|
||||||
...datasetConfigs,
|
...datasetConfigs,
|
||||||
datasets: {
|
datasets: {
|
||||||
datasets: [...postDatasets],
|
datasets: [...postDatasets],
|
||||||
} as any,
|
} as any,
|
||||||
},
|
},
|
||||||
|
agent_mode: {
|
||||||
|
enabled: false,
|
||||||
|
tools: [],
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
provider: modelConfig.provider,
|
||||||
|
name: modelConfig.model_id,
|
||||||
|
mode: modelConfig.mode,
|
||||||
|
completion_params: completionParams as any,
|
||||||
|
},
|
||||||
|
more_like_this: features.moreLikeThis as any,
|
||||||
|
sensitive_word_avoidance: features.moderation as any,
|
||||||
|
text_to_speech: features.text2speech as any,
|
||||||
|
// ##TODO## file_upload
|
||||||
file_upload: {
|
file_upload: {
|
||||||
image: visionConfig,
|
image: visionConfig,
|
||||||
},
|
},
|
||||||
|
opening_statement: introduction,
|
||||||
|
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
|
||||||
|
speech_to_text: speechToTextConfig,
|
||||||
|
retriever_resource: citationConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdvancedMode) {
|
if (isAdvancedMode) {
|
||||||
@ -449,7 +449,6 @@ const Debug: FC<IDebug> = ({
|
|||||||
<ChatUserInput inputs={inputs} />
|
<ChatUserInput inputs={inputs} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* ##TODO## new style of completion */}
|
|
||||||
{mode === AppType.completion && (
|
{mode === AppType.completion && (
|
||||||
<PromptValuePanel
|
<PromptValuePanel
|
||||||
appType={mode as AppType}
|
appType={mode as AppType}
|
||||||
@ -509,26 +508,36 @@ const Debug: FC<IDebug> = ({
|
|||||||
)}
|
)}
|
||||||
{/* Text Generation */}
|
{/* Text Generation */}
|
||||||
{mode === AppType.completion && (
|
{mode === AppType.completion && (
|
||||||
<div className="mt-6 px-3 pb-4">
|
<>
|
||||||
<GroupName name={t('appDebug.result')} />
|
|
||||||
{(completionRes || isResponding) && (
|
{(completionRes || isResponding) && (
|
||||||
<TextGeneration
|
<>
|
||||||
className="mt-2"
|
<div className='mx-4 mt-3'><GroupName name={t('appDebug.result')} /></div>
|
||||||
content={completionRes}
|
<div className='mx-3 mb-8'>
|
||||||
isLoading={!completionRes && isResponding}
|
<TextGeneration
|
||||||
isShowTextToSpeech={textToSpeechConfig.enabled && !!text2speechDefaultModel}
|
className="mt-2"
|
||||||
isResponding={isResponding}
|
content={completionRes}
|
||||||
isInstalledApp={false}
|
isLoading={!completionRes && isResponding}
|
||||||
messageId={messageId}
|
isShowTextToSpeech={textToSpeechConfig.enabled && !!text2speechDefaultModel}
|
||||||
isError={false}
|
isResponding={isResponding}
|
||||||
onRetry={() => { }}
|
isInstalledApp={false}
|
||||||
supportAnnotation
|
messageId={messageId}
|
||||||
appId={appId}
|
isError={false}
|
||||||
varList={varList}
|
onRetry={() => { }}
|
||||||
siteInfo={null}
|
supportAnnotation
|
||||||
/>
|
appId={appId}
|
||||||
|
varList={varList}
|
||||||
|
siteInfo={null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
{!completionRes && !isResponding && (
|
||||||
|
<div className='grow flex flex-col items-center justify-center gap-2'>
|
||||||
|
<RiSparklingFill className='w-12 h-12 text-text-empty-state-icon' />
|
||||||
|
<div className='text-text-quaternary system-sm-regular'>{t('appDebug.noResult')}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{mode === AppType.completion && showPromptLogModal && (
|
{mode === AppType.completion && showPromptLogModal && (
|
||||||
<PromptLogModal
|
<PromptLogModal
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import {
|
import {
|
||||||
RiArrowDownSLine,
|
RiArrowDownSLine,
|
||||||
RiArrowRightLine,
|
RiArrowRightSLine,
|
||||||
|
RiPlayLargeFill,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import {
|
|
||||||
PlayIcon,
|
|
||||||
} from '@heroicons/react/24/solid'
|
|
||||||
import ConfigContext from '@/context/debug-configuration'
|
import ConfigContext from '@/context/debug-configuration'
|
||||||
import type { Inputs, PromptVariable } from '@/models/debug'
|
import type { Inputs } from '@/models/debug'
|
||||||
import { AppType, ModelModeType } from '@/types/app'
|
import { AppType, ModelModeType } from '@/types/app'
|
||||||
import Select from '@/app/components/base/select'
|
import Select from '@/app/components/base/select'
|
||||||
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
|
import Input from '@/app/components/base/input'
|
||||||
import Textarea from '@/app/components/base/textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader'
|
import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader'
|
||||||
|
import FeatureBar from '@/app/components/base/features/new-feature-panel/feature-bar'
|
||||||
import type { VisionFile, VisionSettings } from '@/types/app'
|
import type { VisionFile, VisionSettings } from '@/types/app'
|
||||||
|
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
||||||
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
export type IPromptValuePanelProps = {
|
export type IPromptValuePanelProps = {
|
||||||
appType: AppType
|
appType: AppType
|
||||||
@ -43,15 +45,15 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
|||||||
return key && key?.trim() && name && name?.trim()
|
return key && key?.trim() && name && name?.trim()
|
||||||
})
|
})
|
||||||
|
|
||||||
const promptVariableObj = (() => {
|
const promptVariableObj = useMemo(() => {
|
||||||
const obj: Record<string, boolean> = {}
|
const obj: Record<string, boolean> = {}
|
||||||
promptVariables.forEach((input) => {
|
promptVariables.forEach((input) => {
|
||||||
obj[input.key] = true
|
obj[input.key] = true
|
||||||
})
|
})
|
||||||
return obj
|
return obj
|
||||||
})()
|
}, [promptVariables])
|
||||||
|
|
||||||
const canNotRun = (() => {
|
const canNotRun = useMemo(() => {
|
||||||
if (mode !== AppType.completion)
|
if (mode !== AppType.completion)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
@ -62,19 +64,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
else { return !modelConfig.configs.prompt_template }
|
else { return !modelConfig.configs.prompt_template }
|
||||||
})()
|
}, [chatPromptConfig.prompt, completionPromptConfig.prompt?.text, isAdvancedMode, mode, modelConfig.configs.prompt_template, modelModeType])
|
||||||
const renderRunButton = () => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
disabled={canNotRun}
|
|
||||||
onClick={() => onSend && onSend()}
|
|
||||||
className="w-[80px] !h-8">
|
|
||||||
<PlayIcon className="shrink-0 w-4 h-4 mr-1" aria-hidden="true" />
|
|
||||||
<span className='uppercase text-[13px]'>{t('appDebug.inputs.run')}</span>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const handleInputValueChange = (key: string, value: string) => {
|
const handleInputValueChange = (key: string, value: string) => {
|
||||||
if (!(key in promptVariableObj))
|
if (!(key in promptVariableObj))
|
||||||
return
|
return
|
||||||
@ -95,142 +86,129 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
|||||||
setInputs(newInputs)
|
setInputs(newInputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-3 border border-gray-200 bg-white rounded-xl" style={{
|
<>
|
||||||
boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)',
|
<div className='relative z-[1] mx-3 border-[0.5px] bg-components-panel-on-panel-item-bg border-components-panel-border-subtle rounded-xl shadow-md'>
|
||||||
}}>
|
<div className={cn('px-4 pt-3', userInputFieldCollapse ? 'pb-3' : 'pb-1')}>
|
||||||
<div className={'mt-3 px-4 bg-white'}>
|
<div className='flex items-center gap-0.5 py-0.5 cursor-pointer' onClick={() => setUserInputFieldCollapse(!userInputFieldCollapse)}>
|
||||||
<div className={
|
<div className='text-text-secondary system-md-semibold-uppercase'>{t('appDebug.inputs.userInputField')}</div>
|
||||||
`${!userInputFieldCollapse && 'mb-2'}`
|
{userInputFieldCollapse && <RiArrowRightSLine className='w-4 h-4 text-text-secondary'/>}
|
||||||
}>
|
{!userInputFieldCollapse && <RiArrowDownSLine className='w-4 h-4 text-text-secondary'/>}
|
||||||
<div className='flex items-center space-x-1 cursor-pointer' onClick={() => setUserInputFieldCollapse(!userInputFieldCollapse)}>
|
|
||||||
{
|
|
||||||
userInputFieldCollapse
|
|
||||||
? <RiArrowRightLine className='w-3 h-3 text-gray-300' />
|
|
||||||
: <RiArrowDownSLine className='w-3 h-3 text-gray-300' />
|
|
||||||
}
|
|
||||||
<div className='text-xs font-medium text-gray-800 uppercase'>{t('appDebug.inputs.userInputField')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
{appType === AppType.completion && promptVariables.length > 0 && !userInputFieldCollapse && (
|
{!userInputFieldCollapse && (
|
||||||
<div className="mt-1 text-xs leading-normal text-gray-500">{t('appDebug.inputs.completionVarTip')}</div>
|
<div className='mt-1 text-text-tertiary system-xs-regular'>{t('appDebug.inputs.completionVarTip')}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!userInputFieldCollapse && (
|
{!userInputFieldCollapse && promptVariables.length > 0 && (
|
||||||
<>
|
<div className='px-4 pt-3 pb-4'>
|
||||||
{
|
{promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
|
||||||
promptVariables.length > 0
|
<div
|
||||||
? (
|
key={key}
|
||||||
<div className="space-y-3 ">
|
className='mb-4 last-of-type:mb-0'
|
||||||
{promptVariables.map(({ key, name, type, options, max_length, required }) => (
|
>
|
||||||
<div key={key} className="xl:flex justify-between">
|
<div>
|
||||||
<div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-gray-900">{name || key}</div>
|
<div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
|
||||||
{type === 'select' && (
|
<div className='truncate'>{name || key}</div>
|
||||||
<Select
|
{!required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
|
||||||
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 === 'string' && (
|
|
||||||
<input
|
|
||||||
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
|
|
||||||
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
|
||||||
type="text"
|
|
||||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
|
||||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
|
||||||
maxLength={max_length || DEFAULT_VALUE_MAX_LEN}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{type === 'paragraph' && (
|
|
||||||
<Textarea
|
|
||||||
className='grow h-[120px]'
|
|
||||||
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
|
||||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
|
||||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{type === 'number' && (
|
|
||||||
<input
|
|
||||||
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
|
|
||||||
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
|
||||||
type="number"
|
|
||||||
value={inputs[key] ? `${inputs[key]}` : ''}
|
|
||||||
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
: (
|
|
||||||
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
appType === AppType.completion && visionConfig?.enabled && (
|
|
||||||
<div className="mt-3 xl:flex justify-between">
|
|
||||||
<div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-gray-900">{t('common.imageUploader.imageUpload')}</div>
|
|
||||||
<div className='grow'>
|
<div className='grow'>
|
||||||
<TextGenerationImageUploader
|
{type === 'string' && (
|
||||||
settings={visionConfig}
|
<Input
|
||||||
onFilesChange={files => onVisionFilesChange(files.filter(file => file.progress !== -1).map(fileItem => ({
|
value={inputs[key] ? `${inputs[key]}` : ''}
|
||||||
type: 'image',
|
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
|
||||||
transfer_method: fileItem.type,
|
placeholder={name}
|
||||||
url: fileItem.url,
|
autoFocus={index === 0}
|
||||||
upload_file_id: fileItem.fileId,
|
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>
|
||||||
}
|
))}
|
||||||
</>
|
{/* ##TODO## file_upload */}
|
||||||
)
|
{visionConfig?.enabled && (
|
||||||
}
|
<div className="mt-3 xl:flex justify-between">
|
||||||
</div>
|
<div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-gray-900">{t('common.imageUploader.imageUpload')}</div>
|
||||||
|
<div className='grow'>
|
||||||
{
|
<TextGenerationImageUploader
|
||||||
appType === AppType.completion && (
|
settings={visionConfig}
|
||||||
<div>
|
onFilesChange={files => onVisionFilesChange(files.filter(file => file.progress !== -1).map(fileItem => ({
|
||||||
<div className="mt-5 border-b border-gray-100"></div>
|
type: 'image',
|
||||||
<div className="flex justify-between mt-4 px-4">
|
transfer_method: fileItem.type,
|
||||||
<Button
|
url: fileItem.url,
|
||||||
onClick={onClear}
|
upload_file_id: fileItem.fileId,
|
||||||
disabled={false}
|
})))}
|
||||||
>
|
/>
|
||||||
<span className='text-[13px]'>{t('common.operation.clear')}</span>
|
</div>
|
||||||
</Button>
|
</div>
|
||||||
|
)}
|
||||||
{canNotRun
|
|
||||||
? (<Tooltip
|
|
||||||
popupContent={t('appDebug.otherError.promptNoBeEmpty')}
|
|
||||||
needsDelay
|
|
||||||
>
|
|
||||||
{renderRunButton()}
|
|
||||||
</Tooltip>)
|
|
||||||
: renderRunButton()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
{!userInputFieldCollapse && (
|
||||||
</div>
|
<div className='flex justify-between p-4 pt-3 border-t border-divider-subtle'>
|
||||||
|
<Button className='w-[72px]' onClick={onClear}>{t('common.operation.clear')}</Button>
|
||||||
|
{canNotRun && (
|
||||||
|
<Tooltip popupContent={t('appDebug.otherError.promptNoBeEmpty')} needsDelay>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled={canNotRun}
|
||||||
|
onClick={() => onSend && onSend()}
|
||||||
|
className="w-[96px]">
|
||||||
|
<RiPlayLargeFill className="shrink-0 w-4 h-4 mr-0.5" aria-hidden="true" />
|
||||||
|
{t('appDebug.inputs.run')}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{!canNotRun && (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled={canNotRun}
|
||||||
|
onClick={() => onSend && onSend()}
|
||||||
|
className="w-[96px]">
|
||||||
|
<RiPlayLargeFill className="shrink-0 w-4 h-4 mr-0.5" aria-hidden="true" />
|
||||||
|
{t('appDebug.inputs.run')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='mx-3'>
|
||||||
|
<FeatureBar
|
||||||
|
isChatMode={appType !== AppType.completion}
|
||||||
|
onFeatureBarClick={setShowAppConfigureFeaturesModal} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(PromptValuePanel)
|
export default React.memo(PromptValuePanel)
|
||||||
|
|
||||||
export function replaceStringWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
|
|
||||||
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
|
||||||
const name = inputs[key]
|
|
||||||
if (name) { // has set value
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueObj: PromptVariable | undefined = promptVariables.find(v => v.key === key)
|
|
||||||
return valueObj ? `{{${valueObj.name}}}` : match
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import type { PromptVariable } from '@/models/debug'
|
||||||
|
|
||||||
|
export function replaceStringWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
|
||||||
|
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
||||||
|
const name = inputs[key]
|
||||||
|
if (name) { // has set value
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueObj: PromptVariable | undefined = promptVariables.find(v => v.key === key)
|
||||||
|
return valueObj ? `{{${valueObj.name}}}` : match
|
||||||
|
})
|
||||||
|
}
|
@ -18,7 +18,7 @@ import type {
|
|||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { ssePost } from '@/service/base'
|
import { ssePost } from '@/service/base'
|
||||||
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
|
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils'
|
||||||
import type { Annotation } from '@/models/log'
|
import type { Annotation } from '@/models/log'
|
||||||
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
|
@ -9,11 +9,13 @@ import { useFeatures } from '@/app/components/base/features/hooks'
|
|||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
isChatMode?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
onFeatureBarClick?: (state: boolean) => void
|
onFeatureBarClick?: (state: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureBar = ({
|
const FeatureBar = ({
|
||||||
|
isChatMode = true,
|
||||||
disabled,
|
disabled,
|
||||||
onFeatureBarClick,
|
onFeatureBarClick,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
@ -22,8 +24,13 @@ const FeatureBar = ({
|
|||||||
const [modalOpen, setModalOpen] = useState(false)
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
|
|
||||||
const noFeatureEnabled = useMemo(() => {
|
const noFeatureEnabled = useMemo(() => {
|
||||||
return !Object.values(features).some(f => f.enabled)
|
// completion app citation is always true but not enabled for setting
|
||||||
}, [features])
|
const data = {
|
||||||
|
...features,
|
||||||
|
citation: { enabled: isChatMode ? features.citation?.enabled : false },
|
||||||
|
}
|
||||||
|
return !Object.values(data).some(f => f.enabled)
|
||||||
|
}, [features, isChatMode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='-translate-y-2 m-1 mt-0 px-2.5 py-2 pt-4 bg-util-colors-indigo-indigo-50 rounded-b-[10px] border-l border-b border-r border-components-panel-border-subtle'>
|
<div className='-translate-y-2 m-1 mt-0 px-2.5 py-2 pt-4 bg-util-colors-indigo-indigo-50 rounded-b-[10px] border-l border-b border-r border-components-panel-border-subtle'>
|
||||||
@ -102,7 +109,7 @@ const FeatureBar = ({
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!!features.citation?.enabled && (
|
{isChatMode && !!features.citation?.enabled && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
popupContent={t('appDebug.feature.citation.title')}
|
popupContent={t('appDebug.feature.citation.title')}
|
||||||
>
|
>
|
||||||
|
@ -16,7 +16,7 @@ import type {
|
|||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import type { VisionFile } from '@/types/app'
|
import type { VisionFile } from '@/types/app'
|
||||||
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
|
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils'
|
||||||
|
|
||||||
type GetAbortController = (abortController: AbortController) => void
|
type GetAbortController = (abortController: AbortController) => void
|
||||||
type SendCallback = {
|
type SendCallback = {
|
||||||
|
@ -437,6 +437,7 @@ const translation = {
|
|||||||
run: 'RUN',
|
run: 'RUN',
|
||||||
},
|
},
|
||||||
result: 'Output Text',
|
result: 'Output Text',
|
||||||
|
noResult: 'Output will be displayed here.',
|
||||||
datasetConfig: {
|
datasetConfig: {
|
||||||
settingTitle: 'Retrieval settings',
|
settingTitle: 'Retrieval settings',
|
||||||
knowledgeTip: 'Click the “+” button to add knowledge',
|
knowledgeTip: 'Click the “+” button to add knowledge',
|
||||||
|
@ -430,6 +430,7 @@ const translation = {
|
|||||||
run: '运行',
|
run: '运行',
|
||||||
},
|
},
|
||||||
result: '结果',
|
result: '结果',
|
||||||
|
noResult: '输出结果展示在这',
|
||||||
datasetConfig: {
|
datasetConfig: {
|
||||||
settingTitle: '召回设置',
|
settingTitle: '召回设置',
|
||||||
knowledgeTip: '点击 “+” 按钮添加知识库',
|
knowledgeTip: '点击 “+” 按钮添加知识库',
|
||||||
|
@ -212,7 +212,7 @@ export type ModelConfig = {
|
|||||||
user_input_form: UserInputFormItem[]
|
user_input_form: UserInputFormItem[]
|
||||||
dataset_query_variable?: string
|
dataset_query_variable?: string
|
||||||
more_like_this: {
|
more_like_this: {
|
||||||
enabled: boolean
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
suggested_questions_after_answer: {
|
suggested_questions_after_answer: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
Loading…
x
Reference in New Issue
Block a user