Feat/attachments (#9526)
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
@@ -79,6 +79,8 @@ const nodeDefault: NodeDefault<LLMNodeType> = {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!errorMessages && payload.vision?.enabled && !payload.vision.configs?.variable_selector?.length)
|
||||
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.visionVariable`) })
|
||||
return {
|
||||
isValid: !errorMessages,
|
||||
errorMessage: errorMessages,
|
||||
|
@@ -3,8 +3,8 @@ import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MemoryConfig from '../_base/components/memory-config'
|
||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||
import ConfigVision from '../_base/components/config-vision'
|
||||
import useConfig from './use-config'
|
||||
import ResolutionPicker from './components/resolution-picker'
|
||||
import type { LLMNodeType } from './types'
|
||||
import ConfigPrompt from './components/config-prompt'
|
||||
import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list'
|
||||
@@ -13,14 +13,13 @@ import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import { Resolution } from '@/types/app'
|
||||
import { InputVarType, type NodePanelProps } from '@/app/components/workflow/types'
|
||||
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
|
||||
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.llm'
|
||||
|
||||
const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
@@ -36,7 +35,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
isChatMode,
|
||||
isCompletionModel,
|
||||
shouldShowContextTip,
|
||||
isShowVisionConfig,
|
||||
isVisionModel,
|
||||
handleModelChanged,
|
||||
hasSetBlockStatus,
|
||||
handleCompletionParamsChange,
|
||||
@@ -102,12 +101,13 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
)
|
||||
}
|
||||
|
||||
if (isShowVisionConfig) {
|
||||
if (isVisionModel) {
|
||||
const variableName = data.vision.configs?.variable_selector?.[1] || t(`${i18nPrefix}.files`)!
|
||||
forms.push(
|
||||
{
|
||||
label: t(`${i18nPrefix}.vision`)!,
|
||||
inputs: [{
|
||||
label: t(`${i18nPrefix}.files`)!,
|
||||
label: variableName!,
|
||||
variable: '#files#',
|
||||
type: InputVarType.files,
|
||||
required: false,
|
||||
@@ -256,28 +256,15 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
)}
|
||||
|
||||
{/* Vision: GPT4-vision and so on */}
|
||||
{isShowVisionConfig && (
|
||||
<>
|
||||
<Split />
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.vision`)}
|
||||
tooltip={t('appDebug.vision.description')!}
|
||||
operations={
|
||||
<Switch size='md' defaultValue={inputs.vision.enabled} onChange={handleVisionResolutionEnabledChange} />
|
||||
}
|
||||
>
|
||||
{inputs.vision.enabled
|
||||
? (
|
||||
<ResolutionPicker
|
||||
value={inputs.vision.configs?.detail || Resolution.high}
|
||||
onChange={handleVisionResolutionChange}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
<ConfigVision
|
||||
nodeId={id}
|
||||
readOnly={readOnly}
|
||||
isVisionModel={isVisionModel}
|
||||
enabled={inputs.vision?.enabled}
|
||||
onEnabledChange={handleVisionResolutionEnabledChange}
|
||||
config={inputs.vision?.configs}
|
||||
onConfigChange={handleVisionResolutionChange}
|
||||
/>
|
||||
</div>
|
||||
<Split />
|
||||
<div className='px-4 pt-4 pb-2'>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import type { Resolution } from '@/types/app'
|
||||
import type { CommonNodeType, Memory, ModelConfig, PromptItem, ValueSelector, Variable } from '@/app/components/workflow/types'
|
||||
import type { CommonNodeType, Memory, ModelConfig, PromptItem, ValueSelector, Variable, VisionSetting } from '@/app/components/workflow/types'
|
||||
|
||||
export type LLMNodeType = CommonNodeType & {
|
||||
model: ModelConfig
|
||||
@@ -14,8 +13,6 @@ export type LLMNodeType = CommonNodeType & {
|
||||
}
|
||||
vision: {
|
||||
enabled: boolean
|
||||
configs?: {
|
||||
detail: Resolution
|
||||
}
|
||||
configs?: VisionSetting
|
||||
}
|
||||
}
|
||||
|
@@ -8,11 +8,10 @@ import {
|
||||
useNodesReadOnly,
|
||||
} from '../../hooks'
|
||||
import useAvailableVarList from '../_base/hooks/use-available-var-list'
|
||||
import useConfigVision from '../../hooks/use-config-vision'
|
||||
import type { LLMNodeType } from './types'
|
||||
import { Resolution } from '@/types/app'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import {
|
||||
ModelFeatureEnum,
|
||||
ModelTypeEnum,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
@@ -100,7 +99,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultConfig, isChatModel])
|
||||
|
||||
const [modelChanged, setModelChanged] = useState(false)
|
||||
@@ -109,6 +108,21 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
currentModel,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textGeneration)
|
||||
|
||||
const {
|
||||
isVisionModel,
|
||||
handleVisionResolutionEnabledChange,
|
||||
handleVisionResolutionChange,
|
||||
handleModelChanged: handleVisionConfigAfterModelChanged,
|
||||
} = useConfigVision(model, {
|
||||
payload: inputs.vision,
|
||||
onChange: (newPayload) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.vision = newPayload
|
||||
})
|
||||
setInputs(newInputs)
|
||||
},
|
||||
})
|
||||
|
||||
const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
|
||||
const newInputs = produce(inputRef.current, (draft) => {
|
||||
draft.model.provider = model.provider
|
||||
@@ -139,44 +153,14 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const {
|
||||
currentModel: currModel,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{
|
||||
provider: model.provider,
|
||||
model: model.name,
|
||||
},
|
||||
)
|
||||
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
||||
// change to vision model to set vision enabled, else disabled
|
||||
useEffect(() => {
|
||||
if (!modelChanged)
|
||||
return
|
||||
setModelChanged(false)
|
||||
if (!isShowVisionConfig) {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.vision = {
|
||||
enabled: false,
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
return
|
||||
}
|
||||
if (!inputs.vision?.enabled) {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision?.enabled) {
|
||||
draft.vision = {
|
||||
enabled: true,
|
||||
configs: {
|
||||
detail: Resolution.high,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isShowVisionConfig, modelChanged])
|
||||
handleVisionConfigAfterModelChanged()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isVisionModel, modelChanged])
|
||||
|
||||
// variables
|
||||
const isShowVars = (() => {
|
||||
@@ -293,46 +277,12 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleVisionResolutionEnabledChange = useCallback((enabled: boolean) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision) {
|
||||
draft.vision = {
|
||||
enabled,
|
||||
configs: {
|
||||
detail: Resolution.high,
|
||||
},
|
||||
}
|
||||
}
|
||||
else {
|
||||
draft.vision.enabled = enabled
|
||||
if (!draft.vision.configs) {
|
||||
draft.vision.configs = {
|
||||
detail: Resolution.high,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleVisionResolutionChange = useCallback((newResolution: Resolution) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision.configs) {
|
||||
draft.vision.configs = {
|
||||
detail: Resolution.high,
|
||||
}
|
||||
}
|
||||
draft.vision.configs.detail = newResolution
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const filterInputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string, VarType.secret].includes(varPayload.type)
|
||||
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const filterVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret].includes(varPayload.type)
|
||||
const filterMemoryPromptVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
@@ -340,7 +290,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
availableNodesWithParent,
|
||||
} = useAvailableVarList(id, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar,
|
||||
filterVar: filterMemoryPromptVar,
|
||||
})
|
||||
|
||||
// single run
|
||||
@@ -425,7 +375,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
isCompletionModel,
|
||||
hasSetBlockStatus,
|
||||
shouldShowContextTip,
|
||||
isShowVisionConfig,
|
||||
isVisionModel,
|
||||
handleModelChanged,
|
||||
handleCompletionParamsChange,
|
||||
isShowVars,
|
||||
@@ -435,7 +385,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
handleAddEmptyVariable,
|
||||
handleContextVarChange,
|
||||
filterInputVar,
|
||||
filterVar,
|
||||
filterVar: filterMemoryPromptVar,
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
handlePromptChange,
|
||||
|
Reference in New Issue
Block a user