| | |
| | | import type { CodeNodeType } from '../../../code/types' |
| | | import type { EndNodeType } from '../../../end/types' |
| | | import type { AnswerNodeType } from '../../../answer/types' |
| | | import { type LLMNodeType, type StructuredOutput, Type } from '../../../llm/types' |
| | | import type { LLMNodeType } from '../../../llm/types' |
| | | import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/types' |
| | | import type { IfElseNodeType } from '../../../if-else/types' |
| | | import type { TemplateTransformNodeType } from '../../../template-transform/types' |
| | |
| | | import type { ToolNodeType } from '../../../tool/types' |
| | | import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types' |
| | | import type { IterationNodeType } from '../../../iteration/types' |
| | | import type { LoopNodeType } from '../../../loop/types' |
| | | import type { ListFilterNodeType } from '../../../list-operator/types' |
| | | import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants' |
| | | import { OUTPUT_FILE_SUB_VARIABLES } from '../../../if-else/default' |
| | | import type { DocExtractorNodeType } from '../../../document-extractor/types' |
| | | import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' |
| | | 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 type { Field as StructField } from '@/app/components/workflow/nodes/llm/types' |
| | | |
| | | import { |
| | | HTTP_REQUEST_OUTPUT_STRUCT, |
| | | KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT, |
| | |
| | | } from '@/app/components/workflow/constants' |
| | | import type { PromptItem } from '@/models/debug' |
| | | import { VAR_REGEX } from '@/config' |
| | | import type { AgentNodeType } from '../../../agent/types' |
| | | |
| | | export const isSystemVar = (valueSelector: ValueSelector) => { |
| | | return valueSelector[0] === 'sys' || valueSelector[1] === 'sys' |
| | |
| | | } as any)[type] || VarType.string |
| | | } |
| | | |
| | | const structTypeToVarType = (type: Type, isArray?: boolean): VarType => { |
| | | if (isArray) { |
| | | return ({ |
| | | [Type.string]: VarType.arrayString, |
| | | [Type.number]: VarType.arrayNumber, |
| | | [Type.object]: VarType.arrayObject, |
| | | } as any)[type] || VarType.string |
| | | } |
| | | return ({ |
| | | [Type.string]: VarType.string, |
| | | [Type.number]: VarType.number, |
| | | [Type.boolean]: VarType.boolean, |
| | | [Type.object]: VarType.object, |
| | | [Type.array]: VarType.array, |
| | | } 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<string, StructField>, filterVar: (payload: Var, selector: ValueSelector) => boolean): Record<string, StructField> => { |
| | | const res = produce(properties, (draft) => { |
| | | Object.keys(properties).forEach((key) => { |
| | | const item = properties[key] |
| | | const isObj = item.type === Type.object |
| | | const isArray = item.type === Type.array |
| | | const arrayType = item.items?.type |
| | | |
| | | if (!isObj && !filterVar({ |
| | | variable: key, |
| | | type: structTypeToVarType(isArray ? arrayType! : item.type, isArray), |
| | | }, [key])) { |
| | | delete properties[key] |
| | | return |
| | | } |
| | | if (item.type === Type.object && item.properties) |
| | | item.properties = findExceptVarInStructuredProperties(item.properties, filterVar) |
| | | }) |
| | | return draft |
| | | }) |
| | | return res |
| | | } |
| | | |
| | | const findExceptVarInStructuredOutput = (structuredOutput: StructuredOutput, filterVar: (payload: Var, selector: ValueSelector) => boolean): StructuredOutput => { |
| | | const res = produce(structuredOutput, (draft) => { |
| | | const properties = draft.schema.properties |
| | | Object.keys(properties).forEach((key) => { |
| | | const item = properties[key] |
| | | const isObj = item.type === Type.object |
| | | const isArray = item.type === Type.array |
| | | const arrayType = item.items?.type |
| | | if (!isObj && !filterVar({ |
| | | variable: key, |
| | | type: structTypeToVarType(isArray ? arrayType! : item.type, isArray), |
| | | }, [key])) { |
| | | delete properties[key] |
| | | return |
| | | } |
| | | if (item.type === Type.object && item.properties) |
| | | item.properties = findExceptVarInStructuredProperties(item.properties, filterVar) |
| | | }) |
| | | return draft |
| | | }) |
| | | return res |
| | | } |
| | | |
| | | const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: ValueSelector) => boolean, value_selector: ValueSelector, isFile?: boolean): Var => { |
| | | const { children } = obj |
| | | const isStructuredOutput = !!(children as StructuredOutput)?.schema?.properties |
| | | |
| | | let childrenResult: Var[] | StructuredOutput | undefined |
| | | |
| | | if (isStructuredOutput) { |
| | | childrenResult = findExceptVarInStructuredOutput(children, filterVar) |
| | | } |
| | | else if (Array.isArray(children)) { |
| | | childrenResult = children.filter((item: Var) => { |
| | | const { children: itemChildren } = item |
| | | const currSelector = [...value_selector, item.variable] |
| | | |
| | | if (!itemChildren) |
| | | return filterVar(item, currSelector) |
| | | |
| | | const filteredObj = findExceptVarInObject(item, filterVar, currSelector, false) // File doesn't contain file children |
| | | return filteredObj.children && (filteredObj.children as Var[])?.length > 0 |
| | | }) |
| | | } |
| | | else { |
| | | childrenResult = [] |
| | | } |
| | | |
| | | const res: Var = { |
| | | variable: obj.variable, |
| | | type: isFile ? VarType.file : VarType.object, |
| | | children: childrenResult, |
| | | } |
| | | children: 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 |
| | | }), |
| | | } |
| | | return res |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | case BlockEnum.LLM: { |
| | | res.vars = [...LLM_OUTPUT_STRUCT] |
| | | if (data.structured_output_enabled && data.structured_output?.schema?.properties && Object.keys(data.structured_output.schema.properties).length > 0) { |
| | | res.vars.push({ |
| | | variable: 'structured_output', |
| | | type: VarType.object, |
| | | children: data.structured_output, |
| | | }) |
| | | } |
| | | |
| | | res.vars = LLM_OUTPUT_STRUCT |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.KnowledgeRetrieval: { |
| | | res.vars = KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT |
| | | break |
| | |
| | | break |
| | | } |
| | | |
| | | // eslint-disable-next-line sonarjs/no-duplicated-branches |
| | | case BlockEnum.VariableAggregator: { |
| | | const { |
| | | output_type, |
| | |
| | | } |
| | | |
| | | case BlockEnum.Tool: { |
| | | const { |
| | | output_schema, |
| | | } = data as ToolNodeType |
| | | if (!output_schema) { |
| | | res.vars = TOOL_OUTPUT_STRUCT |
| | | } |
| | | else { |
| | | const outputSchema: any[] = [] |
| | | Object.keys(output_schema.properties).forEach((outputKey) => { |
| | | const output = output_schema.properties[outputKey] |
| | | const dataType = output.type |
| | | outputSchema.push({ |
| | | variable: outputKey, |
| | | type: dataType === 'array' |
| | | ? `array[${output.items?.type.slice(0, 1).toLocaleLowerCase()}${output.items?.type.slice(1)}]` |
| | | : `${output.type.slice(0, 1).toLocaleLowerCase()}${output.type.slice(1)}`, |
| | | description: output.description, |
| | | children: output.type === 'object' ? { |
| | | schema: { |
| | | type: 'object', |
| | | properties: output.properties, |
| | | }, |
| | | } : undefined, |
| | | }) |
| | | }) |
| | | res.vars = [ |
| | | ...TOOL_OUTPUT_STRUCT, |
| | | ...outputSchema, |
| | | ] |
| | | } |
| | | res.vars = TOOL_OUTPUT_STRUCT |
| | | break |
| | | } |
| | | |
| | |
| | | type: (data as IterationNodeType).output_type || VarType.arrayString, |
| | | }, |
| | | ] |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.Loop: { |
| | | const { loop_variables } = data as LoopNodeType |
| | | res.isLoop = true |
| | | res.vars = loop_variables?.map((v) => { |
| | | return { |
| | | variable: v.label, |
| | | type: v.var_type, |
| | | isLoopVariable: true, |
| | | nodeId: res.nodeId, |
| | | } |
| | | }) || [] |
| | | |
| | | break |
| | | } |
| | | |
| | |
| | | variable: 'last_record', |
| | | type: (data as ListFilterNodeType).item_var_type, |
| | | }, |
| | | ] |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.Agent: { |
| | | const payload = data as AgentNodeType |
| | | const outputs: Var[] = [] |
| | | Object.keys(payload.output_schema?.properties || {}).forEach((outputKey) => { |
| | | const output = payload.output_schema.properties[outputKey] |
| | | outputs.push({ |
| | | variable: outputKey, |
| | | type: output.type === 'array' |
| | | ? `Array[${output.items?.type.slice(0, 1).toLocaleUpperCase()}${output.items?.type.slice(1)}]` as VarType |
| | | : `${output.type.slice(0, 1).toLocaleUpperCase()}${output.type.slice(1)}` as VarType, |
| | | }) |
| | | }) |
| | | res.vars = [ |
| | | ...outputs, |
| | | ...TOOL_OUTPUT_STRUCT, |
| | | ] |
| | | break |
| | | } |
| | |
| | | res.vars = res.vars.filter((v) => { |
| | | const isCurrentMatched = filterVar(v, (() => { |
| | | const variableArr = v.variable.split('.') |
| | | const [first] = variableArr |
| | | const [first, ..._other] = variableArr |
| | | if (first === 'sys' || first === 'env' || first === 'conversation') |
| | | return variableArr |
| | | |
| | |
| | | return false |
| | | |
| | | const obj = findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector, isFile) |
| | | return obj?.children && ((obj?.children as Var[]).length > 0 || Object.keys((obj?.children as StructuredOutput)?.schema?.properties || {}).length > 0) |
| | | return obj?.children && obj?.children.length > 0 |
| | | }).map((v) => { |
| | | const isFile = v.type === VarType.file |
| | | |
| | |
| | | chatVarList: conversationVariables, |
| | | }, |
| | | } |
| | | // Sort nodes in reverse chronological order (most recent first) |
| | | const sortedNodes = [...nodes].sort((a, b) => { |
| | | if (a.data.type === BlockEnum.Start) return 1 |
| | | if (b.data.type === BlockEnum.Start) return -1 |
| | | if (a.data.type === 'env') return 1 |
| | | if (b.data.type === 'env') return -1 |
| | | if (a.data.type === 'conversation') return 1 |
| | | if (b.data.type === 'conversation') return -1 |
| | | // sort nodes by x position |
| | | return (b.position?.x || 0) - (a.position?.x || 0) |
| | | }) |
| | | |
| | | const res = [ |
| | | ...sortedNodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node?.data?.type)), |
| | | ...nodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node.data.type)), |
| | | ...(environmentVariables.length > 0 ? [ENV_NODE] : []), |
| | | ...((isChatMode && conversationVariables.length > 0) ? [CHAT_VAR_NODE] : []), |
| | | ].map((node) => { |
| | |
| | | }: { |
| | | valueSelector: ValueSelector |
| | | beforeNodesOutputVars: NodeOutPutVar[] |
| | | }): VarType => { |
| | | const outputVarNodeId = valueSelector[0] |
| | | const isSystem = isSystemVar(valueSelector) |
| | | |
| | | const targetVar = isSystem ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId) |
| | | |
| | | if (!targetVar) |
| | | return VarType.string |
| | | |
| | | let arrayType: VarType = VarType.string |
| | | |
| | | let curr: any = targetVar.vars |
| | | if (isSystem) { |
| | | arrayType = curr.find((v: any) => v.variable === (valueSelector).join('.'))?.type |
| | | } |
| | | else { |
| | | for (let i = 1; i < valueSelector.length; i++) { |
| | | const key = valueSelector[i] |
| | | const isLast = i === valueSelector.length - 1 |
| | | curr = Array.isArray(curr) ? curr.find(v => v.variable === key) : [] |
| | | |
| | | if (isLast) |
| | | arrayType = curr?.type |
| | | else if (curr?.type === VarType.object || curr?.type === VarType.file) |
| | | curr = curr.children || [] |
| | | } |
| | | } |
| | | |
| | | switch (arrayType as VarType) { |
| | | case VarType.arrayString: |
| | | return VarType.string |
| | | case VarType.arrayNumber: |
| | | return VarType.number |
| | | case VarType.arrayObject: |
| | | return VarType.object |
| | | case VarType.array: |
| | | return VarType.any |
| | | case VarType.arrayFile: |
| | | return VarType.file |
| | | default: |
| | | return VarType.string |
| | | } |
| | | } |
| | | |
| | | const getLoopItemType = ({ |
| | | valueSelector, |
| | | beforeNodesOutputVars, |
| | | }: { |
| | | valueSelector: ValueSelector |
| | | beforeNodesOutputVars: NodeOutPutVar[] |
| | | |
| | | }): VarType => { |
| | | const outputVarNodeId = valueSelector[0] |
| | | const isSystem = isSystemVar(valueSelector) |
| | |
| | | parentNode, |
| | | valueSelector, |
| | | isIterationItem, |
| | | isLoopItem, |
| | | availableNodes, |
| | | isChatMode, |
| | | isConstant, |
| | | environmentVariables = [], |
| | | conversationVariables = [], |
| | | }: { |
| | | }: |
| | | { |
| | | valueSelector: ValueSelector |
| | | parentNode?: Node | null |
| | | isIterationItem?: boolean |
| | | isLoopItem?: boolean |
| | | availableNodes: any[] |
| | | isChatMode: boolean |
| | | isConstant?: boolean |
| | |
| | | if (valueSelector[1] === 'index') |
| | | return VarType.number |
| | | } |
| | | |
| | | const isLoopInnerVar = parentNode?.data.type === BlockEnum.Loop |
| | | if (isLoopItem) { |
| | | return getLoopItemType({ |
| | | valueSelector, |
| | | beforeNodesOutputVars, |
| | | }) |
| | | } |
| | | if (isLoopInnerVar) { |
| | | if (valueSelector[1] === 'item') { |
| | | const itemType = getLoopItemType({ |
| | | valueSelector: (parentNode?.data as any).iterator_selector || [], |
| | | beforeNodesOutputVars, |
| | | }) |
| | | return itemType |
| | | } |
| | | if (valueSelector[1] === 'index') |
| | | return VarType.number |
| | | } |
| | | |
| | | const isSystem = isSystemVar(valueSelector) |
| | | const isEnv = isENV(valueSelector) |
| | | const isChatVar = isConversationVar(valueSelector) |
| | | const startNode = availableNodes.find((node: any) => { |
| | | return node?.data.type === BlockEnum.Start |
| | | return node.data.type === BlockEnum.Start |
| | | }) |
| | | |
| | | const targetVarNodeId = isSystem ? startNode?.id : valueSelector[0] |
| | |
| | | |
| | | let type: VarType = VarType.string |
| | | let curr: any = targetVar.vars |
| | | |
| | | if (isSystem || isEnv || isChatVar) { |
| | | return curr.find((v: any) => v.variable === (valueSelector as ValueSelector).join('.'))?.type |
| | | } |
| | | else { |
| | | const targetVar = curr.find((v: any) => v.variable === valueSelector[1]) |
| | | if (!targetVar) |
| | | return VarType.string |
| | | |
| | | const isStructuredOutputVar = !!targetVar.children?.schema?.properties |
| | | if (isStructuredOutputVar) { |
| | | if (valueSelector.length === 2) { // root |
| | | return VarType.object |
| | | } |
| | | let currProperties = targetVar.children.schema; |
| | | (valueSelector as ValueSelector).slice(2).forEach((key, i) => { |
| | | const isLast = i === valueSelector.length - 3 |
| | | if (!currProperties) |
| | | return |
| | | |
| | | currProperties = currProperties.properties[key] |
| | | if (isLast) |
| | | type = structTypeToVarType(currProperties?.type) |
| | | }) |
| | | return type |
| | | } |
| | | |
| | | (valueSelector as ValueSelector).slice(1).forEach((key, i) => { |
| | | const isLast = i === valueSelector.length - 2 |
| | | if (Array.isArray(curr)) |
| | |
| | | }, |
| | | ], |
| | | } |
| | | const iterationIndex = beforeNodesOutputVars.findIndex(v => v.nodeId === iterationNode?.id) |
| | | if (iterationIndex > -1) |
| | | beforeNodesOutputVars.splice(iterationIndex, 1) |
| | | beforeNodesOutputVars.unshift(iterationVar) |
| | | } |
| | | return beforeNodesOutputVars |
| | |
| | | break |
| | | } |
| | | case BlockEnum.LLM: { |
| | | const payload = data as LLMNodeType |
| | | const payload = (data as LLMNodeType) |
| | | const isChatModel = payload.model?.mode === 'chat' |
| | | let prompts: string[] = [] |
| | | if (isChatModel) { |
| | |
| | | break |
| | | } |
| | | case BlockEnum.QuestionClassifier: { |
| | | const payload = data as QuestionClassifierNodeType |
| | | const payload = (data as QuestionClassifierNodeType) |
| | | res = [payload.query_variable_selector] |
| | | const varInInstructions = matchNotSystemVars([payload.instruction || '']) |
| | | res.push(...varInInstructions) |
| | | break |
| | | } |
| | | case BlockEnum.HttpRequest: { |
| | | const payload = data as HttpNodeType |
| | | const payload = (data as HttpNodeType) |
| | | res = matchNotSystemVars([payload.url, payload.headers, payload.params, typeof payload.body.data === 'string' ? payload.body.data : payload.body.data.map(d => d.value).join('')]) |
| | | break |
| | | } |
| | | case BlockEnum.Tool: { |
| | | const payload = data as ToolNodeType |
| | | const payload = (data as ToolNodeType) |
| | | const mixVars = matchNotSystemVars(Object.keys(payload.tool_parameters)?.filter(key => payload.tool_parameters[key].type === ToolVarType.mixed).map(key => payload.tool_parameters[key].value) as string[]) |
| | | const vars = Object.keys(payload.tool_parameters).filter(key => payload.tool_parameters[key].type === ToolVarType.variable).map(key => payload.tool_parameters[key].value as string) || [] |
| | | res = [...(mixVars as ValueSelector[]), ...(vars as any)] |
| | |
| | | } |
| | | |
| | | case BlockEnum.ParameterExtractor: { |
| | | const payload = data as ParameterExtractorNodeType |
| | | const payload = (data as ParameterExtractorNodeType) |
| | | res = [payload.query] |
| | | const varInInstructions = matchNotSystemVars([payload.instruction || '']) |
| | | res.push(...varInInstructions) |
| | |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.Loop: { |
| | | const payload = data as LoopNodeType |
| | | res = payload.break_conditions?.map((c) => { |
| | | return c.variable_selector || [] |
| | | }) || [] |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.ListFilter: { |
| | | res = [(data as ListFilterNodeType).variable] |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.Agent: { |
| | | const payload = data as AgentNodeType |
| | | const valueSelectors: ValueSelector[] = [] |
| | | if (!payload.agent_parameters) |
| | | break |
| | | |
| | | Object.keys(payload.agent_parameters || {}).forEach((key) => { |
| | | const { value } = payload.agent_parameters![key] |
| | | if (typeof value === 'string') |
| | | valueSelectors.push(...matchNotSystemVars([value])) |
| | | }) |
| | | res = valueSelectors |
| | | break |
| | | } |
| | | } |
| | |
| | | let res: string | string[] = '' |
| | | switch (type) { |
| | | case BlockEnum.LLM: { |
| | | const payload = data as LLMNodeType |
| | | const payload = (data as LLMNodeType) |
| | | res = [`#${valueSelector.join('.')}#`] |
| | | if (payload.context?.variable_selector.join('.') === valueSelector.join('.')) |
| | | res.push('#context#') |
| | |
| | | } |
| | | break |
| | | } |
| | | // eslint-disable-next-line sonarjs/no-duplicated-branches |
| | | case BlockEnum.VariableAggregator: { |
| | | const payload = data as VariableAssignerNodeType |
| | | if (payload.variables) { |
| | |
| | | |
| | | break |
| | | } |
| | | case BlockEnum.Loop: { |
| | | const payload = data as LoopNodeType |
| | | if (payload.break_conditions) { |
| | | payload.break_conditions = payload.break_conditions.map((c) => { |
| | | if (c.variable_selector?.join('.') === oldVarSelector.join('.')) |
| | | c.variable_selector = newVarSelector |
| | | return c |
| | | }) |
| | | } |
| | | break |
| | | } |
| | | case BlockEnum.ListFilter: { |
| | | const payload = data as ListFilterNodeType |
| | | if (payload.variable.join('.') === oldVarSelector.join('.')) |
| | |
| | | }) |
| | | return newNode |
| | | } |
| | | |
| | | const varToValueSelectorList = (v: Var, parentValueSelector: ValueSelector, res: ValueSelector[]) => { |
| | | if (!v.variable) |
| | | return |
| | | |
| | | res.push([...parentValueSelector, v.variable]) |
| | | const isStructuredOutput = !!(v.children as StructuredOutput)?.schema?.properties |
| | | |
| | | if ((v.children as Var[])?.length > 0) { |
| | | (v.children as Var[]).forEach((child) => { |
| | | if (v.children && v.children.length > 0) { |
| | | v.children.forEach((child) => { |
| | | varToValueSelectorList(child, [...parentValueSelector, v.variable], res) |
| | | }) |
| | | } |
| | | if (isStructuredOutput) { |
| | | Object.keys((v.children as StructuredOutput)?.schema?.properties || {}).forEach((key) => { |
| | | const type = (v.children as StructuredOutput)?.schema?.properties[key].type |
| | | const isArray = type === Type.array |
| | | const arrayType = (v.children as StructuredOutput)?.schema?.properties[key].items?.type |
| | | varToValueSelectorList({ |
| | | variable: key, |
| | | type: structTypeToVarType(isArray ? arrayType! : type, isArray), |
| | | }, [...parentValueSelector, v.variable], res) |
| | | }) |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | case BlockEnum.LLM: { |
| | | const vars = [...LLM_OUTPUT_STRUCT] |
| | | const llmNodeData = data as LLMNodeType |
| | | if (llmNodeData.structured_output_enabled && llmNodeData.structured_output?.schema?.properties && Object.keys(llmNodeData.structured_output.schema.properties).length > 0) { |
| | | vars.push({ |
| | | variable: 'structured_output', |
| | | type: VarType.object, |
| | | children: llmNodeData.structured_output, |
| | | }) |
| | | } |
| | | varsToValueSelectorList(vars, [id], res) |
| | | varsToValueSelectorList(LLM_OUTPUT_STRUCT, [id], res) |
| | | break |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | case BlockEnum.Iteration: { |
| | | res.push([id, 'output']) |
| | | break |
| | | } |
| | | |
| | | case BlockEnum.Loop: { |
| | | res.push([id, 'output']) |
| | | break |
| | | } |