refactor(workflow): introduce specific exceptions for code validation (#10218)

This commit is contained in:
-LAN- 2024-11-04 15:22:41 +08:00 committed by GitHub
parent 8b5ea39916
commit be96f6e62d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 42 additions and 20 deletions

View File

@ -12,6 +12,12 @@ from core.workflow.nodes.code.entities import CodeNodeData
from core.workflow.nodes.enums import NodeType from core.workflow.nodes.enums import NodeType
from models.workflow import WorkflowNodeExecutionStatus from models.workflow import WorkflowNodeExecutionStatus
from .exc import (
CodeNodeError,
DepthLimitError,
OutputValidationError,
)
class CodeNode(BaseNode[CodeNodeData]): class CodeNode(BaseNode[CodeNodeData]):
_node_data_cls = CodeNodeData _node_data_cls = CodeNodeData
@ -60,7 +66,7 @@ class CodeNode(BaseNode[CodeNodeData]):
# Transform result # Transform result
result = self._transform_result(result, self.node_data.outputs) result = self._transform_result(result, self.node_data.outputs)
except (CodeExecutionError, ValueError) as e: except (CodeExecutionError, CodeNodeError) as e:
return NodeRunResult(status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e)) return NodeRunResult(status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e))
return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=variables, outputs=result) return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=variables, outputs=result)
@ -76,10 +82,10 @@ class CodeNode(BaseNode[CodeNodeData]):
if value is None: if value is None:
return None return None
else: else:
raise ValueError(f"Output variable `{variable}` must be a string") raise OutputValidationError(f"Output variable `{variable}` must be a string")
if len(value) > dify_config.CODE_MAX_STRING_LENGTH: if len(value) > dify_config.CODE_MAX_STRING_LENGTH:
raise ValueError( raise OutputValidationError(
f"The length of output variable `{variable}` must be" f"The length of output variable `{variable}` must be"
f" less than {dify_config.CODE_MAX_STRING_LENGTH} characters" f" less than {dify_config.CODE_MAX_STRING_LENGTH} characters"
) )
@ -97,10 +103,10 @@ class CodeNode(BaseNode[CodeNodeData]):
if value is None: if value is None:
return None return None
else: else:
raise ValueError(f"Output variable `{variable}` must be a number") raise OutputValidationError(f"Output variable `{variable}` must be a number")
if value > dify_config.CODE_MAX_NUMBER or value < dify_config.CODE_MIN_NUMBER: if value > dify_config.CODE_MAX_NUMBER or value < dify_config.CODE_MIN_NUMBER:
raise ValueError( raise OutputValidationError(
f"Output variable `{variable}` is out of range," f"Output variable `{variable}` is out of range,"
f" it must be between {dify_config.CODE_MIN_NUMBER} and {dify_config.CODE_MAX_NUMBER}." f" it must be between {dify_config.CODE_MIN_NUMBER} and {dify_config.CODE_MAX_NUMBER}."
) )
@ -108,7 +114,7 @@ class CodeNode(BaseNode[CodeNodeData]):
if isinstance(value, float): if isinstance(value, float):
# raise error if precision is too high # raise error if precision is too high
if len(str(value).split(".")[1]) > dify_config.CODE_MAX_PRECISION: if len(str(value).split(".")[1]) > dify_config.CODE_MAX_PRECISION:
raise ValueError( raise OutputValidationError(
f"Output variable `{variable}` has too high precision," f"Output variable `{variable}` has too high precision,"
f" it must be less than {dify_config.CODE_MAX_PRECISION} digits." f" it must be less than {dify_config.CODE_MAX_PRECISION} digits."
) )
@ -125,7 +131,7 @@ class CodeNode(BaseNode[CodeNodeData]):
:return: :return:
""" """
if depth > dify_config.CODE_MAX_DEPTH: if depth > dify_config.CODE_MAX_DEPTH:
raise ValueError(f"Depth limit ${dify_config.CODE_MAX_DEPTH} reached, object too deep.") raise DepthLimitError(f"Depth limit ${dify_config.CODE_MAX_DEPTH} reached, object too deep.")
transformed_result = {} transformed_result = {}
if output_schema is None: if output_schema is None:
@ -177,14 +183,14 @@ class CodeNode(BaseNode[CodeNodeData]):
depth=depth + 1, depth=depth + 1,
) )
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}.{output_name} is not a valid array." f"Output {prefix}.{output_name} is not a valid array."
f" make sure all elements are of the same type." f" make sure all elements are of the same type."
) )
elif output_value is None: elif output_value is None:
pass pass
else: else:
raise ValueError(f"Output {prefix}.{output_name} is not a valid type.") raise OutputValidationError(f"Output {prefix}.{output_name} is not a valid type.")
return result return result
@ -192,7 +198,7 @@ class CodeNode(BaseNode[CodeNodeData]):
for output_name, output_config in output_schema.items(): for output_name, output_config in output_schema.items():
dot = "." if prefix else "" dot = "." if prefix else ""
if output_name not in result: if output_name not in result:
raise ValueError(f"Output {prefix}{dot}{output_name} is missing.") raise OutputValidationError(f"Output {prefix}{dot}{output_name} is missing.")
if output_config.type == "object": if output_config.type == "object":
# check if output is object # check if output is object
@ -200,7 +206,7 @@ class CodeNode(BaseNode[CodeNodeData]):
if isinstance(result.get(output_name), type(None)): if isinstance(result.get(output_name), type(None)):
transformed_result[output_name] = None transformed_result[output_name] = None
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an object," f"Output {prefix}{dot}{output_name} is not an object,"
f" got {type(result.get(output_name))} instead." f" got {type(result.get(output_name))} instead."
) )
@ -228,13 +234,13 @@ class CodeNode(BaseNode[CodeNodeData]):
if isinstance(result[output_name], type(None)): if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None transformed_result[output_name] = None
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array," f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead." f" got {type(result.get(output_name))} instead."
) )
else: else:
if len(result[output_name]) > dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH: if len(result[output_name]) > dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH:
raise ValueError( raise OutputValidationError(
f"The length of output variable `{prefix}{dot}{output_name}` must be" f"The length of output variable `{prefix}{dot}{output_name}` must be"
f" less than {dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH} elements." f" less than {dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH} elements."
) )
@ -249,13 +255,13 @@ class CodeNode(BaseNode[CodeNodeData]):
if isinstance(result[output_name], type(None)): if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None transformed_result[output_name] = None
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array," f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead." f" got {type(result.get(output_name))} instead."
) )
else: else:
if len(result[output_name]) > dify_config.CODE_MAX_STRING_ARRAY_LENGTH: if len(result[output_name]) > dify_config.CODE_MAX_STRING_ARRAY_LENGTH:
raise ValueError( raise OutputValidationError(
f"The length of output variable `{prefix}{dot}{output_name}` must be" f"The length of output variable `{prefix}{dot}{output_name}` must be"
f" less than {dify_config.CODE_MAX_STRING_ARRAY_LENGTH} elements." f" less than {dify_config.CODE_MAX_STRING_ARRAY_LENGTH} elements."
) )
@ -270,13 +276,13 @@ class CodeNode(BaseNode[CodeNodeData]):
if isinstance(result[output_name], type(None)): if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None transformed_result[output_name] = None
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array," f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead." f" got {type(result.get(output_name))} instead."
) )
else: else:
if len(result[output_name]) > dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH: if len(result[output_name]) > dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH:
raise ValueError( raise OutputValidationError(
f"The length of output variable `{prefix}{dot}{output_name}` must be" f"The length of output variable `{prefix}{dot}{output_name}` must be"
f" less than {dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH} elements." f" less than {dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH} elements."
) )
@ -286,7 +292,7 @@ class CodeNode(BaseNode[CodeNodeData]):
if value is None: if value is None:
pass pass
else: else:
raise ValueError( raise OutputValidationError(
f"Output {prefix}{dot}{output_name}[{i}] is not an object," f"Output {prefix}{dot}{output_name}[{i}] is not an object,"
f" got {type(value)} instead at index {i}." f" got {type(value)} instead at index {i}."
) )
@ -303,13 +309,13 @@ class CodeNode(BaseNode[CodeNodeData]):
for i, value in enumerate(result[output_name]) for i, value in enumerate(result[output_name])
] ]
else: else:
raise ValueError(f"Output type {output_config.type} is not supported.") raise OutputValidationError(f"Output type {output_config.type} is not supported.")
parameters_validated[output_name] = True parameters_validated[output_name] = True
# check if all output parameters are validated # check if all output parameters are validated
if len(parameters_validated) != len(result): if len(parameters_validated) != len(result):
raise ValueError("Not all output parameters are validated.") raise CodeNodeError("Not all output parameters are validated.")
return transformed_result return transformed_result

View File

@ -0,0 +1,16 @@
class CodeNodeError(ValueError):
"""Base class for code node errors."""
pass
class OutputValidationError(CodeNodeError):
"""Raised when there is an output validation error."""
pass
class DepthLimitError(CodeNodeError):
"""Raised when the depth limit is reached."""
pass