diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index e407e7ea81..2c0959ec53 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -665,7 +665,7 @@ export const getVarType = ({ if (!targetVar) return VarType.string - const isStructuredOutputVar = !!targetVar.children.schema?.properties + const isStructuredOutputVar = !!targetVar.children?.schema?.properties if (isStructuredOutputVar) { let currProperties = targetVar.children.schema; (valueSelector as ValueSelector).slice(2).forEach((key, i) => { diff --git a/web/app/components/workflow/nodes/llm/components/structure-output.tsx b/web/app/components/workflow/nodes/llm/components/structure-output.tsx new file mode 100644 index 0000000000..b466d79ad9 --- /dev/null +++ b/web/app/components/workflow/nodes/llm/components/structure-output.tsx @@ -0,0 +1,60 @@ +'use client' +import Button from '@/app/components/base/button' +import { RiEditLine } from '@remixicon/react' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import type { SchemaRoot, StructuredOutput } from '../types' +import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' +import { useBoolean } from 'ahooks' +import JsonSchemaConfigModal from './json-schema-config-modal' + +type Props = { + value?: StructuredOutput + onChange: (value: StructuredOutput) => void, +} + +const StructureOutput: FC = ({ + value, + onChange, +}) => { + const [showConfig, { + setTrue: showConfigModal, + setFalse: hideConfigModal, + }] = useBoolean(false) + + const handleChange = useCallback((value: SchemaRoot) => { + onChange({ + schema: value, + }) + }, [onChange]) + return ( +
+
+
+
structured_output
+
object
+
+ +
+ {value?.schema ? ( + ) : ( +
no data
+ )} + + {showConfig && ( + + )} +
+ ) +} +export default React.memo(StructureOutput) diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 6c3831f5bc..cab8245aba 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -20,6 +20,7 @@ import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/c 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 StructureOutput from './components/structure-output' const i18nPrefix = 'workflow.nodes.llm' @@ -64,6 +65,7 @@ const Panel: FC> = ({ contexts, setContexts, runningStatus, + handleStructureOutputChange, handleRun, handleStop, varInputs, @@ -279,6 +281,10 @@ const Panel: FC> = ({ type='string' description={t(`${i18nPrefix}.outputVars.output`)} /> + {isShowSingleRun && ( diff --git a/web/app/components/workflow/nodes/llm/types.ts b/web/app/components/workflow/nodes/llm/types.ts index 2fcd110879..a4931f7017 100644 --- a/web/app/components/workflow/nodes/llm/types.ts +++ b/web/app/components/workflow/nodes/llm/types.ts @@ -15,6 +15,8 @@ export type LLMNodeType = CommonNodeType & { enabled: boolean configs?: VisionSetting } + structured_output_enabled?: boolean + structured_output?: StructuredOutput } export enum Type { diff --git a/web/app/components/workflow/nodes/llm/use-config.ts b/web/app/components/workflow/nodes/llm/use-config.ts index 6b2d27e70f..0e6fe00cbc 100644 --- a/web/app/components/workflow/nodes/llm/use-config.ts +++ b/web/app/components/workflow/nodes/llm/use-config.ts @@ -9,7 +9,7 @@ import { } from '../../hooks' import useAvailableVarList from '../_base/hooks/use-available-var-list' import useConfigVision from '../../hooks/use-config-vision' -import type { LLMNodeType } from './types' +import type { LLMNodeType, StructuredOutput } from './types' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { ModelTypeEnum, @@ -277,6 +277,13 @@ const useConfig = (id: string, payload: LLMNodeType) => { setInputs(newInputs) }, [inputs, setInputs]) + const handleStructureOutputChange = useCallback((newOutput: StructuredOutput) => { + const newInputs = produce(inputs, (draft) => { + draft.structured_output = newOutput + }) + setInputs(newInputs) + }, [inputs, setInputs]) + const filterInputVar = useCallback((varPayload: Var) => { return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type) }, []) @@ -408,6 +415,7 @@ const useConfig = (id: string, payload: LLMNodeType) => { setContexts, varInputs, runningStatus, + handleStructureOutputChange, handleRun, handleStop, runResult,