feat: Retain LLM Configuration Settings When Changing Model (#21247)
This commit is contained in:
@@ -80,6 +80,8 @@ import {
|
|||||||
import PluginDependency from '@/app/components/workflow/plugin-dependency'
|
import PluginDependency from '@/app/components/workflow/plugin-dependency'
|
||||||
import { supportFunctionCall } from '@/utils/tool-call'
|
import { supportFunctionCall } from '@/utils/tool-call'
|
||||||
import { MittProvider } from '@/context/mitt-context'
|
import { MittProvider } from '@/context/mitt-context'
|
||||||
|
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
|
||||||
type PublishConfig = {
|
type PublishConfig = {
|
||||||
modelConfig: ModelConfig
|
modelConfig: ModelConfig
|
||||||
@@ -453,7 +455,21 @@ const Configuration: FC = () => {
|
|||||||
...visionConfig,
|
...visionConfig,
|
||||||
enabled: supportVision,
|
enabled: supportVision,
|
||||||
}, true)
|
}, true)
|
||||||
setCompletionParams({})
|
|
||||||
|
try {
|
||||||
|
const { params: filtered, removedDetails } = await fetchAndMergeValidCompletionParams(
|
||||||
|
provider,
|
||||||
|
modelId,
|
||||||
|
completionParams,
|
||||||
|
)
|
||||||
|
if (Object.keys(removedDetails).length)
|
||||||
|
Toast.notify({ type: 'warning', message: `${t('common.modelProvider.parametersInvalidRemoved')}: ${Object.entries(removedDetails).map(([k, reason]) => `${k} (${reason})`).join(', ')}` })
|
||||||
|
setCompletionParams(filtered)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Toast.notify({ type: 'error', message: t('common.error') })
|
||||||
|
setCompletionParams({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
||||||
|
@@ -19,6 +19,8 @@ import Editor from '@/app/components/workflow/nodes/_base/components/prompt/edit
|
|||||||
import StructureOutput from './components/structure-output'
|
import StructureOutput from './components/structure-output'
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
|
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
|
||||||
|
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
|
||||||
const i18nPrefix = 'workflow.nodes.llm'
|
const i18nPrefix = 'workflow.nodes.llm'
|
||||||
|
|
||||||
@@ -68,10 +70,27 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||||||
modelId: string
|
modelId: string
|
||||||
mode?: string
|
mode?: string
|
||||||
}) => {
|
}) => {
|
||||||
handleCompletionParamsChange({})
|
(async () => {
|
||||||
handleModelChanged(model)
|
try {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
const { params: filtered, removedDetails } = await fetchAndMergeValidCompletionParams(
|
||||||
}, [])
|
model.provider,
|
||||||
|
model.modelId,
|
||||||
|
inputs.model.completion_params,
|
||||||
|
)
|
||||||
|
const keys = Object.keys(removedDetails)
|
||||||
|
if (keys.length)
|
||||||
|
Toast.notify({ type: 'warning', message: `${t('common.modelProvider.parametersInvalidRemoved')}: ${keys.map(k => `${k} (${removedDetails[k]})`).join(', ')}` })
|
||||||
|
handleCompletionParamsChange(filtered)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Toast.notify({ type: 'error', message: t('common.error') })
|
||||||
|
handleCompletionParamsChange({})
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleModelChanged(model)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, [inputs.model.completion_params])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-2'>
|
<div className='mt-2'>
|
||||||
|
88
web/utils/completion-params.ts
Normal file
88
web/utils/completion-params.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import type { FormValue, ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
|
||||||
|
export const mergeValidCompletionParams = (
|
||||||
|
oldParams: FormValue | undefined,
|
||||||
|
rules: ModelParameterRule[],
|
||||||
|
): { params: FormValue; removedDetails: Record<string, string> } => {
|
||||||
|
if (!oldParams || Object.keys(oldParams).length === 0)
|
||||||
|
return { params: {}, removedDetails: {} }
|
||||||
|
|
||||||
|
const acceptedKeys = new Set(rules.map(r => r.name))
|
||||||
|
const ruleMap: Record<string, ModelParameterRule> = {}
|
||||||
|
rules.forEach((r) => {
|
||||||
|
ruleMap[r.name] = r
|
||||||
|
})
|
||||||
|
|
||||||
|
const nextParams: FormValue = {}
|
||||||
|
const removedDetails: Record<string, string> = {}
|
||||||
|
|
||||||
|
Object.entries(oldParams).forEach(([key, value]) => {
|
||||||
|
if (!acceptedKeys.has(key)) {
|
||||||
|
removedDetails[key] = 'unsupported'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const rule = ruleMap[key]
|
||||||
|
if (!rule) {
|
||||||
|
removedDetails[key] = 'unsupported'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rule.type) {
|
||||||
|
case 'int':
|
||||||
|
case 'float': {
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
removedDetails[key] = 'invalid type'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const min = rule.min ?? Number.NEGATIVE_INFINITY
|
||||||
|
const max = rule.max ?? Number.POSITIVE_INFINITY
|
||||||
|
if (value < min || value > max) {
|
||||||
|
removedDetails[key] = `out of range (${min}-${max})`
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nextParams[key] = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 'boolean': {
|
||||||
|
if (typeof value !== 'boolean') {
|
||||||
|
removedDetails[key] = 'invalid type'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nextParams[key] = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 'string':
|
||||||
|
case 'text': {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
removedDetails[key] = 'invalid type'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Array.isArray(rule.options) && rule.options.length) {
|
||||||
|
if (!(rule.options as string[]).includes(value)) {
|
||||||
|
removedDetails[key] = 'unsupported option'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextParams[key] = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
removedDetails[key] = `unsupported rule type: ${(rule as any)?.type ?? 'unknown'}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { params: nextParams, removedDetails }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchAndMergeValidCompletionParams = async (
|
||||||
|
provider: string,
|
||||||
|
modelId: string,
|
||||||
|
oldParams: FormValue | undefined,
|
||||||
|
): Promise<{ params: FormValue; removedDetails: Record<string, string> }> => {
|
||||||
|
const { fetchModelParameterRules } = await import('@/service/common')
|
||||||
|
const url = `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`
|
||||||
|
const { data: parameterRules } = await fetchModelParameterRules(url)
|
||||||
|
return mergeValidCompletionParams(oldParams, parameterRules ?? [])
|
||||||
|
}
|
Reference in New Issue
Block a user