import type { RefObject } from 'react' import type { LLMNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { InputVar, PromptItem, Var, Variable } from '@/app/components/workflow/types' import { noop } from 'es-toolkit/function' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useHooksStore } from '@/app/components/workflow/hooks-store/store' import { InputVarType, VarType } from '@/app/components/workflow/types' import { AppModeEnum } from '@/types/app' import { FlowType } from '@/types/common' import { useIsChatMode } from '../../hooks' import useConfigVision from '../../hooks/use-config-vision' import { EditionType } from '../../types' import useAvailableVarList from '../_base/hooks/use-available-var-list' import useNodeCrud from '../_base/hooks/use-node-crud' import { findVariableWhenOnLLMVision } from '../utils' const i18nPrefix = 'nodes.llm' const isSystemInputVar = (item: InputVar) => { return typeof item.variable === 'string' && item.variable.startsWith('#sys.') } type Params = { id: string payload: LLMNodeType runInputData: Record runInputDataRef: RefObject> getInputVars: (textList: string[]) => InputVar[] setRunInputData: (data: Record) => void toVarInputs: (variables: Variable[]) => InputVar[] } const useSingleRunFormParams = ({ id, payload, runInputData, runInputDataRef, getInputVars, setRunInputData, toVarInputs, }: Params) => { const { t } = useTranslation() const { inputs } = useNodeCrud(id, payload) const getVarInputs = getInputVars const isChatMode = useIsChatMode() const flowType = useHooksStore((s) => s.configsMap?.flowType) const isSnippetFlow = flowType === FlowType.snippet const contexts = runInputData['#context#'] const setContexts = useCallback( (newContexts: string[]) => { setRunInputData?.({ ...runInputDataRef.current, '#context#': newContexts, }) }, [runInputDataRef, setRunInputData], ) const visionFiles = runInputData['#files#'] const setVisionFiles = useCallback( (newFiles: any[]) => { setRunInputData?.({ ...runInputDataRef.current, '#files#': newFiles, }) }, [runInputDataRef, setRunInputData], ) // model const model = inputs.model const modelMode = inputs.model?.mode const isChatModel = modelMode === AppModeEnum.CHAT const { isVisionModel } = useConfigVision(model, { payload: inputs.vision, onChange: noop, }) const isShowVars = (() => { if (isChatModel) return (inputs.prompt_template as PromptItem[]).some( (item) => item.edition_type === EditionType.jinja2, ) return (inputs.prompt_template as PromptItem).edition_type === EditionType.jinja2 })() const filterMemoryPromptVar = useCallback((varPayload: Var) => { return [ VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile, ].includes(varPayload.type) }, []) const { availableVars } = useAvailableVarList(id, { onlyLeafNodeVar: false, filterVar: filterMemoryPromptVar, }) 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] if (!isSnippetFlow && isChatMode && isChatModel && !!inputs.memory) arr.push('{{#sys.query#}}') if (isChatMode && isChatModel && !!inputs.memory) arr.push(inputs.memory.query_prompt_template) return arr })() const varInputs = (() => { const vars = getVarInputs(allVarStrArr) || [] const filteredVars = isSnippetFlow ? vars.filter((item) => !isSystemInputVar(item)) : vars if (isShowVars) { const jinjaVars = toVarInputs ? toVarInputs(inputs.prompt_config?.jinja2_variables || []) : [] return isSnippetFlow ? [...filteredVars, ...jinjaVars.filter((item) => !isSystemInputVar(item))] : [...filteredVars, ...jinjaVars] } return filteredVars })() const inputVarValues = (() => { const vars: Record = {} Object.keys(runInputData) .filter((key) => !['#context#', '#files#'].includes(key)) .forEach((key) => { vars[key] = runInputData[key] }) return vars })() const setInputVarValues = useCallback( (newPayload: Record) => { const newVars = { ...newPayload, '#context#': runInputDataRef.current['#context#'], '#files#': runInputDataRef.current['#files#'], } setRunInputData?.(newVars) }, [runInputDataRef, setRunInputData], ) const forms = (() => { const forms: FormProps[] = [] if (varInputs.length > 0) { forms.push({ label: t(($) => $[`${i18nPrefix}.singleRun.variable`], { ns: 'workflow' })!, inputs: varInputs, values: inputVarValues, onChange: setInputVarValues, }) } if (inputs.context?.variable_selector && inputs.context?.variable_selector.length > 0) { forms.push({ label: t(($) => $[`${i18nPrefix}.context`], { ns: 'workflow' })!, inputs: [ { label: '', variable: '#context#', type: InputVarType.contexts, required: false, }, ], values: { '#context#': contexts }, onChange: (keyValue) => setContexts(keyValue['#context#']), }) } if (isVisionModel && payload.vision?.enabled && payload.vision?.configs?.variable_selector) { const currentVariable = findVariableWhenOnLLMVision( payload.vision.configs.variable_selector, availableVars, ) forms.push({ label: t(($) => $[`${i18nPrefix}.vision`], { ns: 'workflow' })!, 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 })() const getDependentVars = () => { const promptVars = varInputs .map((item) => { // Guard against null/undefined variable to prevent app crash if (!item.variable || typeof item.variable !== 'string') return [] return item.variable.slice(1, -1).split('.') }) .filter((arr) => arr.length > 0) const contextVar = payload.context.variable_selector const vars = [...promptVars, contextVar] if (isVisionModel && payload.vision?.enabled && payload.vision?.configs?.variable_selector) { const visionVar = payload.vision.configs.variable_selector vars.push(visionVar) } return vars } const getDependentVar = (variable: string) => { if (variable === '#context#') return payload.context.variable_selector if (variable === '#files#') return payload.vision.configs?.variable_selector return false } return { forms, getDependentVars, getDependentVar, } } export default useSingleRunFormParams