From c6ea31c29615c7b33b0084e8b99b63f1f4d2983c Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 4 Feb 2026 12:11:22 +0800 Subject: [PATCH] fix(workflow): add backward compatibility for any-value variable type --- api/app/core/workflow/nodes/code/node.py | 5 +++-- api/app/core/workflow/nodes/cycle_graph/config.py | 2 +- api/app/core/workflow/nodes/cycle_graph/node.py | 3 +++ .../workflow/nodes/variable_aggregator/config.py | 2 +- .../core/workflow/nodes/variable_aggregator/node.py | 11 ++++++++++- api/app/core/workflow/variable/base_variable.py | 2 ++ api/app/core/workflow/variable/variable_objects.py | 12 ++++++++++++ 7 files changed, 32 insertions(+), 5 deletions(-) diff --git a/api/app/core/workflow/nodes/code/node.py b/api/app/core/workflow/nodes/code/node.py index bfd176e8..fa7ceeb0 100644 --- a/api/app/core/workflow/nodes/code/node.py +++ b/api/app/core/workflow/nodes/code/node.py @@ -5,12 +5,12 @@ import re from string import Template from textwrap import dedent from typing import Any - +import urllib.parse import httpx from app.core.workflow.nodes import BaseNode, WorkflowState -from app.core.workflow.variable.base_variable import VariableType from app.core.workflow.nodes.code.config import CodeNodeConfig +from app.core.workflow.variable.base_variable import VariableType from app.core.workflow.variable_pool import VariablePool logger = logging.getLogger(__name__) @@ -108,6 +108,7 @@ class CodeNode(BaseNode): code = base64.b64decode( self.typed_config.code ).decode("utf-8") + code = urllib.parse.unquote(code, encoding='utf-8') input_variable_dict = base64.b64encode( json.dumps(input_variable_dict).encode("utf-8") diff --git a/api/app/core/workflow/nodes/cycle_graph/config.py b/api/app/core/workflow/nodes/cycle_graph/config.py index 72971286..52aca1d9 100644 --- a/api/app/core/workflow/nodes/cycle_graph/config.py +++ b/api/app/core/workflow/nodes/cycle_graph/config.py @@ -129,7 +129,7 @@ class IterationNodeConfig(BaseNodeConfig): ) output_type: VariableType = Field( - ..., + default=None, description="Data type of the loop iteration output" ) diff --git a/api/app/core/workflow/nodes/cycle_graph/node.py b/api/app/core/workflow/nodes/cycle_graph/node.py index 9a3cf6b2..6908cb73 100644 --- a/api/app/core/workflow/nodes/cycle_graph/node.py +++ b/api/app/core/workflow/nodes/cycle_graph/node.py @@ -53,6 +53,9 @@ class CycleGraphNode(BaseNode): elif self.node_type == NodeType.ITERATION: # Iteration node outputs the processed collection config = IterationNodeConfig(**self.config) + if not config.output_type: + outputs['output'] = VariableType.ANY + return outputs if config.output_type in [ VariableType.ARRAY_FILE, VariableType.ARRAY_STRING, diff --git a/api/app/core/workflow/nodes/variable_aggregator/config.py b/api/app/core/workflow/nodes/variable_aggregator/config.py index dbcc08e2..7fe63be1 100644 --- a/api/app/core/workflow/nodes/variable_aggregator/config.py +++ b/api/app/core/workflow/nodes/variable_aggregator/config.py @@ -16,7 +16,7 @@ class VariableAggregatorNodeConfig(BaseNodeConfig): ) group_type: dict[str, VariableType] = Field( - ..., + default=None, description="每个分组的变量类型" ) diff --git a/api/app/core/workflow/nodes/variable_aggregator/node.py b/api/app/core/workflow/nodes/variable_aggregator/node.py index 48ee9f85..56ab4cfb 100644 --- a/api/app/core/workflow/nodes/variable_aggregator/node.py +++ b/api/app/core/workflow/nodes/variable_aggregator/node.py @@ -19,6 +19,10 @@ class VariableAggregatorNode(BaseNode): def _output_types(self) -> dict[str, VariableType]: config = VariableAggregatorNodeConfig(**self.config) output = {} + if not config.group_type: + for group_name in config.group_variables.keys(): + output[group_name] = VariableType.ANY + return output for var_type in config.group_type: output[var_type] = config.group_type[var_type] return output @@ -64,6 +68,8 @@ class VariableAggregatorNode(BaseNode): return value logger.info("No variable found in non-group mode; returning empty string.") + if not self.typed_config.group_type: + return "" return DEFAULT_VALUE(self.typed_config.group_type["output"]) # -------------------------- @@ -83,7 +89,10 @@ class VariableAggregatorNode(BaseNode): result[group_name] = value break else: - result[group_name] = DEFAULT_VALUE(self.typed_config.group_type[group_name]) + if not self.typed_config.group_type: + result[group_name] = "" + else: + result[group_name] = DEFAULT_VALUE(self.typed_config.group_type[group_name]) logger.info(f"No variable found for group '{group_name}'; set empty string.") logger.info(f"Node: {self.node_id} variable aggregation result: {result}") return result diff --git a/api/app/core/workflow/variable/base_variable.py b/api/app/core/workflow/variable/base_variable.py index 221c7917..d7c96fab 100644 --- a/api/app/core/workflow/variable/base_variable.py +++ b/api/app/core/workflow/variable/base_variable.py @@ -20,6 +20,8 @@ class VariableType(StrEnum): NESTED_ARRAY = "array_nest" + ANY = 'any' + @classmethod def type_map(cls, var: Any) -> "VariableType": """Maps a Python value to a corresponding VariableType. diff --git a/api/app/core/workflow/variable/variable_objects.py b/api/app/core/workflow/variable/variable_objects.py index 3af7d1bb..35a30418 100644 --- a/api/app/core/workflow/variable/variable_objects.py +++ b/api/app/core/workflow/variable/variable_objects.py @@ -107,6 +107,16 @@ class NestedArrayObject(BaseVariable): return [[item.get_value() for item in row] for row in self.value] +class AnyObject(BaseVariable): + type = 'any' + + def valid_value(self, value: Any) -> Any: + return value + + def to_literal(self) -> str: + return str(self.value) + + def make_array(child_type: Type[T], value: list[Any]) -> ArrayObject[T]: """简化 ArrayObject 创建,不需要重复写类型""" @@ -133,5 +143,7 @@ def create_variable_instance(var_type: VariableType, value: Any) -> T: return make_array(DictObject, value) case VariableType.ARRAY_FILE: return make_array(FileObject, value) + case VariableType.ANY: + return AnyObject(value) case _: raise TypeError(f"Invalid type - {var_type}")