Skip to content

Commit

Permalink
refactor(workflow): introduce specific exceptions for code validation (
Browse files Browse the repository at this point in the history
  • Loading branch information
laipz8200 authored Nov 4, 2024
1 parent 8b5ea39 commit be96f6e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 20 deletions.
46 changes: 26 additions & 20 deletions api/core/workflow/nodes/code/code_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
from core.workflow.nodes.enums import NodeType
from models.workflow import WorkflowNodeExecutionStatus

from .exc import (
CodeNodeError,
DepthLimitError,
OutputValidationError,
)


class CodeNode(BaseNode[CodeNodeData]):
_node_data_cls = CodeNodeData
Expand Down Expand Up @@ -60,7 +66,7 @@ def _run(self) -> NodeRunResult:

# Transform result
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.SUCCEEDED, inputs=variables, outputs=result)
Expand All @@ -76,10 +82,10 @@ def _check_string(self, value: str, variable: str) -> str:
if value is None:
return None
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:
raise ValueError(
raise OutputValidationError(
f"The length of output variable `{variable}` must be"
f" less than {dify_config.CODE_MAX_STRING_LENGTH} characters"
)
Expand All @@ -97,18 +103,18 @@ def _check_number(self, value: Union[int, float], variable: str) -> Union[int, f
if value is None:
return None
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:
raise ValueError(
raise OutputValidationError(
f"Output variable `{variable}` is out of range,"
f" it must be between {dify_config.CODE_MIN_NUMBER} and {dify_config.CODE_MAX_NUMBER}."
)

if isinstance(value, float):
# raise error if precision is too high
if len(str(value).split(".")[1]) > dify_config.CODE_MAX_PRECISION:
raise ValueError(
raise OutputValidationError(
f"Output variable `{variable}` has too high precision,"
f" it must be less than {dify_config.CODE_MAX_PRECISION} digits."
)
Expand All @@ -125,7 +131,7 @@ def _transform_result(
:return:
"""
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 = {}
if output_schema is None:
Expand Down Expand Up @@ -177,30 +183,30 @@ def _transform_result(
depth=depth + 1,
)
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}.{output_name} is not a valid array."
f" make sure all elements are of the same type."
)
elif output_value is None:
pass
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

parameters_validated = {}
for output_name, output_config in output_schema.items():
dot = "." if prefix else ""
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":
# check if output is object
if not isinstance(result.get(output_name), dict):
if isinstance(result.get(output_name), type(None)):
transformed_result[output_name] = None
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an object,"
f" got {type(result.get(output_name))} instead."
)
Expand Down Expand Up @@ -228,13 +234,13 @@ def _transform_result(
if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead."
)
else:
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" less than {dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH} elements."
)
Expand All @@ -249,13 +255,13 @@ def _transform_result(
if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead."
)
else:
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" less than {dify_config.CODE_MAX_STRING_ARRAY_LENGTH} elements."
)
Expand All @@ -270,13 +276,13 @@ def _transform_result(
if isinstance(result[output_name], type(None)):
transformed_result[output_name] = None
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}{dot}{output_name} is not an array,"
f" got {type(result.get(output_name))} instead."
)
else:
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" less than {dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH} elements."
)
Expand All @@ -286,7 +292,7 @@ def _transform_result(
if value is None:
pass
else:
raise ValueError(
raise OutputValidationError(
f"Output {prefix}{dot}{output_name}[{i}] is not an object,"
f" got {type(value)} instead at index {i}."
)
Expand All @@ -303,13 +309,13 @@ def _transform_result(
for i, value in enumerate(result[output_name])
]
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

# check if all output parameters are validated
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

Expand Down
16 changes: 16 additions & 0 deletions api/core/workflow/nodes/code/exc.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit be96f6e

Please sign in to comment.