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 15d0a16d2b..187b2f18e5 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -67,6 +67,16 @@ const structTypeToVarType = (type: Type): VarType => { } as any)[type] || VarType.string } +export const varTypeToStructType = (type: VarType): Type => { + return ({ + [VarType.string]: Type.string, + [VarType.number]: Type.number, + [VarType.boolean]: Type.boolean, + [VarType.object]: Type.object, + [VarType.array]: Type.array, + } as any)[type] || Type.string +} + const findExceptVarInStructuredProperties = (properties: Record, filterVar: (payload: Var, selector: ValueSelector) => boolean): Record => { const res = produce(properties, (draft) => { Object.keys(properties).forEach((key) => { 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 64047dda84..e45bb12f07 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 @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { useHover } from 'ahooks' import { useTranslation } from 'react-i18next' import cn from '@/utils/classnames' @@ -15,20 +15,12 @@ import { 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 { Type } from '../../../llm/types' import PickerStructurePanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' - -type ObjectChildrenProps = { - nodeId: string - title: string - data: Var[] - objPath: string[] - onChange: (value: ValueSelector, item: Var) => void - onHovering?: (value: boolean) => void - itemWidth?: number - isSupportFileVar?: boolean -} +import { varTypeToStructType } from './utils' +import type { Field } from '@/app/components/workflow/nodes/llm/types' +import { FILE_STRUCT } from '@/app/components/workflow/constants' type ItemProps = { nodeId: string @@ -49,7 +41,6 @@ const Item: FC = ({ itemData, onChange, onHovering, - itemWidth, isSupportFileVar, isException, }) => { @@ -59,6 +50,31 @@ const Item: FC = ({ const isSys = itemData.variable.startsWith('sys.') const isEnv = itemData.variable.startsWith('env.') const isChatVar = itemData.variable.startsWith('conversation.') + + const objStructuredOutput: StructuredOutput | null = useMemo(() => { + if (!isObj) return null + const properties: Record = {}; + (isFile ? FILE_STRUCT : (itemData.children as Var[])).forEach((c) => { + properties[c.variable] = { + type: varTypeToStructType(c.type), + } + }) + return { + schema: { + type: Type.object, + properties, + required: [], + additionalProperties: false, + }, + } + }, [isFile, isObj, itemData.children]) + + const structuredOutput = (() => { + if (isStructureOutput) + return itemData.children as StructuredOutput + return objStructuredOutput + })() + const itemRef = useRef(null) const [isItemHovering, setIsItemHovering] = useState(false) const _ = useHover(itemRef, { @@ -136,109 +152,21 @@ const Item: FC = ({ - {isStructureOutput && ( + {(isStructureOutput || isObj) && ( { onChange(valueSelector, itemData) }} /> )} - {(isObj && !isFile) && ( - // eslint-disable-next-line ts/no-use-before-define - - )} - {isFile && ( - // eslint-disable-next-line ts/no-use-before-define - - )} ) } -const ObjectChildren: FC = ({ - title, - nodeId, - objPath, - data, - onChange, - onHovering, - itemWidth, - isSupportFileVar, -}) => { - const currObjPath = objPath - const itemRef = useRef(null) - const [isItemHovering, setIsItemHovering] = useState(false) - const _ = useHover(itemRef, { - onChange: (hovering) => { - if (hovering) { - setIsItemHovering(true) - } - else { - setTimeout(() => { - setIsItemHovering(false) - }, 100) - } - }, - }) - const [isChildrenHovering, setIsChildrenHovering] = useState(false) - const isHovering = isItemHovering || isChildrenHovering - useEffect(() => { - onHovering && onHovering(isHovering) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isHovering]) - useEffect(() => { - onHovering && onHovering(isItemHovering) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isItemHovering]) - // absolute top-[-2px] - return ( -
-
{title}.{currObjPath.join('.')}
- { - (data && data.length > 0) - && data.map((v, i) => ( - - )) - } -
- ) -} - type Props = { hideSearch?: boolean searchBoxClassName?: string