From 8161abe6a3ca4eb2b56f9055d6a8d47fd60673bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Thu, 19 Mar 2026 14:00:10 +0800 Subject: [PATCH 1/7] feat: Variable aggregation supports outputting dictionary types. --- .../i_variable_aggregation_node.py | 1 + .../impl/base_variable_aggregation_node.py | 16 +++++++++++---- .../execution-detail-card/index.vue | 8 +++++--- ui/src/locales/lang/en-US/workflow.ts | 4 +++- ui/src/locales/lang/zh-CN/workflow.ts | 4 +++- ui/src/locales/lang/zh-Hant/workflow.ts | 4 +++- .../nodes/variable-aggregation-node/index.ts | 8 +++++++- .../nodes/variable-aggregation-node/index.vue | 20 ++++++++++++++++--- 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py index 4878f0c4c0c..34d6f321eba 100644 --- a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py +++ b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py @@ -11,6 +11,7 @@ class VariableListSerializer(serializers.Serializer): v_id = serializers.CharField(required=True, label=_("Variable id")) + key = serializers.CharField(required=False, label=_("Key")) variable = serializers.ListField(required=True, label=_("Variable")) diff --git a/apps/application/flow/step_node/variable_aggregation_node/impl/base_variable_aggregation_node.py b/apps/application/flow/step_node/variable_aggregation_node/impl/base_variable_aggregation_node.py index e3390dfc2b9..fa1d6479e5c 100644 --- a/apps/application/flow/step_node/variable_aggregation_node/impl/base_variable_aggregation_node.py +++ b/apps/application/flow/step_node/variable_aggregation_node/impl/base_variable_aggregation_node.py @@ -31,7 +31,6 @@ def save_context(self, details, workflow_manage): self.context['exception_message'] = details.get('err_message') def get_first_non_null(self, variable_list): - for variable in variable_list: v = self.workflow_manage.get_reference_field( variable.get('variable')[0], @@ -40,12 +39,16 @@ def get_first_non_null(self, variable_list): return v return None - def set_variable_to_json(self, variable_list): - + def set_variable_to_array(self, variable_list): return [self.workflow_manage.get_reference_field( variable.get('variable')[0], variable.get('variable')[1:]) for variable in variable_list] + def set_variable_to_dict(self, variable_list): + return {(variable.get('key') or variable.get('variable')[-1]): self.workflow_manage.get_reference_field( + variable.get('variable')[0], + variable.get('variable')[1:]) for variable in variable_list} + def reset_variable(self, variable): value = self.workflow_manage.get_reference_field( variable.get('variable')[0], @@ -65,9 +68,14 @@ def reset_group_list(self, group_list): def execute(self, strategy, group_list, **kwargs) -> NodeResult: strategy_map = {'first_non_null': self.get_first_non_null, - 'variable_to_json': self.set_variable_to_json, + 'variable_to_array': self.set_variable_to_array, + 'variable_to_dict': self.set_variable_to_dict, } + # 向下兼容 + if strategy == 'variable_to_json': + strategy = 'variable_to_array' + result = {item.get('field'): strategy_map[strategy](item.get('variable_list')) for item in group_list} return NodeResult( diff --git a/ui/src/components/execution-detail-card/index.vue b/ui/src/components/execution-detail-card/index.vue index 04b1e89eeed..3c1a9976de2 100644 --- a/ui/src/components/execution-detail-card/index.vue +++ b/ui/src/components/execution-detail-card/index.vue @@ -936,9 +936,11 @@
{{ - data.strategy === 'variable_to_json' - ? t('workflow.nodes.variableAggregationNode.placeholder1') - : t('workflow.nodes.variableAggregationNode.placeholder') + data.strategy === 'first_non_null' + ? t('workflow.nodes.variableAggregationNode.placeholder') + : data.strategy === 'variable_to_dict' + ? t('workflow.nodes.variableAggregationNode.placeholder2') + : t('workflow.nodes.variableAggregationNode.placeholder1') }}
diff --git a/ui/src/locales/lang/en-US/workflow.ts b/ui/src/locales/lang/en-US/workflow.ts index 939706357f9..486e728c909 100644 --- a/ui/src/locales/lang/en-US/workflow.ts +++ b/ui/src/locales/lang/en-US/workflow.ts @@ -323,7 +323,9 @@ You are a master of problem optimization, adept at accurately inferring user int text: 'Aggregate variables of each group according to the aggregation strategy', Strategy: 'Aggregation Strategy', placeholder: 'Return the first non-null value of each group', - placeholder1: 'Return the set of variables for each group', + placeholder1: 'Return the array of variables for each group', + placeholder2: 'Return the dict of variables for each group', + placeholder_key: 'Input key', group: { noneError: 'Name cannot be empty', dupError: 'Name cannot be duplicated', diff --git a/ui/src/locales/lang/zh-CN/workflow.ts b/ui/src/locales/lang/zh-CN/workflow.ts index 79164f83bff..3cea08aa567 100644 --- a/ui/src/locales/lang/zh-CN/workflow.ts +++ b/ui/src/locales/lang/zh-CN/workflow.ts @@ -304,7 +304,9 @@ export default { text: '按聚合策略聚合每组的变量', Strategy: '聚合策略', placeholder: '返回每组的第一个非空值', - placeholder1: '返回每组变量的集合', + placeholder1: '返回每组变量的数组(Array)', + placeholder2: '返回每组变量的字典(Dict)', + placeholder_key: '输入键名', group: { noneError: '名称不能为空', dupError: '名称不能重复', diff --git a/ui/src/locales/lang/zh-Hant/workflow.ts b/ui/src/locales/lang/zh-Hant/workflow.ts index 326b8c5e863..e962ce471ad 100644 --- a/ui/src/locales/lang/zh-Hant/workflow.ts +++ b/ui/src/locales/lang/zh-Hant/workflow.ts @@ -322,7 +322,9 @@ export default { text: '按聚合策略聚合每組的變量', Strategy: '聚合策略', placeholder: '返回每組的第一個非空值', - placeholder1: '返回每組變量的集合', + placeholder1: '返回每組變量的數組(Array)', + placeholder2: '返回每組變量的字典(Dict)', + placeholder_key: '輸入鍵名', group: { noneError: '名稱不能為空', dupError: '名稱不能重複', diff --git a/ui/src/workflow/nodes/variable-aggregation-node/index.ts b/ui/src/workflow/nodes/variable-aggregation-node/index.ts index 1f2c433de60..3cddd2c9734 100644 --- a/ui/src/workflow/nodes/variable-aggregation-node/index.ts +++ b/ui/src/workflow/nodes/variable-aggregation-node/index.ts @@ -10,8 +10,14 @@ class VariableAggregationNode extends AppNode { } } +class VariableAggregationNodeModel extends AppNodeModel { + get_width() { + return 450 + } +} + export default { type: 'variable-aggregation-node', - model: AppNodeModel, + model: VariableAggregationNodeModel, view: VariableAggregationNode, } diff --git a/ui/src/workflow/nodes/variable-aggregation-node/index.vue b/ui/src/workflow/nodes/variable-aggregation-node/index.vue index c40a7e4aa7b..077b198fc20 100644 --- a/ui/src/workflow/nodes/variable-aggregation-node/index.vue +++ b/ui/src/workflow/nodes/variable-aggregation-node/index.vue @@ -34,7 +34,11 @@ /> + @@ -77,10 +81,17 @@ trigger: 'change', }" > + @@ -149,7 +160,10 @@ const form = { const form_data = computed({ get: () => { if (props.nodeModel.properties.node_data) { - return props.nodeModel.properties.node_data + // 向下兼容 + if (props.nodeModel.properties.node_data.strategy === 'variable_to_json') { + props.nodeModel.properties.node_data.strategy = 'variable_to_array' + } } else { set(props.nodeModel.properties, 'node_data', form) } From aac993be68200a7027b6d2d33f77a5d523ab3b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Thu, 19 Mar 2026 14:24:35 +0800 Subject: [PATCH 2/7] fix placeholder_key --- ui/src/locales/lang/en-US/workflow.ts | 2 +- ui/src/locales/lang/zh-CN/workflow.ts | 2 +- ui/src/locales/lang/zh-Hant/workflow.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/locales/lang/en-US/workflow.ts b/ui/src/locales/lang/en-US/workflow.ts index 486e728c909..74fa49f04b4 100644 --- a/ui/src/locales/lang/en-US/workflow.ts +++ b/ui/src/locales/lang/en-US/workflow.ts @@ -61,6 +61,7 @@ export default { ReferencingRequired: 'Referenced variable is required', ReferencingError: 'Invalid referenced variable', NoReferencing: 'Referenced variable does not exist', + placeholder_key: 'Enter key', placeholder: 'Please select a variable', inputPlaceholder: 'Please enter variable', loop: 'Loop Variable', @@ -325,7 +326,6 @@ You are a master of problem optimization, adept at accurately inferring user int placeholder: 'Return the first non-null value of each group', placeholder1: 'Return the array of variables for each group', placeholder2: 'Return the dict of variables for each group', - placeholder_key: 'Input key', group: { noneError: 'Name cannot be empty', dupError: 'Name cannot be duplicated', diff --git a/ui/src/locales/lang/zh-CN/workflow.ts b/ui/src/locales/lang/zh-CN/workflow.ts index 3cea08aa567..0f069ddeccd 100644 --- a/ui/src/locales/lang/zh-CN/workflow.ts +++ b/ui/src/locales/lang/zh-CN/workflow.ts @@ -60,6 +60,7 @@ export default { ReferencingRequired: '引用变量必填', ReferencingError: '引用变量错误', NoReferencing: '不存在的引用变量', + placeholder_key: '请输入键名', placeholder: '请选择变量', inputPlaceholder: '请输入变量', loop: '循环变量', @@ -306,7 +307,6 @@ export default { placeholder: '返回每组的第一个非空值', placeholder1: '返回每组变量的数组(Array)', placeholder2: '返回每组变量的字典(Dict)', - placeholder_key: '输入键名', group: { noneError: '名称不能为空', dupError: '名称不能重复', diff --git a/ui/src/locales/lang/zh-Hant/workflow.ts b/ui/src/locales/lang/zh-Hant/workflow.ts index e962ce471ad..25d844d783f 100644 --- a/ui/src/locales/lang/zh-Hant/workflow.ts +++ b/ui/src/locales/lang/zh-Hant/workflow.ts @@ -60,6 +60,7 @@ export default { ReferencingRequired: '引用變量必填', ReferencingError: '引用變量錯誤', NoReferencing: '不存在的引用變量', + placeholder_key: '請輸入鍵名', placeholder: '請選擇變量', inputPlaceholder: '請輸入變量', loop: '循環變量', @@ -324,7 +325,6 @@ export default { placeholder: '返回每組的第一個非空值', placeholder1: '返回每組變量的數組(Array)', placeholder2: '返回每組變量的字典(Dict)', - placeholder_key: '輸入鍵名', group: { noneError: '名稱不能為空', dupError: '名稱不能重複', From 0754ec574f85f7dfd25b8657b7d873b8fc06aa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Thu, 19 Mar 2026 14:36:18 +0800 Subject: [PATCH 3/7] fix variable key can't null or empty --- .../variable_aggregation_node/i_variable_aggregation_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py index 34d6f321eba..87555baf2fa 100644 --- a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py +++ b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py @@ -11,7 +11,7 @@ class VariableListSerializer(serializers.Serializer): v_id = serializers.CharField(required=True, label=_("Variable id")) - key = serializers.CharField(required=False, label=_("Key")) + key = serializers.CharField(required=False, label=_("Key"), allow_null=True, allow_blank=True, ) variable = serializers.ListField(required=True, label=_("Variable")) From 417a8ce23b01b7ca9b2c7348be66ecdfdf26f1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Wed, 18 Mar 2026 15:10:21 +0800 Subject: [PATCH 4/7] =?UTF-8?q?optimize:=20=E8=AF=AD=E6=B3=95=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/base_variable_splitting_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/application/flow/step_node/variable_splitting_node/impl/base_variable_splitting_node.py b/apps/application/flow/step_node/variable_splitting_node/impl/base_variable_splitting_node.py index c11b79a7b30..f19d45b1d37 100644 --- a/apps/application/flow/step_node/variable_splitting_node/impl/base_variable_splitting_node.py +++ b/apps/application/flow/step_node/variable_splitting_node/impl/base_variable_splitting_node.py @@ -41,7 +41,7 @@ def save_context(self, details, workflow_manage): self.context['exception_message'] = details.get('err_message') def execute(self, input_variable, variable_list, **kwargs) -> NodeResult: - if type(input_variable).__name__ == "str": + if isinstance(input_variable, str): try: input_variable = json.loads(input_variable) except Exception: From da4f8eef77a4694de662966060c6ff572754e779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Thu, 19 Mar 2026 17:46:36 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E5=B0=8F=E8=B0=83=E6=95=B4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/workflow/nodes/variable-aggregation-node/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/workflow/nodes/variable-aggregation-node/index.vue b/ui/src/workflow/nodes/variable-aggregation-node/index.vue index 077b198fc20..ead21a59e20 100644 --- a/ui/src/workflow/nodes/variable-aggregation-node/index.vue +++ b/ui/src/workflow/nodes/variable-aggregation-node/index.vue @@ -91,7 +91,7 @@ From 9c7657ab304c14b8ab3368560ffcb640de7d4470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Fri, 20 Mar 2026 17:01:14 +0800 Subject: [PATCH 6/7] feat: `Variable assign` supports convert type --- .../impl/base_variable_assign_node.py | 34 ++++++++++++++++--- .../nodes/variable-assign-node/index.ts | 8 ++++- .../nodes/variable-assign-node/index.vue | 17 ++++++++-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py b/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py index 3ff1982f573..67d0584af0d 100644 --- a/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py +++ b/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py @@ -31,6 +31,25 @@ def chat_evaluation(self, variable, value): else: self.workflow_manage.chat_context[variable['fields'][1]] = value + def convert(self, val, target_type): + if not target_type or val is None: + return val + + if target_type == 'json_object': + return json.loads(val) + elif target_type == 'json_string': + return json.dumps(val, ensure_ascii=False) + elif target_type == 'string': + return str(val) + elif target_type == 'int': + return int(val) + elif target_type == 'float': + return float(val) + elif target_type == 'boolean': + return bool(val) + else: + return val + def handle(self, variable, evaluation): result = { 'name': variable['name'], @@ -42,19 +61,23 @@ def handle(self, variable, evaluation): val = variable['value'] else: val = json.loads(variable['value']) + val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = variable['value'] = val elif variable['type'] == 'string': # 变量解析 例如:{{global.xxx}} val = self.workflow_manage.generate_prompt(variable['value']) + val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = val else: val = variable['value'] + val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = val else: reference = self.get_reference_content(variable['reference']) + reference = self.convert(reference, variable['target_type']) evaluation(variable, reference) result['output_value'] = reference return result @@ -62,22 +85,23 @@ def handle(self, variable, evaluation): def execute(self, variable_list, **kwargs) -> NodeResult: # result_list = [] - is_chat = False + contains_chat_variable = False for variable in variable_list: if 'fields' not in variable: continue + if 'global' == variable['fields'][0]: result = self.handle(variable, self.global_evaluation) result_list.append(result) - if 'chat' == variable['fields'][0]: + elif 'chat' == variable['fields'][0]: result = self.handle(variable, self.chat_evaluation) result_list.append(result) - is_chat = True - if 'loop' == variable['fields'][0]: + contains_chat_variable = True + elif 'loop' == variable['fields'][0]: result = self.handle(variable, self.loop_evaluation) result_list.append(result) - if is_chat: + if contains_chat_variable: from application.flow.loop_workflow_manage import LoopWorkflowManage if isinstance(self.workflow_manage, LoopWorkflowManage): self.workflow_manage.parentWorkflowManage.get_chat_info().set_chat_variable( diff --git a/ui/src/workflow/nodes/variable-assign-node/index.ts b/ui/src/workflow/nodes/variable-assign-node/index.ts index 567bf425ae1..6c50d4b73cd 100644 --- a/ui/src/workflow/nodes/variable-assign-node/index.ts +++ b/ui/src/workflow/nodes/variable-assign-node/index.ts @@ -7,8 +7,14 @@ class VariableAssignNode extends AppNode { } } +class VariableAssignModel extends AppNodeModel { + get_width() { + return 450 + } +} + export default { type: 'variable-assign-node', - model: AppNodeModel, + model: VariableAssignModel, view: VariableAssignNode } diff --git a/ui/src/workflow/nodes/variable-assign-node/index.vue b/ui/src/workflow/nodes/variable-assign-node/index.vue index a455730e0d6..79cd5698197 100644 --- a/ui/src/workflow/nodes/variable-assign-node/index.vue +++ b/ui/src/workflow/nodes/variable-assign-node/index.vue @@ -68,6 +68,7 @@ + + + + + + + + @@ -174,6 +186,7 @@ const workflowMode = inject('workflowMode') as WorkflowMode const props = defineProps<{ nodeModel: any }>() const typeOptions = ['string', 'num', 'json', 'bool'] +const targetTypeOptions = ['string', 'int', 'float', 'json_object', 'json_string', 'boolean'] const wheel = (e: any) => { if (e.ctrlKey === true) { From 6c599b7974d5fe5e6f986927d978ff84b25f0ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Fri, 20 Mar 2026 17:05:09 +0800 Subject: [PATCH 7/7] Revert "feat: `Variable assign` supports convert type" This reverts commit 9c7657ab304c14b8ab3368560ffcb640de7d4470. --- .../impl/base_variable_assign_node.py | 34 +++---------------- .../nodes/variable-assign-node/index.ts | 8 +---- .../nodes/variable-assign-node/index.vue | 17 ++-------- 3 files changed, 8 insertions(+), 51 deletions(-) diff --git a/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py b/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py index 67d0584af0d..3ff1982f573 100644 --- a/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py +++ b/apps/application/flow/step_node/variable_assign_node/impl/base_variable_assign_node.py @@ -31,25 +31,6 @@ def chat_evaluation(self, variable, value): else: self.workflow_manage.chat_context[variable['fields'][1]] = value - def convert(self, val, target_type): - if not target_type or val is None: - return val - - if target_type == 'json_object': - return json.loads(val) - elif target_type == 'json_string': - return json.dumps(val, ensure_ascii=False) - elif target_type == 'string': - return str(val) - elif target_type == 'int': - return int(val) - elif target_type == 'float': - return float(val) - elif target_type == 'boolean': - return bool(val) - else: - return val - def handle(self, variable, evaluation): result = { 'name': variable['name'], @@ -61,23 +42,19 @@ def handle(self, variable, evaluation): val = variable['value'] else: val = json.loads(variable['value']) - val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = variable['value'] = val elif variable['type'] == 'string': # 变量解析 例如:{{global.xxx}} val = self.workflow_manage.generate_prompt(variable['value']) - val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = val else: val = variable['value'] - val = self.convert(val, variable['target_type']) evaluation(variable, val) result['output_value'] = val else: reference = self.get_reference_content(variable['reference']) - reference = self.convert(reference, variable['target_type']) evaluation(variable, reference) result['output_value'] = reference return result @@ -85,23 +62,22 @@ def handle(self, variable, evaluation): def execute(self, variable_list, **kwargs) -> NodeResult: # result_list = [] - contains_chat_variable = False + is_chat = False for variable in variable_list: if 'fields' not in variable: continue - if 'global' == variable['fields'][0]: result = self.handle(variable, self.global_evaluation) result_list.append(result) - elif 'chat' == variable['fields'][0]: + if 'chat' == variable['fields'][0]: result = self.handle(variable, self.chat_evaluation) result_list.append(result) - contains_chat_variable = True - elif 'loop' == variable['fields'][0]: + is_chat = True + if 'loop' == variable['fields'][0]: result = self.handle(variable, self.loop_evaluation) result_list.append(result) - if contains_chat_variable: + if is_chat: from application.flow.loop_workflow_manage import LoopWorkflowManage if isinstance(self.workflow_manage, LoopWorkflowManage): self.workflow_manage.parentWorkflowManage.get_chat_info().set_chat_variable( diff --git a/ui/src/workflow/nodes/variable-assign-node/index.ts b/ui/src/workflow/nodes/variable-assign-node/index.ts index 6c50d4b73cd..567bf425ae1 100644 --- a/ui/src/workflow/nodes/variable-assign-node/index.ts +++ b/ui/src/workflow/nodes/variable-assign-node/index.ts @@ -7,14 +7,8 @@ class VariableAssignNode extends AppNode { } } -class VariableAssignModel extends AppNodeModel { - get_width() { - return 450 - } -} - export default { type: 'variable-assign-node', - model: VariableAssignModel, + model: AppNodeModel, view: VariableAssignNode } diff --git a/ui/src/workflow/nodes/variable-assign-node/index.vue b/ui/src/workflow/nodes/variable-assign-node/index.vue index 79cd5698197..a455730e0d6 100644 --- a/ui/src/workflow/nodes/variable-assign-node/index.vue +++ b/ui/src/workflow/nodes/variable-assign-node/index.vue @@ -68,7 +68,6 @@ - - - - - - - - @@ -186,7 +174,6 @@ const workflowMode = inject('workflowMode') as WorkflowMode const props = defineProps<{ nodeModel: any }>() const typeOptions = ['string', 'num', 'json', 'bool'] -const targetTypeOptions = ['string', 'int', 'float', 'json_object', 'json_string', 'boolean'] const wheel = (e: any) => { if (e.ctrlKey === true) {