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 title: ReactNode
headerRight?: ReactNode headerRight?: ReactNode
hasHeaderBottomBorder?: boolean hasHeaderBottomBorder?: boolean
isFocus?: boolean
noBodySpacing?: boolean noBodySpacing?: boolean
children?: ReactNode children?: ReactNode
} }
@ -20,25 +19,17 @@ const FeaturePanel: FC<IFeaturePanelProps> = ({
title, title,
headerRight, headerRight,
hasHeaderBottomBorder, hasHeaderBottomBorder,
isFocus,
noBodySpacing, noBodySpacing,
children, children,
}) => { }) => {
return ( return (
<div <div className={cn('rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn pb-3', noBodySpacing && '!pb-0', className)}>
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)',
}
: {}}
>
{/* Header */} {/* 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 justify-between items-center h-8'>
<div className='flex items-center space-x-1 shrink-0'> <div className='flex items-center space-x-1 shrink-0'>
{headerIcon && <div className='flex items-center justify-center w-6 h-6'>{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 className='text-text-secondary system-sm-semibold'>{title}</div>
</div> </div>
<div className='flex gap-2 items-center'> <div className='flex gap-2 items-center'>
{headerRight && <div>{headerRight}</div>} {headerRight && <div>{headerRight}</div>}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,23 +1,7 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next'
import { 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 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 }> = ({ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({
title, title,
@ -38,87 +22,3 @@ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }>
</div> </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 = { type Props = {
isChatMode?: boolean isChatMode?: boolean
showFileUpload?: boolean
disabled?: boolean disabled?: boolean
onFeatureBarClick?: (state: boolean) => void onFeatureBarClick?: (state: boolean) => void
} }
const FeatureBar = ({ const FeatureBar = ({
isChatMode = true, isChatMode = true,
showFileUpload = true,
disabled, disabled,
onFeatureBarClick, onFeatureBarClick,
}: Props) => { }: Props) => {
@ -28,9 +30,10 @@ const FeatureBar = ({
const data = { const data = {
...features, ...features,
citation: { enabled: isChatMode ? features.citation?.enabled : false }, citation: { enabled: isChatMode ? features.citation?.enabled : false },
file: showFileUpload ? features.file! : { enabled: false },
} }
return !Object.values(data).some(f => f.enabled) return !Object.values(data).some(f => f.enabled)
}, [features, isChatMode]) }, [features, isChatMode, showFileUpload])
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'>
@ -91,7 +94,7 @@ const FeatureBar = ({
</Tooltip> </Tooltip>
</VoiceSettings> </VoiceSettings>
)} )}
{!!features.file?.enabled && ( {showFileUpload && !!features.file?.enabled && (
<Tooltip <Tooltip
popupContent={t('appDebug.feature.fileUpload.title')} popupContent={t('appDebug.feature.fileUpload.title')}
> >

View File

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