Commit c3dd66e9 by Archer Committed by GitHub

fix: clear reference values in node debug (#6915)

* fix: clear reference values in node debug

* doc

* fix: align invalid condition input type

* fix: support boolean values in BoolSchema

* update lock
parent 8788ac04
---
title: 'V4.15.0-beta2(进行中)'
description: 'FastGPT V4.15.0-beta2 更新说明'
---
## 🚀 新增内容
## ⚙️ 优化
1. 优化 OTEL 日志采集格式。
2. 增加 workflow zod 检查鲁棒性。
## 🐛 修复
1. 工作流,单节点调试,存在异常默认值。
## 代码优化
{ {
"title": "4.15.x", "title": "4.15.x",
"description": "", "description": "",
"pages": ["4150"] "pages": ["41502", "4150"]
} }
{ {
"title": "4.15.x", "title": "4.15.x",
"description": "", "description": "",
"pages": ["4150"] "pages": ["41502", "4150"]
} }
...@@ -140,6 +140,7 @@ description: FastGPT 文档目录 ...@@ -140,6 +140,7 @@ description: FastGPT 文档目录
- [/self-host/upgrading/4-14/41481](/self-host/upgrading/4-14/41481) - [/self-host/upgrading/4-14/41481](/self-host/upgrading/4-14/41481)
- [/self-host/upgrading/4-14/4149](/self-host/upgrading/4-14/4149) - [/self-host/upgrading/4-14/4149](/self-host/upgrading/4-14/4149)
- [/self-host/upgrading/4-15/4150](/self-host/upgrading/4-15/4150) - [/self-host/upgrading/4-15/4150](/self-host/upgrading/4-15/4150)
- [/self-host/upgrading/4-15/41502](/self-host/upgrading/4-15/41502)
- [/self-host/upgrading/outdated/40](/self-host/upgrading/outdated/40) - [/self-host/upgrading/outdated/40](/self-host/upgrading/outdated/40)
- [/self-host/upgrading/outdated/41](/self-host/upgrading/outdated/41) - [/self-host/upgrading/outdated/41](/self-host/upgrading/outdated/41)
- [/self-host/upgrading/outdated/4100](/self-host/upgrading/outdated/4100) - [/self-host/upgrading/outdated/4100](/self-host/upgrading/outdated/4100)
......
...@@ -271,7 +271,7 @@ ...@@ -271,7 +271,7 @@
"content/self-host/upgrading/4-14/41481.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/4-14/41481.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4149.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/4-14/4149.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4149.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/4-14/4149.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-15/4150.mdx": "2026-05-09T14:27:39+08:00", "content/self-host/upgrading/4-15/4150.mdx": "2026-05-09T16:13:01+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
"fumadocs-ui": "15.6.3", "fumadocs-ui": "15.6.3",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"lucide-react": "^0.525.0", "lucide-react": "^0.525.0",
"next": "^15.5.15", "next": "^15.5.18",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-responsive": "^10.0.1", "react-responsive": "^10.0.1",
......
import z from 'zod'; import z from 'zod';
import { stripUrlTrailingSlash } from '../string/url'; import { stripUrlTrailingSlash } from '../string/url';
const truthyBoolStrs = ['true', '1', 'yes', 'y']; const truthyBoolStrs = ['true', '1', 'yes', 'y', 'on'];
export const BoolSchema = z export const BoolSchema = z.preprocess((val) => {
.string() if (typeof val === 'boolean') return val;
.transform((val) => truthyBoolStrs.includes(val.toLowerCase()))
.pipe(z.boolean()); if (typeof val === 'string') {
return truthyBoolStrs.includes(val.trim().toLowerCase());
}
if (typeof val === 'number') {
if (val === 1) return true;
if (val === 0) return false;
}
return val;
}, z.boolean());
export const NumSchema = z.coerce.number<number>(); export const NumSchema = z.coerce.number<number>();
export const IntSchema = NumSchema.int().nonnegative(); export const IntSchema = NumSchema.int().nonnegative();
......
...@@ -3,6 +3,7 @@ import { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '.. ...@@ -3,6 +3,7 @@ import { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '..
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant'; import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant';
import { SecretValueTypeSchema } from '../../../common/secret/type'; import { SecretValueTypeSchema } from '../../../common/secret/type';
import z from 'zod'; import z from 'zod';
import { BoolSchema, IntSchema, NumSchema } from '../../../common/zod';
/* Dataset node */ /* Dataset node */
export const SelectedDatasetSchema = z.object({ export const SelectedDatasetSchema = z.object({
...@@ -19,9 +20,9 @@ export type SelectedDatasetType = z.infer<typeof SelectedDatasetSchema>; ...@@ -19,9 +20,9 @@ export type SelectedDatasetType = z.infer<typeof SelectedDatasetSchema>;
export const CustomFieldConfigTypeSchema = z.object({ export const CustomFieldConfigTypeSchema = z.object({
// reference // reference
selectValueTypeList: z.array(z.enum(WorkflowIOValueTypeEnum)).optional(), // 可以选哪个数据类型, 只有1个的话,则默认选择 selectValueTypeList: z.array(z.enum(WorkflowIOValueTypeEnum)).optional(), // 可以选哪个数据类型, 只有1个的话,则默认选择
showDefaultValue: z.boolean().optional(), showDefaultValue: BoolSchema.optional(),
showDescription: z.boolean().optional(), showDescription: BoolSchema.optional(),
hideBottomDivider: z.boolean().optional() hideBottomDivider: BoolSchema.optional()
}); });
export type CustomFieldConfigType = z.infer<typeof CustomFieldConfigTypeSchema>; export type CustomFieldConfigType = z.infer<typeof CustomFieldConfigTypeSchema>;
...@@ -30,15 +31,15 @@ export const InputComponentPropsTypeSchema = z.object({ ...@@ -30,15 +31,15 @@ export const InputComponentPropsTypeSchema = z.object({
label: z.string(), label: z.string(),
valueType: z.enum(WorkflowIOValueTypeEnum).optional(), valueType: z.enum(WorkflowIOValueTypeEnum).optional(),
required: z.boolean().optional(), required: BoolSchema.optional(),
defaultValue: z.any().optional(), defaultValue: z.any().optional(),
// 不同组件的配置嘻嘻 // 不同组件的配置嘻嘻
referencePlaceholder: z.string().optional(), referencePlaceholder: z.string().optional(),
isRichText: z.boolean().optional(), // Prompt editor isRichText: BoolSchema.optional(), // Prompt editor
placeholder: z.string().optional(), // input,textarea placeholder: z.string().optional(), // input,textarea
maxLength: z.number().optional(), // input,textarea maxLength: IntSchema.optional(), // input,textarea
minLength: z.number().optional(), // password minLength: IntSchema.optional(), // password
list: z list: z
.array( .array(
z.object({ z.object({
...@@ -49,21 +50,21 @@ export const InputComponentPropsTypeSchema = z.object({ ...@@ -49,21 +50,21 @@ export const InputComponentPropsTypeSchema = z.object({
}) })
) )
.optional(), // select .optional(), // select
markList: z.array(z.object({ label: z.string(), value: z.number() })).optional(), // slider markList: z.array(z.object({ label: z.string(), value: NumSchema })).optional(), // slider
step: z.number().optional(), // slider step: NumSchema.optional(), // slider
max: z.number().optional(), // slider, number input max: NumSchema.optional(), // slider, number input
min: z.number().optional(), // slider, number input min: NumSchema.optional(), // slider, number input
precision: z.number().optional(), // number input precision: NumSchema.optional(), // number input
canSelectFile: z.boolean().optional(), // file select canSelectFile: BoolSchema.optional(), // file select
canSelectImg: z.boolean().optional(), // file select canSelectImg: BoolSchema.optional(), // file select
canSelectVideo: z.boolean().optional(), // file select canSelectVideo: BoolSchema.optional(), // file select
canSelectAudio: z.boolean().optional(), // file select canSelectAudio: BoolSchema.optional(), // file select
canSelectCustomFileExtension: z.boolean().optional(), // file select canSelectCustomFileExtension: BoolSchema.optional(), // file select
customFileExtensionList: z.array(z.string()).optional(), // file select customFileExtensionList: z.array(z.string()).optional(), // file select
canLocalUpload: z.boolean().optional(), // file select canLocalUpload: BoolSchema.optional(), // file select
canUrlUpload: z.boolean().optional(), // file select canUrlUpload: BoolSchema.optional(), // file select
maxFiles: z.number().optional(), // file select maxFiles: IntSchema.optional(), // file select
// Time // Time
timeGranularity: z.enum(['day', 'hour', 'minute', 'second']).optional(), // time point select, time range select timeGranularity: z.enum(['day', 'hour', 'minute', 'second']).optional(), // time point select, time range select
...@@ -86,7 +87,7 @@ export const InputConfigTypeSchema = z.object({ ...@@ -86,7 +87,7 @@ export const InputConfigTypeSchema = z.object({
key: z.string(), key: z.string(),
label: z.string(), label: z.string(),
description: z.string().optional(), description: z.string().optional(),
required: z.boolean().optional(), required: BoolSchema.optional(),
inputType: z.enum(['input', 'numberInput', 'secret', 'switch', 'select']), inputType: z.enum(['input', 'numberInput', 'secret', 'switch', 'select']),
value: SecretValueTypeSchema.optional(), value: SecretValueTypeSchema.optional(),
...@@ -97,7 +98,7 @@ export type InputConfigType = z.infer<typeof InputConfigTypeSchema>; ...@@ -97,7 +98,7 @@ export type InputConfigType = z.infer<typeof InputConfigTypeSchema>;
// Workflow node input // Workflow node input
export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend({ export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend({
selectedTypeIndex: z.number().optional(), selectedTypeIndex: IntSchema.optional(),
renderTypeList: z.array(z.enum(FlowNodeInputTypeEnum)), // Node Type. Decide on a render style renderTypeList: z.array(z.enum(FlowNodeInputTypeEnum)), // Node Type. Decide on a render style
valueDesc: z.string().optional(), // data desc valueDesc: z.string().optional(), // data desc
value: z.any().optional(), value: z.any().optional(),
...@@ -111,11 +112,11 @@ export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend( ...@@ -111,11 +112,11 @@ export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend(
inputList: z.array(InputConfigTypeSchema).optional(), // when key === 'system_input_config', this field is used inputList: z.array(InputConfigTypeSchema).optional(), // when key === 'system_input_config', this field is used
// render components params // render components params
canEdit: z.boolean().optional(), // dynamic inputs canEdit: BoolSchema.optional(), // dynamic inputs
isPro: z.boolean().optional(), // Pro version field isPro: BoolSchema.optional(), // Pro version field
isToolOutput: z.boolean().optional(), isToolOutput: BoolSchema.optional(),
deprecated: z.boolean().optional() // node deprecated deprecated: BoolSchema.optional() // node deprecated
}); });
export type FlowNodeInputItemType = z.infer<typeof FlowNodeInputItemTypeSchema>; export type FlowNodeInputItemType = z.infer<typeof FlowNodeInputItemTypeSchema>;
...@@ -131,18 +132,18 @@ export const FlowNodeOutputItemTypeSchema = z.object({ ...@@ -131,18 +132,18 @@ export const FlowNodeOutputItemTypeSchema = z.object({
label: z.string().optional(), label: z.string().optional(),
description: z.string().optional(), description: z.string().optional(),
defaultValue: z.any().optional(), defaultValue: z.any().optional(),
required: z.boolean().optional(), required: BoolSchema.optional(),
invalid: z.boolean().optional(), invalid: BoolSchema.optional(),
invalidCondition: z invalidCondition: z
.function({ .function({
input: z.tuple([ input: z.tuple([
z.object({ z.object({
inputs: z.array(FlowNodeInputItemTypeSchema), inputs: z.custom<FlowNodeInputItemType[]>(),
llmModelMap: z.record(z.string(), LLMModelItemSchema) llmModelMap: z.record(z.string(), LLMModelItemSchema)
}) })
]), ]),
output: z.boolean() output: BoolSchema
}) })
.optional() .optional()
.meta({ .meta({
...@@ -153,7 +154,7 @@ export const FlowNodeOutputItemTypeSchema = z.object({ ...@@ -153,7 +154,7 @@ export const FlowNodeOutputItemTypeSchema = z.object({
}), }),
customFieldConfig: CustomFieldConfigTypeSchema.optional(), customFieldConfig: CustomFieldConfigTypeSchema.optional(),
deprecated: z.boolean().optional() deprecated: BoolSchema.optional()
}); });
export type FlowNodeOutputItemType = z.infer<typeof FlowNodeOutputItemTypeSchema>; export type FlowNodeOutputItemType = z.infer<typeof FlowNodeOutputItemTypeSchema>;
......
import { describe, expect, it } from 'vitest';
import { BoolSchema } from '@fastgpt/global/common/zod';
import {
FlowNodeInputItemTypeSchema,
FlowNodeOutputItemTypeSchema
} from '@fastgpt/global/core/workflow/type/io';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
describe('BoolSchema', () => {
it('should accept boolean values directly', () => {
expect(BoolSchema.parse(true)).toBe(true);
expect(BoolSchema.parse(false)).toBe(false);
});
it('should convert common truthy string values to true', () => {
['true', '1', 'yes', 'y', 'on', ' TRUE '].forEach((value) => {
expect(BoolSchema.parse(value)).toBe(true);
});
});
it('should convert other string values to false', () => {
['false', '0', 'no', 'n', 'off', '', 'random'].forEach((value) => {
expect(BoolSchema.parse(value)).toBe(false);
});
});
it('should convert numeric 0 and 1 values', () => {
expect(BoolSchema.parse(1)).toBe(true);
expect(BoolSchema.parse(0)).toBe(false);
expect(BoolSchema.safeParse(2).success).toBe(false);
});
it('should keep workflow io boolean fields compatible with runtime objects', () => {
expect(
FlowNodeInputItemTypeSchema.parse({
key: 'input',
label: 'Input',
renderTypeList: [FlowNodeInputTypeEnum.input],
valueType: WorkflowIOValueTypeEnum.string,
required: true
}).required
).toBe(true);
const output = FlowNodeOutputItemTypeSchema.parse({
id: 'output',
key: 'output',
type: FlowNodeOutputTypeEnum.static,
required: true,
invalid: false,
invalidCondition: () => true
});
expect(output.required).toBe(true);
expect(output.invalid).toBe(false);
expect(output.invalidCondition?.({ inputs: [], llmModelMap: {} })).toBe(true);
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
Subproject commit 91c9337cb64290f7fbaf68939545e432ddacbc7a Subproject commit 78d6cc129c75f2fc123b92f5016af5edb632ad14
...@@ -6,7 +6,6 @@ import { ...@@ -6,7 +6,6 @@ import {
} from '@fastgpt/global/core/workflow/type/edge'; } from '@fastgpt/global/core/workflow/type/edge';
import { useCallback, useState, useMemo } from 'react'; import { useCallback, useState, useMemo } from 'react';
import { checkWorkflowNodeAndConnection } from '@/web/core/workflow/utils'; import { checkWorkflowNodeAndConnection } from '@/web/core/workflow/utils';
import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { uiWorkflow2StoreWorkflow } from '../../utils'; import { uiWorkflow2StoreWorkflow } from '../../utils';
import { type RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type'; import { type RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
...@@ -14,10 +13,7 @@ import { type RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/ ...@@ -14,10 +13,7 @@ import { type RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { Box, Button, Flex } from '@chakra-ui/react'; import { Box, Button, Flex } from '@chakra-ui/react';
import { type FieldErrors, useForm } from 'react-hook-form'; import { type FieldErrors, useForm } from 'react-hook-form';
import { import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
VariableInputEnum,
WorkflowIOValueTypeEnum
} from '@fastgpt/global/core/workflow/constants';
import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils'; import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
...@@ -30,9 +26,13 @@ import { ...@@ -30,9 +26,13 @@ import {
variableInputTypeToInputType variableInputTypeToInputType
} from '@/components/core/app/formRender/utils'; } from '@/components/core/app/formRender/utils';
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation'; import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
import { WorkflowUtilsContext } from '../../context/workflowUtilsContext';
import { WorkflowActionsContext } from '../../context/workflowActionsContext'; import { WorkflowActionsContext } from '../../context/workflowActionsContext';
import { WorkflowDebugContext } from '../../context/workflowDebugContext'; import { WorkflowDebugContext } from '../../context/workflowDebugContext';
import {
getDebugInputFormProps,
getDebugInputFormValue,
getDebugRuntimeInputs
} from './useDebugInput';
const MyRightDrawer = dynamic( const MyRightDrawer = dynamic(
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer') () => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
...@@ -159,15 +159,7 @@ export const useDebug = () => { ...@@ -159,15 +159,7 @@ export const useDebug = () => {
const variablesForm = useForm<Record<string, any>>({ const variablesForm = useForm<Record<string, any>>({
defaultValues: { defaultValues: {
nodeVariables: renderInputs.reduce((acc: Record<string, any>, input) => { nodeVariables: renderInputs.reduce((acc: Record<string, any>, input) => {
const isReference = checkInputIsReference(input); acc[input.key] = getDebugInputFormValue(input);
if (isReference) {
acc[input.key] = undefined;
} else if (typeof input.value === 'object') {
acc[input.key] = JSON.stringify(input.value, null, 2);
} else {
acc[input.key] = input.value;
}
return acc; return acc;
}, {}), }, {}),
variables: defaultGlobalVariables variables: defaultGlobalVariables
...@@ -188,27 +180,9 @@ export const useDebug = () => { ...@@ -188,27 +180,9 @@ export const useDebug = () => {
node.nodeId === runtimeNode.nodeId node.nodeId === runtimeNode.nodeId
? { ? {
...runtimeNode, ...runtimeNode,
inputs: runtimeNode.inputs.map((input) => { inputs: getDebugRuntimeInputs({
let parseValue = (() => { inputs: runtimeNode.inputs,
try { nodeVariables: data.nodeVariables
if (
input.valueType === WorkflowIOValueTypeEnum.string ||
input.valueType === WorkflowIOValueTypeEnum.number ||
input.valueType === WorkflowIOValueTypeEnum.boolean
) {
return data.nodeVariables[input.key];
}
return JSON.parse(data.nodeVariables[input.key]);
} catch (e) {
return data.nodeVariables[input.key];
}
})();
return {
...input,
value: parseValue ?? input.value
};
}) })
} }
: node : node
...@@ -263,9 +237,12 @@ export const useDebug = () => { ...@@ -263,9 +237,12 @@ export const useDebug = () => {
/> />
)} )}
<Box display={currentTab === TabEnum.node ? 'block' : 'none'}> <Box display={currentTab === TabEnum.node ? 'block' : 'none'}>
{renderInputs.map((item) => ( {renderInputs.map((item) => {
const inputProps = getDebugInputFormProps(item);
return (
<LabelAndFormRender <LabelAndFormRender
{...item} {...inputProps}
key={item.key} key={item.key}
label={item.label} label={item.label}
required={item.required} required={item.required}
...@@ -275,7 +252,8 @@ export const useDebug = () => { ...@@ -275,7 +252,8 @@ export const useDebug = () => {
fieldName={`nodeVariables.${item.key}`} fieldName={`nodeVariables.${item.key}`}
bg={'myGray.50'} bg={'myGray.50'}
/> />
))} );
})}
</Box> </Box>
<Box display={currentTab === TabEnum.global ? 'block' : 'none'}> <Box display={currentTab === TabEnum.global ? 'block' : 'none'}>
{customVar.map((item) => ( {customVar.map((item) => (
......
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils';
import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
const primitiveValueTypes = new Set<WorkflowIOValueTypeEnum>([
WorkflowIOValueTypeEnum.string,
WorkflowIOValueTypeEnum.number,
WorkflowIOValueTypeEnum.boolean
]);
export const getDebugInputFormValue = (input: FlowNodeInputItemType) => {
if (checkInputIsReference(input)) return undefined;
const value = input.value ?? input.defaultValue;
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value, null, 2);
}
return value;
};
export const getDebugInputFormProps = (input: FlowNodeInputItemType) => {
const props = { ...input };
delete props.value;
delete props.defaultValue;
return props;
};
const parseDebugInputFormValue = (input: FlowNodeInputItemType, value: any) => {
if (primitiveValueTypes.has(input.valueType as WorkflowIOValueTypeEnum)) {
return value;
}
try {
return JSON.parse(value);
} catch {
return value;
}
};
export const getDebugRuntimeInputs = ({
inputs,
nodeVariables = {}
}: {
inputs: FlowNodeInputItemType[];
nodeVariables?: Record<string, any>;
}) => {
return inputs.map((input) => {
if (!Object.prototype.hasOwnProperty.call(nodeVariables, input.key)) {
return input;
}
return {
...input,
value: parseDebugInputFormValue(input, nodeVariables[input.key])
};
});
};
import { describe, expect, it } from 'vitest';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
import {
getDebugInputFormProps,
getDebugInputFormValue,
getDebugRuntimeInputs
} from '@/pageComponents/app/detail/WorkflowComponents/Flow/hooks/useDebugInput';
const makeInput = (input: Partial<FlowNodeInputItemType>): FlowNodeInputItemType => ({
key: 'input',
label: 'Input',
renderTypeList: [FlowNodeInputTypeEnum.input],
valueType: WorkflowIOValueTypeEnum.string,
...input
});
describe('useDebugInput', () => {
it('should not use reference value as node debug form default value', () => {
const input = makeInput({
key: 'userChatInput',
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
selectedTypeIndex: 0,
value: [['workflowStart', 'userChatInput']]
});
expect(getDebugInputFormValue(input)).toBeUndefined();
});
it('should remove raw value props before rendering debug form fields', () => {
const input = makeInput({
value: [['workflowStart', 'userChatInput']],
defaultValue: 'default'
});
const props = getDebugInputFormProps(input);
expect(props).not.toHaveProperty('value');
expect(props).not.toHaveProperty('defaultValue');
});
it('should clear old reference value when a rendered debug field is submitted empty', () => {
const referenceInput = makeInput({
key: 'userChatInput',
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
selectedTypeIndex: 0,
value: [['workflowStart', 'userChatInput']]
});
const [updatedInput] = getDebugRuntimeInputs({
inputs: [referenceInput],
nodeVariables: {
userChatInput: undefined
}
});
expect(updatedInput.value).toBeUndefined();
});
it('should keep inputs that are not shown in the debug form unchanged', () => {
const hiddenInput = makeInput({
key: 'temperature',
valueType: WorkflowIOValueTypeEnum.number,
value: 0.7
});
const [updatedInput] = getDebugRuntimeInputs({
inputs: [hiddenInput],
nodeVariables: {}
});
expect(updatedInput).toBe(hiddenInput);
});
it('should parse json values from debug form', () => {
const objectInput = makeInput({
key: 'config',
valueType: WorkflowIOValueTypeEnum.object,
value: { old: true }
});
const [updatedInput] = getDebugRuntimeInputs({
inputs: [objectInput],
nodeVariables: {
config: '{"new":true}'
}
});
expect(updatedInput.value).toEqual({ new: true });
});
});
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "catalog:", "dayjs": "catalog:",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"hono": "^4.7.6", "hono": "^4.12.18",
"lodash": "catalog:", "lodash": "catalog:",
"moment": "^2.30.1", "moment": "^2.30.1",
"qs": "^6.13.1", "qs": "^6.13.1",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment