vellum-ai 0.11.10__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vellum/client/core/client_wrapper.py +1 -1
- vellum/workflows/descriptors/base.py +2 -2
- vellum/workflows/descriptors/tests/test_utils.py +4 -4
- vellum/workflows/errors/__init__.py +3 -3
- vellum/workflows/errors/types.py +46 -3
- vellum/workflows/events/node.py +3 -3
- vellum/workflows/events/tests/test_event.py +3 -3
- vellum/workflows/events/workflow.py +3 -3
- vellum/workflows/exceptions.py +8 -4
- vellum/workflows/nodes/bases/base.py +9 -2
- vellum/workflows/nodes/bases/tests/test_base_node.py +13 -0
- vellum/workflows/nodes/core/error_node/node.py +9 -5
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +3 -13
- vellum/workflows/nodes/core/map_node/node.py +2 -2
- vellum/workflows/nodes/core/retry_node/node.py +5 -5
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +6 -6
- vellum/workflows/nodes/core/templating_node/node.py +2 -2
- vellum/workflows/nodes/core/try_node/node.py +7 -7
- vellum/workflows/nodes/core/try_node/tests/test_node.py +9 -7
- vellum/workflows/nodes/displayable/bases/api_node/node.py +3 -3
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +4 -12
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +2 -2
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +2 -2
- vellum/workflows/nodes/displayable/bases/search_node.py +3 -3
- vellum/workflows/nodes/displayable/code_execution_node/node.py +6 -6
- vellum/workflows/nodes/displayable/guardrail_node/node.py +3 -3
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +3 -3
- vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +3 -3
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +7 -14
- vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +4 -4
- vellum/workflows/nodes/utils.py +5 -9
- vellum/workflows/references/external_input.py +2 -2
- vellum/workflows/references/node.py +2 -2
- vellum/workflows/references/state_value.py +2 -2
- vellum/workflows/references/workflow_input.py +2 -2
- vellum/workflows/runner/runner.py +15 -15
- {vellum_ee/workflows/display → vellum/workflows}/utils/tests/test_uuids.py +1 -1
- vellum/workflows/workflows/base.py +7 -7
- {vellum_ai-0.11.10.dist-info → vellum_ai-0.12.0.dist-info}/METADATA +1 -1
- {vellum_ai-0.11.10.dist-info → vellum_ai-0.12.0.dist-info}/RECORD +60 -61
- vellum_ee/workflows/display/nodes/base_node_display.py +50 -21
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +1 -1
- vellum_ee/workflows/display/nodes/get_node_display_class.py +10 -1
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +5 -4
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +13 -6
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/merge_node.py +4 -4
- vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +5 -1
- vellum_ee/workflows/display/nodes/vellum/try_node.py +12 -6
- vellum_ee/workflows/display/nodes/vellum/utils.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +269 -85
- vellum_ee/workflows/display/workflows/base_workflow_display.py +2 -2
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +1 -1
- vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
- {vellum_ee/workflows/display → vellum/workflows}/utils/uuids.py +0 -0
- {vellum_ai-0.11.10.dist-info → vellum_ai-0.12.0.dist-info}/LICENSE +0 -0
- {vellum_ai-0.11.10.dist-info → vellum_ai-0.12.0.dist-info}/WHEEL +0 -0
- {vellum_ai-0.11.10.dist-info → vellum_ai-0.12.0.dist-info}/entry_points.txt +0 -0
@@ -17,7 +17,7 @@ class BaseClientWrapper:
|
|
17
17
|
headers: typing.Dict[str, str] = {
|
18
18
|
"X-Fern-Language": "Python",
|
19
19
|
"X-Fern-SDK-Name": "vellum-ai",
|
20
|
-
"X-Fern-SDK-Version": "0.
|
20
|
+
"X-Fern-SDK-Version": "0.12.0",
|
21
21
|
}
|
22
22
|
headers["X_API_KEY"] = self.api_key
|
23
23
|
return headers
|
@@ -258,12 +258,12 @@ class BaseDescriptor(Generic[_T]):
|
|
258
258
|
|
259
259
|
return DoesNotEndWithExpression(lhs=self, rhs=other)
|
260
260
|
|
261
|
-
def
|
261
|
+
def is_null(self) -> "IsNullExpression":
|
262
262
|
from vellum.workflows.expressions.is_null import IsNullExpression
|
263
263
|
|
264
264
|
return IsNullExpression(expression=self)
|
265
265
|
|
266
|
-
def
|
266
|
+
def is_not_null(self) -> "IsNotNullExpression":
|
267
267
|
from vellum.workflows.expressions.is_not_null import IsNotNullExpression
|
268
268
|
|
269
269
|
return IsNotNullExpression(expression=self)
|
@@ -35,8 +35,8 @@ class FixtureState(BaseState):
|
|
35
35
|
(FixtureState.gamma.does_not_contain(FixtureState.delta), False),
|
36
36
|
(FixtureState.gamma.does_not_begin_with(FixtureState.delta), True),
|
37
37
|
(FixtureState.gamma.does_not_end_with(FixtureState.delta), True),
|
38
|
-
(FixtureState.alpha.
|
39
|
-
(FixtureState.alpha.
|
38
|
+
(FixtureState.alpha.is_null(), False),
|
39
|
+
(FixtureState.alpha.is_not_null(), True),
|
40
40
|
(FixtureState.delta.in_(FixtureState.gamma), True),
|
41
41
|
(FixtureState.delta.not_in(FixtureState.gamma), False),
|
42
42
|
(FixtureState.alpha.between(FixtureState.beta, FixtureState.epsilon), False),
|
@@ -66,8 +66,8 @@ class FixtureState(BaseState):
|
|
66
66
|
"does_not_contain",
|
67
67
|
"does_not_begin_with",
|
68
68
|
"does_not_end_with",
|
69
|
-
"
|
70
|
-
"
|
69
|
+
"is_null",
|
70
|
+
"is_not_null",
|
71
71
|
"in_",
|
72
72
|
"not_in",
|
73
73
|
"between",
|
vellum/workflows/errors/types.py
CHANGED
@@ -1,20 +1,63 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from enum import Enum
|
3
|
+
from typing import Dict
|
3
4
|
|
5
|
+
from vellum.client.types.vellum_error import VellumError
|
6
|
+
from vellum.client.types.vellum_error_code_enum import VellumErrorCodeEnum
|
7
|
+
from vellum.client.types.workflow_event_error import WorkflowEventError
|
8
|
+
from vellum.client.types.workflow_execution_event_error_code import WorkflowExecutionEventErrorCode
|
4
9
|
|
5
|
-
|
10
|
+
|
11
|
+
class WorkflowErrorCode(Enum):
|
6
12
|
INVALID_WORKFLOW = "INVALID_WORKFLOW"
|
7
13
|
INVALID_INPUTS = "INVALID_INPUTS"
|
8
14
|
INVALID_OUTPUTS = "INVALID_OUTPUTS"
|
9
15
|
INVALID_STATE = "INVALID_STATE"
|
10
16
|
INVALID_TEMPLATE = "INVALID_TEMPLATE"
|
11
17
|
INTERNAL_ERROR = "INTERNAL_ERROR"
|
18
|
+
NODE_EXECUTION = "NODE_EXECUTION"
|
12
19
|
PROVIDER_ERROR = "PROVIDER_ERROR"
|
13
20
|
USER_DEFINED_ERROR = "USER_DEFINED_ERROR"
|
14
21
|
WORKFLOW_CANCELLED = "WORKFLOW_CANCELLED"
|
15
22
|
|
16
23
|
|
17
24
|
@dataclass(frozen=True)
|
18
|
-
class
|
25
|
+
class WorkflowError:
|
19
26
|
message: str
|
20
|
-
code:
|
27
|
+
code: WorkflowErrorCode
|
28
|
+
|
29
|
+
|
30
|
+
_VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[VellumErrorCodeEnum, WorkflowErrorCode] = {
|
31
|
+
"INVALID_REQUEST": WorkflowErrorCode.INVALID_INPUTS,
|
32
|
+
"INVALID_INPUTS": WorkflowErrorCode.INVALID_INPUTS,
|
33
|
+
"PROVIDER_ERROR": WorkflowErrorCode.PROVIDER_ERROR,
|
34
|
+
"REQUEST_TIMEOUT": WorkflowErrorCode.PROVIDER_ERROR,
|
35
|
+
"INTERNAL_SERVER_ERROR": WorkflowErrorCode.INTERNAL_ERROR,
|
36
|
+
"USER_DEFINED_ERROR": WorkflowErrorCode.USER_DEFINED_ERROR,
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
def vellum_error_to_workflow_error(error: VellumError) -> WorkflowError:
|
41
|
+
return WorkflowError(
|
42
|
+
message=error.message,
|
43
|
+
code=_VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE.get(error.code, WorkflowErrorCode.INTERNAL_ERROR),
|
44
|
+
)
|
45
|
+
|
46
|
+
|
47
|
+
_WORKFLOW_EVENT_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[WorkflowExecutionEventErrorCode, WorkflowErrorCode] = {
|
48
|
+
"WORKFLOW_INITIALIZATION": WorkflowErrorCode.INVALID_WORKFLOW,
|
49
|
+
"WORKFLOW_CANCELLED": WorkflowErrorCode.WORKFLOW_CANCELLED,
|
50
|
+
"NODE_EXECUTION_COUNT_LIMIT_REACHED": WorkflowErrorCode.INVALID_STATE,
|
51
|
+
"INTERNAL_SERVER_ERROR": WorkflowErrorCode.INTERNAL_ERROR,
|
52
|
+
"NODE_EXECUTION": WorkflowErrorCode.NODE_EXECUTION,
|
53
|
+
"LLM_PROVIDER": WorkflowErrorCode.PROVIDER_ERROR,
|
54
|
+
"INVALID_TEMPLATE": WorkflowErrorCode.INVALID_TEMPLATE,
|
55
|
+
"USER_DEFINED_ERROR": WorkflowErrorCode.USER_DEFINED_ERROR,
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
def workflow_event_error_to_workflow_error(error: WorkflowEventError) -> WorkflowError:
|
60
|
+
return WorkflowError(
|
61
|
+
message=error.message,
|
62
|
+
code=_WORKFLOW_EVENT_ERROR_CODE_TO_WORKFLOW_ERROR_CODE.get(error.code, WorkflowErrorCode.INTERNAL_ERROR),
|
63
|
+
)
|
vellum/workflows/events/node.py
CHANGED
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Dict, Generic, List, Literal, Optional, S
|
|
3
3
|
from pydantic import SerializerFunctionWrapHandler, field_serializer, model_serializer
|
4
4
|
|
5
5
|
from vellum.core.pydantic_utilities import UniversalBaseModel
|
6
|
-
from vellum.workflows.errors import
|
6
|
+
from vellum.workflows.errors import WorkflowError
|
7
7
|
from vellum.workflows.expressions.accessor import AccessorExpression
|
8
8
|
from vellum.workflows.outputs.base import BaseOutput
|
9
9
|
from vellum.workflows.ports.port import Port
|
@@ -119,7 +119,7 @@ class NodeExecutionFulfilledEvent(_BaseNodeEvent, Generic[OutputsType]):
|
|
119
119
|
|
120
120
|
|
121
121
|
class NodeExecutionRejectedBody(_BaseNodeExecutionBody):
|
122
|
-
error:
|
122
|
+
error: WorkflowError
|
123
123
|
|
124
124
|
|
125
125
|
class NodeExecutionRejectedEvent(_BaseNodeEvent):
|
@@ -127,7 +127,7 @@ class NodeExecutionRejectedEvent(_BaseNodeEvent):
|
|
127
127
|
body: NodeExecutionRejectedBody
|
128
128
|
|
129
129
|
@property
|
130
|
-
def error(self) ->
|
130
|
+
def error(self) -> WorkflowError:
|
131
131
|
return self.body.error
|
132
132
|
|
133
133
|
|
@@ -4,7 +4,7 @@ from uuid import UUID
|
|
4
4
|
|
5
5
|
from deepdiff import DeepDiff
|
6
6
|
|
7
|
-
from vellum.workflows.errors.types import
|
7
|
+
from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
|
8
8
|
from vellum.workflows.events.node import (
|
9
9
|
NodeExecutionFulfilledBody,
|
10
10
|
NodeExecutionFulfilledEvent,
|
@@ -213,9 +213,9 @@ module_root = name_parts[: name_parts.index("events")]
|
|
213
213
|
span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
214
214
|
body=WorkflowExecutionRejectedBody(
|
215
215
|
workflow_definition=MockWorkflow,
|
216
|
-
error=
|
216
|
+
error=WorkflowError(
|
217
217
|
message="Workflow failed",
|
218
|
-
code=
|
218
|
+
code=WorkflowErrorCode.USER_DEFINED_ERROR,
|
219
219
|
),
|
220
220
|
),
|
221
221
|
),
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Iterable, Liter
|
|
3
3
|
from pydantic import field_serializer
|
4
4
|
|
5
5
|
from vellum.core.pydantic_utilities import UniversalBaseModel
|
6
|
-
from vellum.workflows.errors import
|
6
|
+
from vellum.workflows.errors import WorkflowError
|
7
7
|
from vellum.workflows.outputs.base import BaseOutput
|
8
8
|
from vellum.workflows.references import ExternalInputReference
|
9
9
|
from vellum.workflows.types.generics import OutputsType, StateType, WorkflowInputsType
|
@@ -90,7 +90,7 @@ class WorkflowExecutionFulfilledEvent(_BaseWorkflowEvent, Generic[OutputsType]):
|
|
90
90
|
|
91
91
|
|
92
92
|
class WorkflowExecutionRejectedBody(_BaseWorkflowExecutionBody):
|
93
|
-
error:
|
93
|
+
error: WorkflowError
|
94
94
|
|
95
95
|
|
96
96
|
class WorkflowExecutionRejectedEvent(_BaseWorkflowEvent):
|
@@ -98,7 +98,7 @@ class WorkflowExecutionRejectedEvent(_BaseWorkflowEvent):
|
|
98
98
|
body: WorkflowExecutionRejectedBody
|
99
99
|
|
100
100
|
@property
|
101
|
-
def error(self) ->
|
101
|
+
def error(self) -> WorkflowError:
|
102
102
|
return self.body.error
|
103
103
|
|
104
104
|
|
vellum/workflows/exceptions.py
CHANGED
@@ -1,15 +1,19 @@
|
|
1
|
-
from vellum.workflows.errors import
|
1
|
+
from vellum.workflows.errors import WorkflowError, WorkflowErrorCode
|
2
2
|
|
3
3
|
|
4
4
|
class NodeException(Exception):
|
5
|
-
def __init__(self, message: str, code:
|
5
|
+
def __init__(self, message: str, code: WorkflowErrorCode):
|
6
6
|
self.message = message
|
7
7
|
self.code = code
|
8
8
|
super().__init__(message)
|
9
9
|
|
10
10
|
@property
|
11
|
-
def error(self) ->
|
12
|
-
return
|
11
|
+
def error(self) -> WorkflowError:
|
12
|
+
return WorkflowError(
|
13
13
|
message=self.message,
|
14
14
|
code=self.code,
|
15
15
|
)
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def of(workflow_error: WorkflowError) -> "NodeException":
|
19
|
+
return NodeException(message=workflow_error.message, code=workflow_error.code)
|
@@ -7,7 +7,7 @@ from typing import Any, Dict, Generic, Iterator, Optional, Set, Tuple, Type, Typ
|
|
7
7
|
from vellum.workflows.constants import UNDEF
|
8
8
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
9
9
|
from vellum.workflows.descriptors.utils import is_unresolved, resolve_value
|
10
|
-
from vellum.workflows.errors.types import
|
10
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
11
11
|
from vellum.workflows.exceptions import NodeException
|
12
12
|
from vellum.workflows.graph import Graph
|
13
13
|
from vellum.workflows.graph.graph import GraphTarget
|
@@ -23,6 +23,7 @@ from vellum.workflows.state.context import WorkflowContext
|
|
23
23
|
from vellum.workflows.types.core import MergeBehavior
|
24
24
|
from vellum.workflows.types.generics import StateType
|
25
25
|
from vellum.workflows.types.utils import get_class_attr_names, get_original_base, infer_types
|
26
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
26
27
|
|
27
28
|
|
28
29
|
def is_nested_class(nested: Any, parent: Type) -> bool:
|
@@ -99,6 +100,7 @@ class BaseNodeMeta(type):
|
|
99
100
|
node_class.Execution.node_class = node_class
|
100
101
|
node_class.Trigger.node_class = node_class
|
101
102
|
node_class.ExternalInputs.__parent_class__ = node_class
|
103
|
+
node_class.__id__ = uuid4_from_hash(node_class.__qualname__)
|
102
104
|
return node_class
|
103
105
|
|
104
106
|
@property
|
@@ -213,6 +215,7 @@ class _BaseNodeExecutionMeta(type):
|
|
213
215
|
|
214
216
|
|
215
217
|
class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
|
218
|
+
__id__: UUID = uuid4_from_hash(__qualname__)
|
216
219
|
state: StateType
|
217
220
|
_context: WorkflowContext
|
218
221
|
_inputs: MappingProxyType[NodeReference, Any]
|
@@ -284,7 +287,7 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
|
|
284
287
|
|
285
288
|
raise NodeException(
|
286
289
|
message="Invalid Trigger Node Specification",
|
287
|
-
code=
|
290
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
288
291
|
)
|
289
292
|
|
290
293
|
class Execution(metaclass=_BaseNodeExecutionMeta):
|
@@ -352,3 +355,7 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
|
|
352
355
|
|
353
356
|
def __repr__(self) -> str:
|
354
357
|
return str(self.__class__)
|
358
|
+
|
359
|
+
|
360
|
+
class MyNode2(BaseNode):
|
361
|
+
pass
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from uuid import UUID
|
1
2
|
from typing import Optional
|
2
3
|
|
3
4
|
from vellum.core.pydantic_utilities import UniversalBaseModel
|
@@ -122,3 +123,15 @@ def test_base_node__node_resolution__coalesce_constants():
|
|
122
123
|
|
123
124
|
# THEN the node is initialized with the correct data
|
124
125
|
assert node.value == "world"
|
126
|
+
|
127
|
+
|
128
|
+
def test_base_node__default_id():
|
129
|
+
# GIVEN a node
|
130
|
+
class MyNode(BaseNode):
|
131
|
+
pass
|
132
|
+
|
133
|
+
# WHEN the node is accessed
|
134
|
+
my_id = MyNode.__id__
|
135
|
+
|
136
|
+
# THEN it should equal the hash of `test_base_node__default_id.<locals>.MyNode`
|
137
|
+
assert my_id == UUID("8e71bea7-ce68-492f-9abe-477c788e6273")
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import ClassVar, Union
|
2
2
|
|
3
|
-
from vellum.
|
3
|
+
from vellum.client.types.vellum_error import VellumError
|
4
|
+
from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode, vellum_error_to_workflow_error
|
4
5
|
from vellum.workflows.exceptions import NodeException
|
5
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
6
7
|
|
@@ -12,15 +13,18 @@ class ErrorNode(BaseNode):
|
|
12
13
|
error: Union[str, VellumError] - The error to raise.
|
13
14
|
"""
|
14
15
|
|
15
|
-
error: ClassVar[Union[str, VellumError]]
|
16
|
+
error: ClassVar[Union[str, WorkflowError, VellumError]]
|
16
17
|
|
17
18
|
def run(self) -> BaseNode.Outputs:
|
18
19
|
if isinstance(self.error, str):
|
19
|
-
raise NodeException(message=self.error, code=
|
20
|
-
elif isinstance(self.error,
|
20
|
+
raise NodeException(message=self.error, code=WorkflowErrorCode.USER_DEFINED_ERROR)
|
21
|
+
elif isinstance(self.error, WorkflowError):
|
21
22
|
raise NodeException(message=self.error.message, code=self.error.code)
|
23
|
+
elif isinstance(self.error, VellumError):
|
24
|
+
workflow_error = vellum_error_to_workflow_error(self.error)
|
25
|
+
raise NodeException(message=workflow_error.message, code=workflow_error.code)
|
22
26
|
else:
|
23
27
|
raise NodeException(
|
24
28
|
message=f"Error node received an unexpected input type: {self.error.__class__}",
|
25
|
-
code=
|
29
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
26
30
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Generic, Iterator, Optional, Set, Type, TypeVar
|
2
2
|
|
3
3
|
from vellum.workflows.context import execution_context, get_parent_context
|
4
|
-
from vellum.workflows.errors.types import
|
4
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
5
5
|
from vellum.workflows.exceptions import NodeException
|
6
6
|
from vellum.workflows.nodes.bases.base_subworkflow_node import BaseSubworkflowNode
|
7
7
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
@@ -49,22 +49,12 @@ class InlineSubworkflowNode(BaseSubworkflowNode[StateType], Generic[StateType, W
|
|
49
49
|
elif event.name == "workflow.execution.fulfilled":
|
50
50
|
outputs = event.outputs
|
51
51
|
elif event.name == "workflow.execution.rejected":
|
52
|
-
|
53
|
-
if error.code in VellumErrorCode._value2member_map_:
|
54
|
-
raise NodeException(
|
55
|
-
message=error.message,
|
56
|
-
code=VellumErrorCode(error.code),
|
57
|
-
)
|
58
|
-
else:
|
59
|
-
raise NodeException(
|
60
|
-
message=error.message,
|
61
|
-
code=VellumErrorCode.INTERNAL_ERROR,
|
62
|
-
)
|
52
|
+
raise NodeException.of(event.error)
|
63
53
|
|
64
54
|
if outputs is None:
|
65
55
|
raise NodeException(
|
66
56
|
message="Expected to receive outputs from Workflow Deployment",
|
67
|
-
code=
|
57
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
68
58
|
)
|
69
59
|
|
70
60
|
# For any outputs somehow in our final fulfilled outputs array,
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Callable, Dict, Generic, List, Optional, Tuple
|
|
5
5
|
|
6
6
|
from vellum.workflows.context import execution_context, get_parent_context
|
7
7
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
8
|
-
from vellum.workflows.errors.types import
|
8
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
9
9
|
from vellum.workflows.events.types import ParentContext
|
10
10
|
from vellum.workflows.exceptions import NodeException
|
11
11
|
from vellum.workflows.inputs.base import BaseInputs
|
@@ -88,7 +88,7 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
|
|
88
88
|
break
|
89
89
|
elif terminal_event.name == "workflow.execution.paused":
|
90
90
|
raise NodeException(
|
91
|
-
code=
|
91
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
92
92
|
message=f"Subworkflow unexpectedly paused on iteration {index}",
|
93
93
|
)
|
94
94
|
elif terminal_event.name == "workflow.execution.rejected":
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Optional, Type
|
2
2
|
|
3
|
-
from vellum.workflows.errors.types import
|
3
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
4
4
|
from vellum.workflows.exceptions import NodeException
|
5
5
|
from vellum.workflows.inputs.base import BaseInputs
|
6
6
|
from vellum.workflows.nodes.bases import BaseNode
|
@@ -31,7 +31,7 @@ class RetryNode(BaseNode[StateType], Generic[StateType], metaclass=_RetryNodeMet
|
|
31
31
|
"""
|
32
32
|
|
33
33
|
max_attempts: int
|
34
|
-
retry_on_error_code: Optional[
|
34
|
+
retry_on_error_code: Optional[WorkflowErrorCode] = None
|
35
35
|
subworkflow: Type["BaseWorkflow[SubworkflowInputs, BaseState]"]
|
36
36
|
|
37
37
|
class SubworkflowInputs(BaseInputs):
|
@@ -58,13 +58,13 @@ class RetryNode(BaseNode[StateType], Generic[StateType], metaclass=_RetryNodeMet
|
|
58
58
|
return node_outputs
|
59
59
|
elif terminal_event.name == "workflow.execution.paused":
|
60
60
|
last_exception = NodeException(
|
61
|
-
code=
|
61
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
62
62
|
message=f"Subworkflow unexpectedly paused on attempt {attempt_number}",
|
63
63
|
)
|
64
64
|
break
|
65
65
|
elif self.retry_on_error_code and self.retry_on_error_code != terminal_event.error.code:
|
66
66
|
last_exception = NodeException(
|
67
|
-
code=
|
67
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
68
68
|
message=f"""Unexpected rejection on attempt {attempt_number}: {terminal_event.error.code.value}.
|
69
69
|
Message: {terminal_event.error.message}""",
|
70
70
|
)
|
@@ -76,7 +76,7 @@ Message: {terminal_event.error.message}""",
|
|
76
76
|
|
77
77
|
@classmethod
|
78
78
|
def wrap(
|
79
|
-
cls, max_attempts: int, retry_on_error_code: Optional[
|
79
|
+
cls, max_attempts: int, retry_on_error_code: Optional[WorkflowErrorCode] = None
|
80
80
|
) -> Callable[..., Type["RetryNode"]]:
|
81
81
|
_max_attempts = max_attempts
|
82
82
|
_retry_on_error_code = retry_on_error_code
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
|
-
from vellum.workflows.errors.types import
|
3
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
4
4
|
from vellum.workflows.exceptions import NodeException
|
5
5
|
from vellum.workflows.inputs.base import BaseInputs
|
6
6
|
from vellum.workflows.nodes.bases import BaseNode
|
@@ -11,7 +11,7 @@ from vellum.workflows.state.base import BaseState, StateMeta
|
|
11
11
|
|
12
12
|
def test_retry_node__retry_on_error_code__successfully_retried():
|
13
13
|
# GIVEN a retry node that is configured to retry on PROVIDER_ERROR
|
14
|
-
@RetryNode.wrap(max_attempts=3, retry_on_error_code=
|
14
|
+
@RetryNode.wrap(max_attempts=3, retry_on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
15
15
|
class TestNode(BaseNode):
|
16
16
|
attempt_number = RetryNode.SubworkflowInputs.attempt_number
|
17
17
|
|
@@ -20,7 +20,7 @@ def test_retry_node__retry_on_error_code__successfully_retried():
|
|
20
20
|
|
21
21
|
def run(self) -> Outputs:
|
22
22
|
if self.attempt_number < 3:
|
23
|
-
raise NodeException(message="This will be retried", code=
|
23
|
+
raise NodeException(message="This will be retried", code=WorkflowErrorCode.PROVIDER_ERROR)
|
24
24
|
|
25
25
|
return self.Outputs(execution_count=self.attempt_number)
|
26
26
|
|
@@ -34,7 +34,7 @@ def test_retry_node__retry_on_error_code__successfully_retried():
|
|
34
34
|
|
35
35
|
def test_retry_node__retry_on_error_code__missed():
|
36
36
|
# GIVEN a retry node that is configured to retry on PROVIDER_ERROR
|
37
|
-
@RetryNode.wrap(max_attempts=3, retry_on_error_code=
|
37
|
+
@RetryNode.wrap(max_attempts=3, retry_on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
38
38
|
class TestNode(BaseNode):
|
39
39
|
attempt_number = RetryNode.SubworkflowInputs.attempt_number
|
40
40
|
|
@@ -57,7 +57,7 @@ def test_retry_node__retry_on_error_code__missed():
|
|
57
57
|
exc_info.value.message
|
58
58
|
== "Unexpected rejection on attempt 1: INTERNAL_ERROR.\nMessage: This will not be retried"
|
59
59
|
)
|
60
|
-
assert exc_info.value.code ==
|
60
|
+
assert exc_info.value.code == WorkflowErrorCode.INVALID_OUTPUTS
|
61
61
|
|
62
62
|
|
63
63
|
def test_retry_node__use_parent_inputs_and_state():
|
@@ -69,7 +69,7 @@ def test_retry_node__use_parent_inputs_and_state():
|
|
69
69
|
bar: str
|
70
70
|
|
71
71
|
# AND a retry node that uses the parent's inputs and state
|
72
|
-
@RetryNode.wrap(max_attempts=3, retry_on_error_code=
|
72
|
+
@RetryNode.wrap(max_attempts=3, retry_on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
73
73
|
class TestNode(BaseNode):
|
74
74
|
foo = Inputs.foo
|
75
75
|
bar = State.bar
|
@@ -10,7 +10,7 @@ import pydash
|
|
10
10
|
import pytz
|
11
11
|
import yaml
|
12
12
|
|
13
|
-
from vellum.workflows.errors import
|
13
|
+
from vellum.workflows.errors import WorkflowErrorCode
|
14
14
|
from vellum.workflows.exceptions import NodeException
|
15
15
|
from vellum.workflows.nodes.bases import BaseNode
|
16
16
|
from vellum.workflows.nodes.bases.base import BaseNodeMeta
|
@@ -130,4 +130,4 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
|
|
130
130
|
jinja_globals=self.jinja_globals,
|
131
131
|
)
|
132
132
|
except JinjaTemplateError as e:
|
133
|
-
raise NodeException(message=str(e), code=
|
133
|
+
raise NodeException(message=str(e), code=WorkflowErrorCode.INVALID_TEMPLATE)
|
@@ -2,7 +2,7 @@ import sys
|
|
2
2
|
from types import ModuleType
|
3
3
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Iterator, Optional, Set, Tuple, Type, TypeVar
|
4
4
|
|
5
|
-
from vellum.workflows.errors.types import
|
5
|
+
from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
|
6
6
|
from vellum.workflows.exceptions import NodeException
|
7
7
|
from vellum.workflows.nodes.bases import BaseNode
|
8
8
|
from vellum.workflows.nodes.bases.base import BaseNodeMeta
|
@@ -64,11 +64,11 @@ class TryNode(BaseNode[StateType], Generic[StateType], metaclass=_TryNodeMeta):
|
|
64
64
|
"""
|
65
65
|
|
66
66
|
__wrapped_node__: Optional[Type["BaseNode"]] = None
|
67
|
-
on_error_code: Optional[
|
67
|
+
on_error_code: Optional[WorkflowErrorCode] = None
|
68
68
|
subworkflow: Type["BaseWorkflow"]
|
69
69
|
|
70
70
|
class Outputs(BaseNode.Outputs):
|
71
|
-
error: Optional[
|
71
|
+
error: Optional[WorkflowError] = None
|
72
72
|
|
73
73
|
def run(self) -> Iterator[BaseOutput]:
|
74
74
|
subworkflow = self.subworkflow(
|
@@ -98,13 +98,13 @@ class TryNode(BaseNode[StateType], Generic[StateType], metaclass=_TryNodeMeta):
|
|
98
98
|
outputs = event.outputs
|
99
99
|
elif event.name == "workflow.execution.paused":
|
100
100
|
exception = NodeException(
|
101
|
-
code=
|
101
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
102
102
|
message="Subworkflow unexpectedly paused within Try Node",
|
103
103
|
)
|
104
104
|
elif event.name == "workflow.execution.rejected":
|
105
105
|
if self.on_error_code and self.on_error_code != event.error.code:
|
106
106
|
exception = NodeException(
|
107
|
-
code=
|
107
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
108
108
|
message=f"""Unexpected rejection: {event.error.code.value}.
|
109
109
|
Message: {event.error.message}""",
|
110
110
|
)
|
@@ -116,7 +116,7 @@ Message: {event.error.message}""",
|
|
116
116
|
|
117
117
|
if outputs is None:
|
118
118
|
raise NodeException(
|
119
|
-
code=
|
119
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
120
120
|
message="Expected to receive outputs from Try Node's subworkflow",
|
121
121
|
)
|
122
122
|
|
@@ -130,7 +130,7 @@ Message: {event.error.message}""",
|
|
130
130
|
)
|
131
131
|
|
132
132
|
@classmethod
|
133
|
-
def wrap(cls, on_error_code: Optional[
|
133
|
+
def wrap(cls, on_error_code: Optional[WorkflowErrorCode] = None) -> Callable[..., Type["TryNode"]]:
|
134
134
|
_on_error_code = on_error_code
|
135
135
|
|
136
136
|
def decorator(inner_cls: Type[BaseNode]) -> Type["TryNode"]:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
3
|
from vellum.client import Vellum
|
4
|
-
from vellum.workflows.errors.types import
|
4
|
+
from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
|
5
5
|
from vellum.workflows.exceptions import NodeException
|
6
6
|
from vellum.workflows.inputs.base import BaseInputs
|
7
7
|
from vellum.workflows.nodes.bases import BaseNode
|
@@ -14,13 +14,13 @@ from vellum.workflows.state.context import WorkflowContext
|
|
14
14
|
|
15
15
|
def test_try_node__on_error_code__successfully_caught():
|
16
16
|
# GIVEN a try node that is configured to catch PROVIDER_ERROR
|
17
|
-
@TryNode.wrap(on_error_code=
|
17
|
+
@TryNode.wrap(on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
18
18
|
class TestNode(BaseNode):
|
19
19
|
class Outputs(BaseOutputs):
|
20
20
|
value: int
|
21
21
|
|
22
22
|
def run(self) -> Outputs:
|
23
|
-
raise NodeException(message="This will be caught", code=
|
23
|
+
raise NodeException(message="This will be caught", code=WorkflowErrorCode.PROVIDER_ERROR)
|
24
24
|
|
25
25
|
# WHEN the node is run and throws a PROVIDER_ERROR
|
26
26
|
node = TestNode(state=BaseState())
|
@@ -30,19 +30,21 @@ def test_try_node__on_error_code__successfully_caught():
|
|
30
30
|
assert len(outputs) == 2
|
31
31
|
assert set(outputs) == {
|
32
32
|
BaseOutput(name="value"),
|
33
|
-
BaseOutput(
|
33
|
+
BaseOutput(
|
34
|
+
name="error", value=WorkflowError(message="This will be caught", code=WorkflowErrorCode.PROVIDER_ERROR)
|
35
|
+
),
|
34
36
|
}
|
35
37
|
|
36
38
|
|
37
39
|
def test_try_node__retry_on_error_code__missed():
|
38
40
|
# GIVEN a try node that is configured to catch PROVIDER_ERROR
|
39
|
-
@TryNode.wrap(on_error_code=
|
41
|
+
@TryNode.wrap(on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
40
42
|
class TestNode(BaseNode):
|
41
43
|
class Outputs(BaseOutputs):
|
42
44
|
value: int
|
43
45
|
|
44
46
|
def run(self) -> Outputs:
|
45
|
-
raise NodeException(message="This will be missed", code=
|
47
|
+
raise NodeException(message="This will be missed", code=WorkflowErrorCode.INTERNAL_ERROR)
|
46
48
|
|
47
49
|
# WHEN the node is run and throws a different exception
|
48
50
|
node = TestNode(state=BaseState())
|
@@ -51,7 +53,7 @@ def test_try_node__retry_on_error_code__missed():
|
|
51
53
|
|
52
54
|
# THEN the exception is not caught
|
53
55
|
assert exc_info.value.message == "Unexpected rejection: INTERNAL_ERROR.\nMessage: This will be missed"
|
54
|
-
assert exc_info.value.code ==
|
56
|
+
assert exc_info.value.code == WorkflowErrorCode.INVALID_OUTPUTS
|
55
57
|
|
56
58
|
|
57
59
|
def test_try_node__use_parent_inputs_and_state():
|
@@ -4,7 +4,7 @@ from requests import Request, RequestException, Session
|
|
4
4
|
from requests.exceptions import JSONDecodeError
|
5
5
|
|
6
6
|
from vellum.workflows.constants import APIRequestMethod
|
7
|
-
from vellum.workflows.errors.types import
|
7
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
8
8
|
from vellum.workflows.exceptions import NodeException
|
9
9
|
from vellum.workflows.nodes.bases import BaseNode
|
10
10
|
from vellum.workflows.outputs import BaseOutputs
|
@@ -49,13 +49,13 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
49
49
|
try:
|
50
50
|
prepped = Request(method=method.value, url=url, data=data, json=json, headers=headers).prepare()
|
51
51
|
except Exception as e:
|
52
|
-
raise NodeException(f"Failed to prepare HTTP request: {e}", code=
|
52
|
+
raise NodeException(f"Failed to prepare HTTP request: {e}", code=WorkflowErrorCode.PROVIDER_ERROR)
|
53
53
|
|
54
54
|
try:
|
55
55
|
with Session() as session:
|
56
56
|
response = session.send(prepped)
|
57
57
|
except RequestException as e:
|
58
|
-
raise NodeException(f"HTTP request failed: {e}", code=
|
58
|
+
raise NodeException(f"HTTP request failed: {e}", code=WorkflowErrorCode.PROVIDER_ERROR)
|
59
59
|
|
60
60
|
try:
|
61
61
|
json = response.json()
|