vision config

This commit is contained in:
JzoNg 2024-09-04 15:45:33 +08:00
parent 0b94218378
commit b60c7a5826
22 changed files with 116 additions and 440 deletions

View File

@ -9,7 +9,6 @@ export type IFeaturePanelProps = {
title: ReactNode
headerRight?: ReactNode
hasHeaderBottomBorder?: boolean
isFocus?: boolean
noBodySpacing?: boolean
children?: ReactNode
}
@ -20,25 +19,17 @@ const FeaturePanel: FC<IFeaturePanelProps> = ({
title,
headerRight,
hasHeaderBottomBorder,
isFocus,
noBodySpacing,
children,
}) => {
return (
<div
className={cn(className, isFocus && 'border border-[#2D0DEE]', 'rounded-xl bg-gray-50 pt-2 pb-3', noBodySpacing && '!pb-0')}
style={isFocus
? {
boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)',
}
: {}}
>
<div className={cn('rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn pb-3', noBodySpacing && '!pb-0', className)}>
{/* Header */}
<div className={cn('pb-2 px-3', hasHeaderBottomBorder && 'border-b border-gray-100')}>
<div className={cn('px-3 pt-2', hasHeaderBottomBorder && 'border-b border-divider-subtle')}>
<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-6 h-6'>{headerIcon}</div>}
<div className='text-sm font-semibold text-gray-800'>{title}</div>
<div className='text-text-secondary system-sm-semibold'>{title}</div>
</div>
<div className='flex gap-2 items-center'>
{headerRight && <div>{headerRight}</div>}

View File

@ -23,7 +23,7 @@ const HistoryPanel: FC<Props> = ({
return (
<Panel
className='mt-3'
className='mt-2'
title={
<div className='flex items-center gap-2'>
<div>{t('appDebug.feature.conversationHistory.title')}</div>

View File

@ -273,7 +273,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
}
return (
<Panel
className="mt-4"
className="mt-2"
headerIcon={
<VarIcon className='w-4 h-4 text-primary-500' />
}

View File

@ -1,61 +1,84 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { useContext } from 'use-context-selector'
import Panel from '../base/feature-panel'
import ParamConfig from './param-config'
import { Vision } from '@/app/components/base/icons/src/vender/features'
import Tooltip from '@/app/components/base/tooltip'
import Switch from '@/app/components/base/switch'
import { Eye } from '@/app/components/base/icons/src/vender/solid/general'
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
import ConfigContext from '@/context/debug-configuration'
import { Resolution } from '@/types/app'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
const ConfigVision: FC = () => {
const { t } = useTranslation()
const {
isShowVisionConfig,
visionConfig,
setVisionConfig,
} = useContext(ConfigContext)
const { isShowVisionConfig } = useContext(ConfigContext)
const file = useFeatures(s => s.features.file)
const featuresStore = useFeaturesStore()
const handleChange = useCallback((resolution: Resolution) => {
const {
features,
setFeatures,
} = featuresStore!.getState()
const newFeatures = produce(features, (draft) => {
draft.file = {
...draft.file,
image: { detail: resolution },
}
})
setFeatures(newFeatures)
}, [featuresStore])
if (!isShowVisionConfig)
return null
return (<>
<Panel
className="mt-4"
headerIcon={
<Eye className='w-4 h-4 text-[#6938EF]'/>
}
title={
<div className='flex items-center'>
<div className='mr-1'>{t('appDebug.vision.name')}</div>
return (
<div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
<div className='shrink-0 p-1'>
<div className='p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-indigo-indigo-600'>
<Vision className='w-4 h-4 text-text-primary-on-surface' />
</div>
</div>
<div className='grow flex items-center'>
<div className='mr-1 text-text-secondary system-sm-semibold'>{t('appDebug.vision.name')}</div>
<Tooltip
popupContent={
<div className='w-[180px]' >
{t('appDebug.vision.description')}
</div>
}
/>
</div>
<div className='shrink-0 flex items-center'>
<div className='mr-2 flex items-center gap-0.5'>
<div className='text-text-tertiary system-xs-medium-uppercase'>{t('appDebug.vision.visionSettings.resolution')}</div>
<Tooltip
popupContent={
<div className='w-[180px]' >
{t('appDebug.vision.description')}
{t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => (
<div key={item}>{item}</div>
))}
</div>
}
/>
</div>
}
headerRight={
<div className='flex items-center'>
<ParamConfig />
<div className='ml-4 mr-3 w-[1px] h-3.5 bg-gray-200'></div>
<Switch
defaultValue={visionConfig.enabled}
onChange={value => setVisionConfig({
...visionConfig,
enabled: value,
})}
size='md'
<div className='flex items-center gap-1'>
<OptionCard
title={t('appDebug.vision.visionSettings.high')}
selected={file?.image?.detail === Resolution.high}
onSelect={() => handleChange(Resolution.high)}
/>
<OptionCard
title={t('appDebug.vision.visionSettings.low')}
selected={file?.image?.detail === Resolution.low}
onSelect={() => handleChange(Resolution.low)}
/>
</div>
}
noBodySpacing
/>
</>
</div>
</div>
)
}
export default React.memo(ConfigVision)

View File

@ -1,133 +0,0 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import RadioGroup from './radio-group'
import ConfigContext from '@/context/debug-configuration'
import { Resolution, TransferMethod } from '@/types/app'
import ParamItem from '@/app/components/base/param-item'
import Tooltip from '@/app/components/base/tooltip'
const MIN = 1
const MAX = 6
const ParamConfigContent: FC = () => {
const { t } = useTranslation()
const {
visionConfig,
setVisionConfig,
} = useContext(ConfigContext)
const transferMethod = (() => {
if (!visionConfig.transfer_methods || visionConfig.transfer_methods.length === 2)
return TransferMethod.all
return visionConfig.transfer_methods[0]
})()
return (
<div>
<div>
<div className='leading-6 text-base font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.title')}</div>
<div className='pt-3 space-y-6'>
<div>
<div className='mb-2 flex items-center space-x-1'>
<div className='leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.resolution')}</div>
<Tooltip
popupContent={
<div className='w-[180px]' >
{t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => (
<div key={item}>{item}</div>
))}
</div>
}
/>
</div>
<RadioGroup
className='space-x-3'
options={[
{
label: t('appDebug.vision.visionSettings.high'),
value: Resolution.high,
},
{
label: t('appDebug.vision.visionSettings.low'),
value: Resolution.low,
},
]}
value={visionConfig.detail}
onChange={(value: Resolution) => {
setVisionConfig({
...visionConfig,
detail: value,
})
}}
/>
</div>
<div>
<div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
<RadioGroup
className='space-x-3'
options={[
{
label: t('appDebug.vision.visionSettings.both'),
value: TransferMethod.all,
},
{
label: t('appDebug.vision.visionSettings.localUpload'),
value: TransferMethod.local_file,
},
{
label: t('appDebug.vision.visionSettings.url'),
value: TransferMethod.remote_url,
},
]}
value={transferMethod}
onChange={(value: TransferMethod) => {
if (value === TransferMethod.all) {
setVisionConfig({
...visionConfig,
transfer_methods: [TransferMethod.remote_url, TransferMethod.local_file],
})
return
}
setVisionConfig({
...visionConfig,
transfer_methods: [value],
})
}}
/>
</div>
<div>
<ParamItem
id='upload_limit'
className=''
name={t('appDebug.vision.visionSettings.uploadLimit')}
noTooltip
{...{
default: 2,
step: 1,
min: MIN,
max: MAX,
}}
value={visionConfig.number_limits}
enable={true}
onChange={(_key: string, value: number) => {
if (!value)
return
setVisionConfig({
...visionConfig,
number_limits: value,
})
}}
/>
</div>
</div>
</div>
</div>
)
}
export default React.memo(ParamConfigContent)

View File

@ -1,41 +0,0 @@
'use client'
import type { FC } from 'react'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import VoiceParamConfig from './param-config-content'
import cn from '@/utils/classnames'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
const ParamsConfig: FC = () => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
offset={{
mainAxis: 4,
}}
>
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
<Settings01 className='w-3.5 h-3.5 ' />
<div className='ml-1 leading-[18px] text-xs font-medium '>{t('appDebug.voice.settings')}</div>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 50 }}>
<div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
<VoiceParamConfig />
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default memo(ParamsConfig)

View File

@ -1,40 +0,0 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import s from './style.module.css'
import cn from '@/utils/classnames'
type OPTION = {
label: string
value: any
}
type Props = {
className?: string
options: OPTION[]
value: any
onChange: (value: any) => void
}
const RadioGroup: FC<Props> = ({
className = '',
options,
value,
onChange,
}) => {
return (
<div className={cn(className, 'flex')}>
{options.map(item => (
<div
key={item.value}
className={cn(s.item, item.value === value && s.checked)}
onClick={() => onChange(item.value)}
>
<div className={s.radio}></div>
<div className='text-[13px] font-medium text-gray-900'>{item.label}</div>
</div>
))}
</div>
)
}
export default React.memo(RadioGroup)

View File

@ -1,24 +0,0 @@
.item {
@apply grow flex items-center h-8 px-2.5 rounded-lg bg-gray-25 border border-gray-100 cursor-pointer space-x-2;
}
.item:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.item.checked {
background-color: #ffffff;
border-color: #528BFF;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.10);
}
.radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
}
.item.checked .radio {
border-width: 5px;
border-color: #155eef;
}

View File

@ -58,7 +58,7 @@ const AgentTools: FC = () => {
return (
<>
<Panel
className="mt-4"
className="mt-2"
noBodySpacing={tools.length === 0}
headerIcon={
<RiHammerFill className='w-4 h-4 text-primary-500' />

View File

@ -70,7 +70,7 @@ const DatasetConfig: FC = () => {
return (
<FeaturePanel
className='mt-3'
className='mt-2'
headerIcon={Icon}
title={t('appDebug.feature.dataSet.title')}
headerRight={

View File

@ -49,6 +49,7 @@ const ChatUserInput = ({
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'>
{/* ##TODO## file_upload */}
{promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
<div
key={key}

View File

@ -40,7 +40,6 @@ const ChatItem: FC<ChatItemProps> = ({
modelConfig,
appId,
inputs,
visionConfig,
collectionList,
} = useDebugConfigurationContext()
const { textGenerationModelList } = useProviderContext()
@ -99,7 +98,7 @@ const ChatItem: FC<ChatItemProps> = ({
model_config: configData,
}
if (visionConfig.enabled && files?.length && supportVision)
if ((config.file_upload as any).enabled && files?.length && supportVision)
data.files = files
handleSend(
@ -110,7 +109,7 @@ const ChatItem: FC<ChatItemProps> = ({
onGetSuggestedQuestions: (responseItemId, getAbortController) => fetchSuggestedQuestions(appId, responseItemId, getAbortController),
},
)
}, [appId, config, handleSend, inputs, modelAndParameter, textGenerationModelList, visionConfig.enabled])
}, [appId, config, handleSend, inputs, modelAndParameter, textGenerationModelList])
const { eventEmitter } = useEventEmitterContextContext()
eventEmitter?.useSubscription((v: any) => {

View File

@ -21,9 +21,10 @@ import { useStore as useAppStore } from '@/app/components/app/store'
const DebugWithMultipleModel = () => {
const {
mode,
visionConfig,
isShowVisionConfig,
} = useDebugConfigurationContext()
const speech2text = useFeatures(s => s.features.speech2text)
const file = useFeatures(s => s.features.file)
const {
multipleModelConfigs,
checkCanSend,
@ -129,10 +130,11 @@ const DebugWithMultipleModel = () => {
<div className='shrink-0 pb-0 px-6'>
<ChatInputArea
showFeatureBar
showFileUpload={isShowVisionConfig}
onFeatureBarClick={setShowAppConfigureFeaturesModal}
onSend={handleSend}
speechToTextConfig={speech2text as any}
visionConfig={visionConfig}
visionConfig={file}
/>
</div>
)}

View File

@ -36,7 +36,6 @@ const TextGenerationItem: FC<TextGenerationItemProps> = ({
completionPromptConfig,
dataSets,
datasetConfigs,
visionConfig,
} = useDebugConfigurationContext()
const { textGenerationModelList } = useProviderContext()
const features = useFeatures(s => s.features)
@ -58,10 +57,8 @@ const TextGenerationItem: FC<TextGenerationItemProps> = ({
more_like_this: features.moreLikeThis as any,
sensitive_word_avoidance: features.moderation as any,
text_to_speech: features.text2speech as any,
file_upload: features.file as any,
opening_statement: introduction,
file_upload: {
image: visionConfig,
},
speech_to_text: speechToTextConfig,
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
retriever_resource: citationConfig,
@ -103,7 +100,7 @@ const TextGenerationItem: FC<TextGenerationItemProps> = ({
model_config: configData,
}
if (visionConfig.enabled && files && files?.length > 0) {
if ((config.file_upload as any).enabled && files && files?.length > 0) {
data.files = files.map((item) => {
if (item.transfer_method === TransferMethod.local_file) {
return {

View File

@ -39,9 +39,9 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
modelConfig,
appId,
inputs,
visionConfig,
collectionList,
completionParams,
isShowVisionConfig,
} = useDebugConfigurationContext()
const { textGenerationModelList } = useProviderContext()
const features = useFeatures(s => s.features)
@ -105,7 +105,7 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
model_config: configData,
}
if (visionConfig.enabled && files?.length && supportVision)
if ((config.file_upload as any)?.enabled && files?.length && supportVision)
data.files = files
handleSend(
@ -116,7 +116,7 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
onGetSuggestedQuestions: (responseItemId, getAbortController) => fetchSuggestedQuestions(appId, responseItemId, getAbortController),
},
)
}, [appId, checkCanSend, completionParams, config, handleSend, inputs, modelConfig, textGenerationModelList, visionConfig.enabled])
}, [appId, checkCanSend, completionParams, config, handleSend, inputs, modelConfig, textGenerationModelList])
const allToolIcons = useMemo(() => {
const icons: Record<string, any> = {}
@ -142,6 +142,7 @@ const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSi
chatContainerClassName='px-3 pt-6'
chatFooterClassName='px-3 pt-10 pb-0'
showFeatureBar
showFileUpload={isShowVisionConfig}
onFeatureBarClick={setShowAppConfigureFeaturesModal}
suggestedQuestions={suggestedQuestions}
onSend={doSend}

View File

@ -3,7 +3,7 @@ import type { FC } from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { setAutoFreeze } from 'immer'
import produce, { setAutoFreeze } from 'immer'
import { useBoolean } from 'ahooks'
import {
RiAddLine,
@ -34,7 +34,7 @@ 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'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import type { ModelConfig as BackendModelConfig, VisionFile } from '@/types/app'
import type { ModelConfig as BackendModelConfig, VisionFile, VisionSettings } from '@/types/app'
import { promptVariablesToUserInputsForm } from '@/utils/model-config'
import TextGeneration from '@/app/components/app/text-generate/item'
import { IS_CE_EDITION } from '@/config'
@ -48,7 +48,7 @@ import { useProviderContext } from '@/context/provider-context'
import AgentLogModal from '@/app/components/base/agent-log-modal'
import PromptLogModal from '@/app/components/base/prompt-log-modal'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useFeatures } from '@/app/components/base/features/hooks'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
type IDebug = {
isAPIKeySet: boolean
@ -84,8 +84,6 @@ const Debug: FC<IDebug> = ({
speechToTextConfig,
textToSpeechConfig,
citationConfig,
// moderationConfig,
// moreLikeThisConfig,
formattingChanged,
setFormattingChanged,
dataSets,
@ -93,8 +91,6 @@ const Debug: FC<IDebug> = ({
completionParams,
hasSetContextVar,
datasetConfigs,
visionConfig,
setVisionConfig,
} = useContext(ConfigContext)
const { eventEmitter } = useEventEmitterContextContext()
const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.textEmbedding)
@ -203,6 +199,7 @@ const Debug: FC<IDebug> = ({
const [completionRes, setCompletionRes] = useState('')
const [messageId, setMessageId] = useState<string | null>(null)
const features = useFeatures(s => s.features)
const featuresStore = useFeaturesStore()
const sendTextCompletion = async () => {
if (isResponding) {
@ -252,10 +249,7 @@ const Debug: FC<IDebug> = ({
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: {
image: visionConfig,
},
file_upload: features.file as any,
opening_statement: introduction,
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
speech_to_text: speechToTextConfig,
@ -272,7 +266,7 @@ const Debug: FC<IDebug> = ({
model_config: postModelConfig,
}
if (visionConfig.enabled && completionFiles && completionFiles?.length > 0) {
if ((features.file as any).enabled && completionFiles && completionFiles?.length > 0) {
data.files = completionFiles.map((item) => {
if (item.transfer_method === TransferMethod.local_file) {
return {
@ -348,7 +342,7 @@ const Debug: FC<IDebug> = ({
)
}
const handleVisionConfigInMultipleModel = () => {
const handleVisionConfigInMultipleModel = useCallback(() => {
if (debugWithMultipleModel && mode) {
const supportedVision = multipleModelConfigs.some((modelConfig) => {
const currentProvider = textGenerationModelList.find(modelItem => modelItem.provider === modelConfig.provider)
@ -356,25 +350,24 @@ const Debug: FC<IDebug> = ({
return currentModel?.features?.includes(ModelFeatureEnum.vision)
})
const {
features,
setFeatures,
} = featuresStore!.getState()
if (supportedVision) {
setVisionConfig({
...visionConfig,
enabled: true,
}, true)
}
else {
setVisionConfig({
...visionConfig,
enabled: false,
}, true)
}
const newFeatures = produce(features, (draft) => {
draft.file = {
...draft.file,
enabled: supportedVision,
}
})
setFeatures(newFeatures)
}
}
}, [debugWithMultipleModel, featuresStore, mode, multipleModelConfigs, textGenerationModelList])
useEffect(() => {
handleVisionConfigInMultipleModel()
}, [multipleModelConfigs, mode])
}, [multipleModelConfigs, mode, handleVisionConfigInMultipleModel])
const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
currentLogItem: state.currentLogItem,
@ -455,7 +448,7 @@ const Debug: FC<IDebug> = ({
onSend={handleSendTextCompletion}
inputs={inputs}
visionConfig={{
...visionConfig,
...features.file! as VisionSettings,
image_file_size_limit: fileUploadConfigResponse?.image_file_size_limit,
}}
onVisionFilesChange={setCompletionFiles}

View File

@ -598,7 +598,6 @@ const Configuration: FC = () => {
completionParams: model.completion_params,
}
// ##TODO## new vision config
if (modelConfig.file_upload)
handleSetVisionConfig(modelConfig.file_upload.image, true)
@ -693,10 +692,7 @@ const Configuration: FC = () => {
sensitive_word_avoidance: features?.moderation as any,
speech_to_text: features?.speech2text as any,
text_to_speech: features?.text2speech as any,
// ##TODO## file_upload
file_upload: {
image: visionConfig,
},
file_upload: features?.file as any,
suggested_questions_after_answer: features?.suggested as any,
retriever_resource: features?.citation as any,
agent_mode: {
@ -983,6 +979,7 @@ const Configuration: FC = () => {
<NewFeaturePanel
show
inWorkflow={false}
showFileUpload={isShowVisionConfig}
isChatMode={mode !== 'completion'}
disabled={false}
onChange={handleFeaturesChange}

View File

@ -24,6 +24,7 @@ import type { FileUpload } from '@/app/components/base/features/types'
type ChatInputAreaProps = {
showFeatureBar?: boolean
showFileUpload?: boolean
featureBarDisabled?: boolean
onFeatureBarClick?: (state: boolean) => void
visionConfig?: FileUpload
@ -33,6 +34,7 @@ type ChatInputAreaProps = {
}
const ChatInputArea = ({
showFeatureBar,
showFileUpload,
featureBarDisabled,
onFeatureBarClick,
visionConfig,
@ -155,7 +157,7 @@ const ChatInputArea = ({
)
}
</div>
{showFeatureBar && <FeatureBar disabled={featureBarDisabled} onFeatureBarClick={onFeatureBarClick} />}
{showFeatureBar && <FeatureBar showFileUpload={showFileUpload} disabled={featureBarDisabled} onFeatureBarClick={onFeatureBarClick} />}
</>
</FileContextProvider>
)

View File

@ -62,6 +62,7 @@ export type ChatProps = {
hideLogModal?: boolean
themeBuilder?: ThemeBuilder
showFeatureBar?: boolean
showFileUpload?: boolean
onFeatureBarClick?: (state: boolean) => void
}
@ -92,6 +93,7 @@ const Chat: FC<ChatProps> = ({
hideLogModal,
themeBuilder,
showFeatureBar,
showFileUpload,
onFeatureBarClick,
}) => {
const { t } = useTranslation()
@ -267,6 +269,7 @@ const Chat: FC<ChatProps> = ({
!noChatInput && (
<ChatInputArea
showFeatureBar={showFeatureBar}
showFileUpload={showFileUpload}
featureBarDisabled={isResponding}
onFeatureBarClick={onFeatureBarClick}
visionConfig={config?.file_upload}

View File

@ -1,23 +1,7 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { usePathname, useRouter } from 'next/navigation'
import ConfigParamModal from './config-param-modal'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import Tooltip from '@/app/components/base/tooltip'
import { LinkExternal02, Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import ConfigContext from '@/context/debug-configuration'
import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type'
import { fetchAnnotationConfig, updateAnnotationScore } from '@/service/annotation'
import type { AnnotationReplyConfig as AnnotationReplyConfigType } from '@/models/debug'
type Props = {
onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void
onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void
}
export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({
title,
@ -38,87 +22,3 @@ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }>
</div>
)
}
const AnnotationReplyConfig: FC<Props> = ({
onEmbeddingChange,
onScoreChange,
}) => {
const { t } = useTranslation()
const router = useRouter()
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const {
annotationConfig,
} = useContext(ConfigContext)
const [isShowEdit, setIsShowEdit] = React.useState(false)
return (
<>
<Panel
className="mt-4"
headerIcon={
<MessageFast className='w-4 h-4 text-[#444CE7]' />
}
title={t('appDebug.feature.annotation.title')}
headerRight={
<div className='flex items-center'>
<div
className='flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200'
onClick={() => { setIsShowEdit(true) }}
>
<Settings04 className="w-[14px] h-[14px]" />
<div className='text-xs font-medium'>
{t('common.operation.params')}
</div>
</div>
<div
className='ml-1 flex items-center h-7 px-3 space-x-1 leading-[18px] text-xs font-medium text-gray-700 rounded-md cursor-pointer hover:bg-gray-200'
onClick={() => {
router.push(`/app/${appId}/annotations`)
}}>
<div>{t('appDebug.feature.annotation.cacheManagement')}</div>
<LinkExternal02 className='w-3.5 h-3.5' />
</div>
</div>
}
noBodySpacing
/>
{isShowEdit && (
<ConfigParamModal
appId={appId}
isShow
onHide={() => {
setIsShowEdit(false)
}}
onSave={async (embeddingModel, score) => {
const annotationConfig = await fetchAnnotationConfig(appId) as AnnotationReplyConfigType
let isEmbeddingModelChanged = false
if (
embeddingModel.embedding_model_name !== annotationConfig.embedding_model.embedding_model_name
&& embeddingModel.embedding_provider_name !== annotationConfig.embedding_model.embedding_provider_name
) {
await onEmbeddingChange(embeddingModel)
isEmbeddingModelChanged = true
}
if (score !== annotationConfig.score_threshold) {
await updateAnnotationScore(appId, annotationConfig.id, score)
if (isEmbeddingModelChanged)
onScoreChange(score, embeddingModel)
else
onScoreChange(score)
}
setIsShowEdit(false)
}}
annotationConfig={annotationConfig}
/>
)}
</>
)
}
export default React.memo(AnnotationReplyConfig)

View File

@ -10,12 +10,14 @@ import cn from '@/utils/classnames'
type Props = {
isChatMode?: boolean
showFileUpload?: boolean
disabled?: boolean
onFeatureBarClick?: (state: boolean) => void
}
const FeatureBar = ({
isChatMode = true,
showFileUpload = true,
disabled,
onFeatureBarClick,
}: Props) => {
@ -28,9 +30,10 @@ const FeatureBar = ({
const data = {
...features,
citation: { enabled: isChatMode ? features.citation?.enabled : false },
file: showFileUpload ? features.file! : { enabled: false },
}
return !Object.values(data).some(f => f.enabled)
}, [features, isChatMode])
}, [features, isChatMode, showFileUpload])
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'>
@ -91,7 +94,7 @@ const FeatureBar = ({
</Tooltip>
</VoiceSettings>
)}
{!!features.file?.enabled && (
{showFileUpload && !!features.file?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.fileUpload.title')}
>

View File

@ -24,6 +24,7 @@ type Props = {
onChange?: OnFeaturesChange
onClose: () => void
inWorkflow?: boolean
showFileUpload?: boolean
promptVariables?: PromptVariable[]
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}
@ -35,6 +36,7 @@ const NewFeaturePanel = ({
onChange,
onClose,
inWorkflow = true,
showFileUpload = true,
promptVariables,
onAutoAddPromptVariable,
}: Props) => {
@ -77,7 +79,7 @@ const NewFeaturePanel = ({
{text2speechDefaultModel && (
<TextToSpeech disabled={disabled} onChange={onChange} />
)}
<FileUpload disabled={disabled} onChange={onChange} />
{showFileUpload && <FileUpload disabled={disabled} onChange={onChange} />}
{isChatMode && (
<FollowUp disabled={disabled} onChange={onChange} />
)}