diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index fafd43e5bc..60678bc2ba 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -94,8 +94,11 @@ class CodeNode(BaseNode): :return: """ if not isinstance(value, str): - raise ValueError(f"Output variable `{variable}` must be a string") - + if isinstance(value, type(None)): + return None + else: + raise ValueError(f"Output variable `{variable}` must be a string") + if len(value) > MAX_STRING_LENGTH: raise ValueError(f'The length of output variable `{variable}` must be less than {MAX_STRING_LENGTH} characters') @@ -109,7 +112,10 @@ class CodeNode(BaseNode): :return: """ if not isinstance(value, int | float): - raise ValueError(f"Output variable `{variable}` must be a number") + if isinstance(value, type(None)): + return None + else: + raise ValueError(f"Output variable `{variable}` must be a number") if value > MAX_NUMBER or value < MIN_NUMBER: raise ValueError(f'Output variable `{variable}` is out of range, it must be between {MIN_NUMBER} and {MAX_NUMBER}.') @@ -157,28 +163,31 @@ class CodeNode(BaseNode): elif isinstance(output_value, list): first_element = output_value[0] if len(output_value) > 0 else None if first_element is not None: - if isinstance(first_element, int | float) and all(isinstance(value, int | float) for value in output_value): + if isinstance(first_element, int | float) and all(value is None or isinstance(value, int | float) for value in output_value): for i, value in enumerate(output_value): self._check_number( value=value, variable=f'{prefix}.{output_name}[{i}]' if prefix else f'{output_name}[{i}]' ) - elif isinstance(first_element, str) and all(isinstance(value, str) for value in output_value): + elif isinstance(first_element, str) and all(value is None or isinstance(value, str) for value in output_value): for i, value in enumerate(output_value): self._check_string( value=value, variable=f'{prefix}.{output_name}[{i}]' if prefix else f'{output_name}[{i}]' ) - elif isinstance(first_element, dict) and all(isinstance(value, dict) for value in output_value): + elif isinstance(first_element, dict) and all(value is None or isinstance(value, dict) for value in output_value): for i, value in enumerate(output_value): - self._transform_result( - result=value, - output_schema=None, - prefix=f'{prefix}.{output_name}[{i}]' if prefix else f'{output_name}[{i}]', - depth=depth + 1 - ) + if value is not None: + self._transform_result( + result=value, + output_schema=None, + prefix=f'{prefix}.{output_name}[{i}]' if prefix else f'{output_name}[{i}]', + depth=depth + 1 + ) else: raise ValueError(f'Output {prefix}.{output_name} is not a valid array. make sure all elements are of the same type.') + elif isinstance(output_value, type(None)): + pass else: raise ValueError(f'Output {prefix}.{output_name} is not a valid type.') @@ -193,16 +202,19 @@ class CodeNode(BaseNode): if output_config.type == 'object': # check if output is object if not isinstance(result.get(output_name), dict): - raise ValueError( - f'Output {prefix}{dot}{output_name} is not an object, got {type(result.get(output_name))} instead.' + if isinstance(result.get(output_name), type(None)): + transformed_result[output_name] = None + else: + raise ValueError( + f'Output {prefix}{dot}{output_name} is not an object, got {type(result.get(output_name))} instead.' + ) + else: + transformed_result[output_name] = self._transform_result( + result=result[output_name], + output_schema=output_config.children, + prefix=f'{prefix}.{output_name}', + depth=depth + 1 ) - - transformed_result[output_name] = self._transform_result( - result=result[output_name], - output_schema=output_config.children, - prefix=f'{prefix}.{output_name}', - depth=depth + 1 - ) elif output_config.type == 'number': # check if number available transformed_result[output_name] = self._check_number( @@ -218,68 +230,80 @@ class CodeNode(BaseNode): elif output_config.type == 'array[number]': # check if array of number available if not isinstance(result[output_name], list): - raise ValueError( - f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' - ) + if isinstance(result[output_name], type(None)): + transformed_result[output_name] = None + else: + raise ValueError( + f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' + ) + else: + if len(result[output_name]) > MAX_NUMBER_ARRAY_LENGTH: + raise ValueError( + f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_NUMBER_ARRAY_LENGTH} elements.' + ) - if len(result[output_name]) > MAX_NUMBER_ARRAY_LENGTH: - raise ValueError( - f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_NUMBER_ARRAY_LENGTH} elements.' - ) - - transformed_result[output_name] = [ - self._check_number( - value=value, - variable=f'{prefix}{dot}{output_name}[{i}]' - ) - for i, value in enumerate(result[output_name]) - ] + transformed_result[output_name] = [ + self._check_number( + value=value, + variable=f'{prefix}{dot}{output_name}[{i}]' + ) + for i, value in enumerate(result[output_name]) + ] elif output_config.type == 'array[string]': # check if array of string available if not isinstance(result[output_name], list): - raise ValueError( - f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' - ) + if isinstance(result[output_name], type(None)): + transformed_result[output_name] = None + else: + raise ValueError( + f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' + ) + else: + if len(result[output_name]) > MAX_STRING_ARRAY_LENGTH: + raise ValueError( + f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_STRING_ARRAY_LENGTH} elements.' + ) - if len(result[output_name]) > MAX_STRING_ARRAY_LENGTH: - raise ValueError( - f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_STRING_ARRAY_LENGTH} elements.' - ) - - transformed_result[output_name] = [ - self._check_string( - value=value, - variable=f'{prefix}{dot}{output_name}[{i}]' - ) - for i, value in enumerate(result[output_name]) - ] + transformed_result[output_name] = [ + self._check_string( + value=value, + variable=f'{prefix}{dot}{output_name}[{i}]' + ) + for i, value in enumerate(result[output_name]) + ] elif output_config.type == 'array[object]': # check if array of object available if not isinstance(result[output_name], list): - raise ValueError( - f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' - ) - - if len(result[output_name]) > MAX_OBJECT_ARRAY_LENGTH: - raise ValueError( - f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_OBJECT_ARRAY_LENGTH} elements.' - ) - - for i, value in enumerate(result[output_name]): - if not isinstance(value, dict): + if isinstance(result[output_name], type(None)): + transformed_result[output_name] = None + else: raise ValueError( - f'Output {prefix}{dot}{output_name}[{i}] is not an object, got {type(value)} instead at index {i}.' + f'Output {prefix}{dot}{output_name} is not an array, got {type(result.get(output_name))} instead.' ) + else: + if len(result[output_name]) > MAX_OBJECT_ARRAY_LENGTH: + raise ValueError( + f'The length of output variable `{prefix}{dot}{output_name}` must be less than {MAX_OBJECT_ARRAY_LENGTH} elements.' + ) + + for i, value in enumerate(result[output_name]): + if not isinstance(value, dict): + if isinstance(value, type(None)): + pass + else: + raise ValueError( + f'Output {prefix}{dot}{output_name}[{i}] is not an object, got {type(value)} instead at index {i}.' + ) - transformed_result[output_name] = [ - self._transform_result( - result=value, - output_schema=output_config.children, - prefix=f'{prefix}{dot}{output_name}[{i}]', - depth=depth + 1 - ) - for i, value in enumerate(result[output_name]) - ] + transformed_result[output_name] = [ + None if value is None else self._transform_result( + result=value, + output_schema=output_config.children, + prefix=f'{prefix}{dot}{output_name}[{i}]', + depth=depth + 1 + ) + for i, value in enumerate(result[output_name]) + ] else: raise ValueError(f'Output type {output_config.type} is not supported.')