fix: one step run (#14724)

This commit is contained in:
zxhlyh 2025-03-03 13:29:59 +08:00 committed by GitHub
parent cd46ebbb34
commit e53052ab7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 214 additions and 49 deletions

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useCallback } from 'react' import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import produce from 'immer' import produce from 'immer'
import { import {
@ -24,8 +24,9 @@ import { Variable02 } from '@/app/components/base/icons/src/vender/solid/develop
import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
interface Props { type Props = {
payload: InputVar payload: InputVar
value: any value: any
onChange: (value: any) => void onChange: (value: any) => void
@ -94,6 +95,21 @@ const FormItem: FC<Props> = ({
const isArrayLikeType = [InputVarType.contexts, InputVarType.iterator].includes(type) const isArrayLikeType = [InputVarType.contexts, InputVarType.iterator].includes(type)
const isContext = type === InputVarType.contexts const isContext = type === InputVarType.contexts
const isIterator = type === InputVarType.iterator const isIterator = type === InputVarType.iterator
const singleFileValue = useMemo(() => {
if (payload.variable === '#files#')
return value?.[0] || []
return value ? [value] : []
}, [payload.variable, value])
const handleSingleFileChange = useCallback((files: FileEntity[]) => {
if (payload.variable === '#files#')
onChange(files)
else if (files.length)
onChange(files[0])
else
onChange(null)
}, [onChange, payload.variable])
return ( return (
<div className={cn(className)}> <div className={cn(className)}>
{!isArrayLikeType && ( {!isArrayLikeType && (
@ -161,13 +177,8 @@ const FormItem: FC<Props> = ({
} }
{(type === InputVarType.singleFile) && ( {(type === InputVarType.singleFile) && (
<FileUploaderInAttachmentWrapper <FileUploaderInAttachmentWrapper
value={value ? [value] : []} value={singleFileValue}
onChange={(files) => { onChange={handleSingleFileChange}
if (files.length)
onChange(files[0])
else
onChange(null)
}}
fileConfig={{ fileConfig={{
allowed_file_types: inStepRun allowed_file_types: inStepRun
? [ ? [

View File

@ -50,8 +50,11 @@ function formatValue(value: string | any, type: InputVarType) {
if (type === InputVarType.multiFiles) if (type === InputVarType.multiFiles)
return getProcessedFiles(value) return getProcessedFiles(value)
if (type === InputVarType.singleFile) if (type === InputVarType.singleFile) {
if (Array.isArray(value))
return getProcessedFiles(value)
return getProcessedFiles([value])[0] return getProcessedFiles([value])[0]
}
return value return value
} }

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { unionBy } from 'lodash-es' import { unionBy } from 'lodash-es'
import produce from 'immer' import produce from 'immer'
@ -139,6 +139,11 @@ const useOneStepRun = <T>({
const checkValid = checkValidFns[data.type] const checkValid = checkValidFns[data.type]
const appId = useAppStore.getState().appDetail?.id const appId = useAppStore.getState().appDetail?.id
const [runInputData, setRunInputData] = useState<Record<string, any>>(defaultRunInputData || {}) const [runInputData, setRunInputData] = useState<Record<string, any>>(defaultRunInputData || {})
const runInputDataRef = useRef(runInputData)
const handleSetRunInputData = useCallback((data: Record<string, any>) => {
runInputDataRef.current = data
setRunInputData(data)
}, [])
const iterationTimes = iteratorInputKey ? runInputData[iteratorInputKey].length : 0 const iterationTimes = iteratorInputKey ? runInputData[iteratorInputKey].length : 0
const [runResult, setRunResult] = useState<any>(null) const [runResult, setRunResult] = useState<any>(null)
@ -421,7 +426,8 @@ const useOneStepRun = <T>({
handleRun, handleRun,
handleStop, handleStop,
runInputData, runInputData,
setRunInputData, runInputDataRef,
setRunInputData: handleSetRunInputData,
runResult, runResult,
iterationRunResult, iterationRunResult,
} }

View File

@ -5,6 +5,7 @@ import MemoryConfig from '../_base/components/memory-config'
import VarReferencePicker from '../_base/components/variable/var-reference-picker' import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import ConfigVision from '../_base/components/config-vision' import ConfigVision from '../_base/components/config-vision'
import useConfig from './use-config' import useConfig from './use-config'
import { findVariableWhenOnLLMVision } from '../utils'
import type { LLMNodeType } from './types' import type { LLMNodeType } from './types'
import ConfigPrompt from './components/config-prompt' import ConfigPrompt from './components/config-prompt'
import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list'
@ -102,15 +103,16 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
) )
} }
if (isVisionModel) { if (isVisionModel && data.vision.enabled && data.vision.configs?.variable_selector) {
const variableName = data.vision.configs?.variable_selector?.[1] || t(`${i18nPrefix}.files`)! const currentVariable = findVariableWhenOnLLMVision(data.vision.configs.variable_selector, availableVars)
forms.push( forms.push(
{ {
label: t(`${i18nPrefix}.vision`)!, label: t(`${i18nPrefix}.vision`)!,
inputs: [{ inputs: [{
label: variableName!, label: currentVariable?.variable as any,
variable: '#files#', variable: '#files#',
type: InputVarType.files, type: currentVariable?.formType as any,
required: false, required: false,
}], }],
values: { '#files#': visionFiles }, values: { '#files#': visionFiles },

View File

@ -306,6 +306,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
handleRun, handleRun,
handleStop, handleStop,
runInputData, runInputData,
runInputDataRef,
setRunInputData, setRunInputData,
runResult, runResult,
toVarInputs, toVarInputs,
@ -331,27 +332,27 @@ const useConfig = (id: string, payload: LLMNodeType) => {
const setInputVarValues = useCallback((newPayload: Record<string, any>) => { const setInputVarValues = useCallback((newPayload: Record<string, any>) => {
const newVars = { const newVars = {
...newPayload, ...newPayload,
'#context#': runInputData['#context#'], '#context#': runInputDataRef.current['#context#'],
'#files#': runInputData['#files#'], '#files#': runInputDataRef.current['#files#'],
} }
setRunInputData(newVars) setRunInputData(newVars)
}, [runInputData, setRunInputData]) }, [runInputDataRef, setRunInputData])
const contexts = runInputData['#context#'] const contexts = runInputData['#context#']
const setContexts = useCallback((newContexts: string[]) => { const setContexts = useCallback((newContexts: string[]) => {
setRunInputData({ setRunInputData({
...runInputData, ...runInputDataRef.current,
'#context#': newContexts, '#context#': newContexts,
}) })
}, [runInputData, setRunInputData]) }, [runInputDataRef, setRunInputData])
const visionFiles = runInputData['#files#'] const visionFiles = runInputData['#files#']
const setVisionFiles = useCallback((newFiles: any[]) => { const setVisionFiles = useCallback((newFiles: any[]) => {
setRunInputData({ setRunInputData({
...runInputData, ...runInputDataRef.current,
'#files#': newFiles, '#files#': newFiles,
}) })
}, [runInputData, setRunInputData]) }, [runInputDataRef, setRunInputData])
const allVarStrArr = (() => { const allVarStrArr = (() => {
const arr = isChatModel ? (inputs.prompt_template as PromptItem[]).filter(item => item.edition_type !== EditionType.jinja2).map(item => item.text) : [(inputs.prompt_template as PromptItem).text] const arr = isChatModel ? (inputs.prompt_template as PromptItem[]).filter(item => item.edition_type !== EditionType.jinja2).map(item => item.text) : [(inputs.prompt_template as PromptItem).text]

View File

@ -6,6 +6,7 @@ import VarReferencePicker from '../_base/components/variable/var-reference-picke
import Editor from '../_base/components/prompt/editor' import Editor from '../_base/components/prompt/editor'
import ResultPanel from '../../run/result-panel' import ResultPanel from '../../run/result-panel'
import ConfigVision from '../_base/components/config-vision' import ConfigVision from '../_base/components/config-vision'
import { findVariableWhenOnLLMVision } from '../utils'
import useConfig from './use-config' import useConfig from './use-config'
import type { ParameterExtractorNodeType } from './types' import type { ParameterExtractorNodeType } from './types'
import ExtractParameter from './components/extract-parameter/list' import ExtractParameter from './components/extract-parameter/list'
@ -21,6 +22,7 @@ import Tooltip from '@/app/components/base/tooltip'
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form' import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
import { VarType } from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types'
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
const i18nPrefix = 'workflow.nodes.parameterExtractor' const i18nPrefix = 'workflow.nodes.parameterExtractor'
const i18nCommonPrefix = 'workflow.common' const i18nCommonPrefix = 'workflow.common'
@ -51,6 +53,7 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
handleReasoningModeChange, handleReasoningModeChange,
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
availableVisionVars,
inputVarValues, inputVarValues,
varInputs, varInputs,
isVisionModel, isVisionModel,
@ -63,10 +66,50 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
handleStop, handleStop,
runResult, runResult,
setInputVarValues, setInputVarValues,
visionFiles,
setVisionFiles,
} = useConfig(id, data) } = useConfig(id, data)
const model = inputs.model const model = inputs.model
const singleRunForms = (() => {
const forms: FormProps[] = []
forms.push(
{
label: t('workflow.nodes.llm.singleRun.variable')!,
inputs: [{
label: t(`${i18nPrefix}.inputVar`)!,
variable: 'query',
type: InputVarType.paragraph,
required: true,
}, ...varInputs],
values: inputVarValues,
onChange: setInputVarValues,
},
)
if (isVisionModel && data.vision.enabled && data.vision.configs?.variable_selector) {
const currentVariable = findVariableWhenOnLLMVision(data.vision.configs.variable_selector, availableVisionVars)
forms.push(
{
label: t('workflow.nodes.llm.vision')!,
inputs: [{
label: currentVariable?.variable as any,
variable: '#files#',
type: currentVariable?.formType as any,
required: false,
}],
values: { '#files#': visionFiles },
onChange: keyValue => setVisionFiles((keyValue as any)['#files#']),
},
)
}
return forms
})()
return ( return (
<div className='pt-2'> <div className='pt-2'>
<div className='px-4 space-y-4'> <div className='px-4 space-y-4'>
@ -213,18 +256,7 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
<BeforeRunForm <BeforeRunForm
nodeName={inputs.title} nodeName={inputs.title}
onHide={hideSingleRun} onHide={hideSingleRun}
forms={[ forms={singleRunForms}
{
inputs: [{
label: t(`${i18nPrefix}.inputVar`)!,
variable: 'query',
type: InputVarType.paragraph,
required: true,
}, ...varInputs],
values: inputVarValues,
onChange: setInputVarValues,
},
]}
runningStatus={runningStatus} runningStatus={runningStatus}
onRun={handleRun} onRun={handleRun}
onStop={handleStop} onStop={handleStop}

View File

@ -165,6 +165,10 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
return [VarType.number, VarType.string].includes(varPayload.type) return [VarType.number, VarType.string].includes(varPayload.type)
}, []) }, [])
const filterVisionInputVar = useCallback((varPayload: Var) => {
return [VarType.file, VarType.arrayFile].includes(varPayload.type)
}, [])
const { const {
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
@ -173,6 +177,13 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
filterVar: filterInputVar, filterVar: filterInputVar,
}) })
const {
availableVars: availableVisionVars,
} = useAvailableVarList(id, {
onlyLeafNodeVar: false,
filterVar: filterVisionInputVar,
})
const handleCompletionParamsChange = useCallback((newParams: Record<string, any>) => { const handleCompletionParamsChange = useCallback((newParams: Record<string, any>) => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
draft.model.completion_params = newParams draft.model.completion_params = newParams
@ -223,13 +234,15 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
handleRun, handleRun,
handleStop, handleStop,
runInputData, runInputData,
runInputDataRef,
setRunInputData, setRunInputData,
runResult, runResult,
} = useOneStepRun<ParameterExtractorNodeType>({ } = useOneStepRun<ParameterExtractorNodeType>({
id, id,
data: inputs, data: inputs,
defaultRunInputData: { defaultRunInputData: {
query: '', 'query': '',
'#files#': [],
}, },
}) })
@ -247,6 +260,14 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
setRunInputData(newPayload) setRunInputData(newPayload)
}, [setRunInputData]) }, [setRunInputData])
const visionFiles = runInputData['#files#']
const setVisionFiles = useCallback((newFiles: any[]) => {
setRunInputData({
...runInputDataRef.current,
'#files#': newFiles,
})
}, [runInputDataRef, setRunInputData])
return { return {
readOnly, readOnly,
handleInputVarChange, handleInputVarChange,
@ -264,6 +285,7 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
hasSetBlockStatus, hasSetBlockStatus,
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
availableVisionVars,
isSupportFunctionCall, isSupportFunctionCall,
handleReasoningModeChange, handleReasoningModeChange,
handleMemoryChange, handleMemoryChange,
@ -279,6 +301,8 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
handleStop, handleStop,
runResult, runResult,
setInputVarValues, setInputVarValues,
visionFiles,
setVisionFiles,
} }
} }

View File

@ -3,6 +3,7 @@ import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import VarReferencePicker from '../_base/components/variable/var-reference-picker' import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import ConfigVision from '../_base/components/config-vision' import ConfigVision from '../_base/components/config-vision'
import { findVariableWhenOnLLMVision } from '../utils'
import useConfig from './use-config' import useConfig from './use-config'
import ClassList from './components/class-list' import ClassList from './components/class-list'
import AdvancedSetting from './components/advanced-setting' import AdvancedSetting from './components/advanced-setting'
@ -15,6 +16,7 @@ import ResultPanel from '@/app/components/workflow/run/result-panel'
import Split from '@/app/components/workflow/nodes/_base/components/split' import Split from '@/app/components/workflow/nodes/_base/components/split'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
const i18nPrefix = 'workflow.nodes.questionClassifiers' const i18nPrefix = 'workflow.nodes.questionClassifiers'
@ -36,6 +38,7 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
hasSetBlockStatus, hasSetBlockStatus,
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
availableVisionVars,
handleInstructionChange, handleInstructionChange,
inputVarValues, inputVarValues,
varInputs, varInputs,
@ -51,10 +54,50 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
handleStop, handleStop,
runResult, runResult,
filterVar, filterVar,
visionFiles,
setVisionFiles,
} = useConfig(id, data) } = useConfig(id, data)
const model = inputs.model const model = inputs.model
const singleRunForms = (() => {
const forms: FormProps[] = []
forms.push(
{
label: t('workflow.nodes.llm.singleRun.variable')!,
inputs: [{
label: t(`${i18nPrefix}.inputVars`)!,
variable: 'query',
type: InputVarType.paragraph,
required: true,
}, ...varInputs],
values: inputVarValues,
onChange: setInputVarValues,
},
)
if (isVisionModel && data.vision.enabled && data.vision.configs?.variable_selector) {
const currentVariable = findVariableWhenOnLLMVision(data.vision.configs.variable_selector, availableVisionVars)
forms.push(
{
label: t('workflow.nodes.llm.vision')!,
inputs: [{
label: currentVariable?.variable as any,
variable: '#files#',
type: currentVariable?.formType as any,
required: false,
}],
values: { '#files#': visionFiles },
onChange: keyValue => setVisionFiles((keyValue as any)['#files#']),
},
)
}
return forms
})()
return ( return (
<div className='pt-2'> <div className='pt-2'>
<div className='px-4 space-y-4'> <div className='px-4 space-y-4'>
@ -143,18 +186,7 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
<BeforeRunForm <BeforeRunForm
nodeName={inputs.title} nodeName={inputs.title}
onHide={hideSingleRun} onHide={hideSingleRun}
forms={[ forms={singleRunForms}
{
inputs: [{
label: t(`${i18nPrefix}.inputVars`)!,
variable: 'query',
type: InputVarType.paragraph,
required: true,
}, ...varInputs],
values: inputVarValues,
onChange: setInputVarValues,
},
]}
runningStatus={runningStatus} runningStatus={runningStatus}
onRun={handleRun} onRun={handleRun}
onStop={handleStop} onStop={handleStop}

View File

@ -124,6 +124,10 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
return [VarType.number, VarType.string].includes(varPayload.type) return [VarType.number, VarType.string].includes(varPayload.type)
}, []) }, [])
const filterVisionInputVar = useCallback((varPayload: Var) => {
return [VarType.file, VarType.arrayFile].includes(varPayload.type)
}, [])
const { const {
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
@ -132,6 +136,13 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
filterVar: filterInputVar, filterVar: filterInputVar,
}) })
const {
availableVars: availableVisionVars,
} = useAvailableVarList(id, {
onlyLeafNodeVar: false,
filterVar: filterVisionInputVar,
})
const hasSetBlockStatus = { const hasSetBlockStatus = {
history: false, history: false,
query: isChatMode ? checkHasQueryBlock(inputs.instruction) : false, query: isChatMode ? checkHasQueryBlock(inputs.instruction) : false,
@ -161,13 +172,15 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
handleRun, handleRun,
handleStop, handleStop,
runInputData, runInputData,
runInputDataRef,
setRunInputData, setRunInputData,
runResult, runResult,
} = useOneStepRun<QuestionClassifierNodeType>({ } = useOneStepRun<QuestionClassifierNodeType>({
id, id,
data: inputs, data: inputs,
defaultRunInputData: { defaultRunInputData: {
query: '', 'query': '',
'#files#': [],
}, },
}) })
@ -195,6 +208,14 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
setRunInputData(newPayload) setRunInputData(newPayload)
}, [setRunInputData]) }, [setRunInputData])
const visionFiles = runInputData['#files#']
const setVisionFiles = useCallback((newFiles: any[]) => {
setRunInputData({
...runInputDataRef.current,
'#files#': newFiles,
})
}, [runInputDataRef, setRunInputData])
const filterVar = useCallback((varPayload: Var) => { const filterVar = useCallback((varPayload: Var) => {
return varPayload.type === VarType.string return varPayload.type === VarType.string
}, []) }, [])
@ -212,6 +233,7 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
hasSetBlockStatus, hasSetBlockStatus,
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
availableVisionVars,
handleInstructionChange, handleInstructionChange,
varInputs, varInputs,
inputVarValues, inputVarValues,
@ -228,6 +250,8 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
query, query,
setQuery, setQuery,
runResult, runResult,
visionFiles,
setVisionFiles,
} }
} }

View File

@ -0,0 +1,30 @@
import type {
NodeOutPutVar,
ValueSelector,
} from '@/app/components/workflow/types'
import { InputVarType } from '@/app/components/workflow/types'
export const findVariableWhenOnLLMVision = (valueSelector: ValueSelector, availableVars: NodeOutPutVar[]) => {
const currentVariableNode = availableVars.find((availableVar) => {
if (valueSelector[0] === 'sys' && availableVar.isStartNode)
return true
return valueSelector[0] === availableVar.nodeId
})
const currentVariable = currentVariableNode?.vars.find((variable) => {
if (valueSelector[0] === 'sys' && variable.variable === `sys.${valueSelector[1]}`)
return true
return variable.variable === valueSelector[1]
})
let formType = ''
if (currentVariable?.type === 'array[file]')
formType = InputVarType.multiFiles
if (currentVariable?.type === 'file')
formType = InputVarType.singleFile
return currentVariable && {
...currentVariable,
formType,
}
}