mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 19:59:50 +08:00
vision config
This commit is contained in:
parent
0b94218378
commit
b60c7a5826
|
@ -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>}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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' />
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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' />
|
||||
|
|
|
@ -70,7 +70,7 @@ const DatasetConfig: FC = () => {
|
|||
|
||||
return (
|
||||
<FeaturePanel
|
||||
className='mt-3'
|
||||
className='mt-2'
|
||||
headerIcon={Icon}
|
||||
title={t('appDebug.feature.dataSet.title')}
|
||||
headerRight={
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')}
|
||||
>
|
||||
|
|
|
@ -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} />
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue
Block a user