From 6a76e27b05681cc44f21e91c5949ec8c5d7fd08a Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 11 Mar 2025 16:09:09 +0800 Subject: [PATCH] feat: support var show --- .../nodes/_base/components/variable/utils.ts | 52 ++++++++++++------- .../variable/var-reference-vars.tsx | 22 +++++--- web/app/components/workflow/types.ts | 3 +- 3 files changed, 50 insertions(+), 27 deletions(-) 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 2e2fc4cb22..f7365c7b41 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -3,7 +3,7 @@ import { isArray, uniq } from 'lodash-es' import type { CodeNodeType } from '../../../code/types' import type { EndNodeType } from '../../../end/types' import type { AnswerNodeType } from '../../../answer/types' -import type { LLMNodeType } from '../../../llm/types' +import type { LLMNodeType, StructuredOutput } from '../../../llm/types' import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/types' import type { IfElseNodeType } from '../../../if-else/types' import type { TemplateTransformNodeType } from '../../../template-transform/types' @@ -20,6 +20,8 @@ import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/type import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' import type { ConversationVariable, EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types' +import mockStructData from '@/app/components/workflow/nodes/llm/mock-struct-data' + import { HTTP_REQUEST_OUTPUT_STRUCT, KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT, @@ -59,15 +61,18 @@ const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: Val const res: Var = { variable: obj.variable, type: isFile ? VarType.file : VarType.object, - children: children.filter((item: Var) => { + children: children.length > 0 ? children.filter((item: Var) => { const { children } = item - const currSelector = [...value_selector, item.variable] - if (!children) - return filterVar(item, currSelector) - - const obj = findExceptVarInObject(item, filterVar, currSelector, false) // File doesn't contains file children - return obj.children && obj.children?.length > 0 - }), + const isStructuredOutput = !!(children as StructuredOutput)?.schema + if (!isStructuredOutput) { + const currSelector = [...value_selector, item.variable] + if (!children) + return filterVar(item, currSelector) + const obj = findExceptVarInObject(item, filterVar, currSelector, false) // File doesn't contains file children + return obj.children && (obj.children as Var[])?.length > 0 + } + return true // TODO: handle structured output + }) : (children || []), } return res } @@ -138,7 +143,14 @@ const formatItem = ( } case BlockEnum.LLM: { - res.vars = LLM_OUTPUT_STRUCT + res.vars = [ + ...LLM_OUTPUT_STRUCT, + { + variable: 'structured_output', + type: VarType.object, + children: mockStructData, + }, + ] break } @@ -404,7 +416,7 @@ const formatItem = ( return false const obj = findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector, isFile) - return obj?.children && obj?.children.length > 0 + return obj?.children && ((obj?.children as Var[]).length > 0 || Object.keys((obj?.children as StructuredOutput)?.schema?.properties || {}).length > 0) }).map((v) => { const isFile = v.type === VarType.file @@ -428,6 +440,9 @@ const formatItem = ( return findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector, isFile) }) + if (res.nodeId === 'llm') + console.log(res) + return res } export const toNodeOutputVars = ( @@ -527,8 +542,7 @@ export const getVarType = ({ isConstant, environmentVariables = [], conversationVariables = [], -}: -{ +}: { valueSelector: ValueSelector parentNode?: Node | null isIterationItem?: boolean @@ -1094,11 +1108,13 @@ const varToValueSelectorList = (v: Var, parentValueSelector: ValueSelector, res: return res.push([...parentValueSelector, v.variable]) - - if (v.children && v.children.length > 0) { - v.children.forEach((child) => { - varToValueSelectorList(child, [...parentValueSelector, v.variable], res) - }) + if (v.children) { + if ((v.children as Var[])?.length > 0) { + (v.children as Var[]).forEach((child) => { + varToValueSelectorList(child, [...parentValueSelector, v.variable], res) + }) + } + // TODO: handle structured output } } diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 3b7845003a..762709e7a4 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -16,8 +16,10 @@ import Input from '@/app/components/base/input' import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import { checkKeys } from '@/utils/var' import { FILE_STRUCT } from '@/app/components/workflow/constants' +import type { StructuredOutput } from '../../../llm/types' +import PickerStructurePanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' -interface ObjectChildrenProps { +type ObjectChildrenProps = { nodeId: string title: string data: Var[] @@ -28,7 +30,7 @@ interface ObjectChildrenProps { isSupportFileVar?: boolean } -interface ItemProps { +type ItemProps = { nodeId: string title: string objPath: string[] @@ -51,8 +53,9 @@ const Item: FC = ({ isSupportFileVar, isException, }) => { - const isFile = itemData.type === VarType.file - const isObj = ([VarType.object, VarType.file].includes(itemData.type) && itemData.children && itemData.children.length > 0) + const isStructureOutput = itemData.type === VarType.object && (itemData.children as StructuredOutput)?.schema?.properties + const isFile = itemData.type === VarType.file && !isStructureOutput + const isObj = ([VarType.object, VarType.file].includes(itemData.type) && itemData.children && (itemData.children as Var[]).length > 0) const isSys = itemData.variable.startsWith('sys.') const isEnv = itemData.variable.startsWith('env.') const isChatVar = itemData.variable.startsWith('conversation.') @@ -77,7 +80,7 @@ const Item: FC = ({ }) const [isChildrenHovering, setIsChildrenHovering] = useState(false) const isHovering = isItemHovering || isChildrenHovering - const open = isObj && isHovering + const open = (isObj || isStructureOutput) && isHovering useEffect(() => { onHovering && onHovering(isHovering) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -125,7 +128,7 @@ const Item: FC = ({ )}
{itemData.type}
- {isObj && ( + {(isObj || isStructureOutput) && ( )} @@ -133,6 +136,9 @@ const Item: FC = ({ + {isStructureOutput && ( + + )} {(isObj && !isFile) && ( // eslint-disable-next-line ts/no-use-before-define = ({ ) } -interface Props { +type Props = { hideSearch?: boolean searchBoxClassName?: string vars: NodeOutPutVar[] @@ -322,7 +328,7 @@ const VarReferenceVars: FC = ({ } :
{t('workflow.common.noVar')}
} - + ) } export default React.memo(VarReferenceVars) diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 10df1b02b9..5c8cf18a24 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -14,6 +14,7 @@ import type { ErrorHandleTypeEnum, } from '@/app/components/workflow/nodes/_base/components/error-handle/types' import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/components/retry/types' +import type { StructuredOutput } from './nodes/llm/types' export enum BlockEnum { Start = 'start', @@ -250,7 +251,7 @@ export enum VarType { export type Var = { variable: string type: VarType - children?: Var[] // if type is obj, has the children struct + children?: Var[] | StructuredOutput // if type is obj, has the children struct isParagraph?: boolean isSelect?: boolean options?: string[]