vellum-ai 0.13.27__py3-none-any.whl → 0.14.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.
Files changed (57) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/constants.py +8 -3
  3. vellum/workflows/descriptors/exceptions.py +2 -0
  4. vellum/workflows/descriptors/tests/test_utils.py +21 -0
  5. vellum/workflows/descriptors/utils.py +3 -3
  6. vellum/workflows/errors/types.py +4 -1
  7. vellum/workflows/expressions/accessor.py +4 -3
  8. vellum/workflows/expressions/begins_with.py +3 -2
  9. vellum/workflows/expressions/between.py +4 -3
  10. vellum/workflows/expressions/coalesce_expression.py +2 -2
  11. vellum/workflows/expressions/contains.py +10 -2
  12. vellum/workflows/expressions/does_not_begin_with.py +3 -2
  13. vellum/workflows/expressions/does_not_contain.py +6 -2
  14. vellum/workflows/expressions/does_not_end_with.py +3 -2
  15. vellum/workflows/expressions/ends_with.py +3 -2
  16. vellum/workflows/expressions/greater_than.py +3 -2
  17. vellum/workflows/expressions/greater_than_or_equal_to.py +3 -2
  18. vellum/workflows/expressions/in_.py +2 -1
  19. vellum/workflows/expressions/is_blank.py +2 -1
  20. vellum/workflows/expressions/is_nil.py +2 -2
  21. vellum/workflows/expressions/is_not_blank.py +2 -1
  22. vellum/workflows/expressions/is_not_nil.py +2 -2
  23. vellum/workflows/expressions/is_not_undefined.py +2 -2
  24. vellum/workflows/expressions/is_undefined.py +2 -2
  25. vellum/workflows/expressions/less_than.py +3 -2
  26. vellum/workflows/expressions/less_than_or_equal_to.py +3 -2
  27. vellum/workflows/expressions/not_between.py +4 -3
  28. vellum/workflows/expressions/not_in.py +2 -1
  29. vellum/workflows/nodes/bases/base.py +21 -7
  30. vellum/workflows/nodes/bases/tests/test_base_node.py +84 -0
  31. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +3 -3
  32. vellum/workflows/nodes/core/map_node/node.py +5 -0
  33. vellum/workflows/nodes/core/map_node/tests/test_node.py +22 -0
  34. vellum/workflows/nodes/displayable/bases/api_node/node.py +8 -3
  35. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +19 -9
  36. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +68 -2
  37. vellum/workflows/nodes/displayable/code_execution_node/utils.py +30 -7
  38. vellum/workflows/outputs/base.py +21 -19
  39. vellum/workflows/ports/port.py +14 -5
  40. vellum/workflows/references/external_input.py +2 -2
  41. vellum/workflows/references/lazy.py +2 -2
  42. vellum/workflows/references/output.py +7 -7
  43. vellum/workflows/runner/runner.py +20 -15
  44. vellum/workflows/state/base.py +2 -2
  45. vellum/workflows/state/tests/test_state.py +7 -11
  46. vellum/workflows/utils/vellum_variables.py +3 -0
  47. vellum/workflows/workflows/base.py +20 -0
  48. vellum/workflows/workflows/tests/__init__.py +0 -0
  49. vellum/workflows/workflows/tests/test_base_workflow.py +80 -0
  50. {vellum_ai-0.13.27.dist-info → vellum_ai-0.14.0.dist-info}/METADATA +2 -1
  51. {vellum_ai-0.13.27.dist-info → vellum_ai-0.14.0.dist-info}/RECORD +57 -54
  52. vellum_cli/push.py +15 -1
  53. vellum_cli/tests/test_push.py +44 -0
  54. vellum_ee/workflows/display/nodes/base_node_display.py +2 -2
  55. {vellum_ai-0.13.27.dist-info → vellum_ai-0.14.0.dist-info}/LICENSE +0 -0
  56. {vellum_ai-0.13.27.dist-info → vellum_ai-0.14.0.dist-info}/WHEEL +0 -0
  57. {vellum_ai-0.13.27.dist-info → vellum_ai-0.14.0.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.13.27",
21
+ "X-Fern-SDK-Version": "0.14.0",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -4,11 +4,11 @@ from typing import Any, cast
4
4
 
5
5
  class _UndefMeta(type):
6
6
  def __repr__(cls) -> str:
7
- return "UNDEF"
7
+ return "undefined"
8
8
 
9
9
  def __getattribute__(cls, name: str) -> Any:
10
10
  if name == "__class__":
11
- # ensures that UNDEF.__class__ == UNDEF
11
+ # ensures that undefined.__class__ == undefined
12
12
  return cls
13
13
 
14
14
  return super().__getattribute__(name)
@@ -17,7 +17,12 @@ class _UndefMeta(type):
17
17
  return False
18
18
 
19
19
 
20
- class UNDEF(metaclass=_UndefMeta):
20
+ class undefined(metaclass=_UndefMeta):
21
+ """
22
+ A singleton class that represents an `undefined` value, mirroring the behavior of the `undefined`
23
+ value in TypeScript.
24
+ """
25
+
21
26
  pass
22
27
 
23
28
 
@@ -0,0 +1,2 @@
1
+ class InvalidExpressionException(Exception):
2
+ pass
@@ -1,6 +1,7 @@
1
1
  import pytest
2
2
 
3
3
  from vellum.workflows.descriptors.utils import resolve_value
4
+ from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
4
5
  from vellum.workflows.nodes.bases.base import BaseNode
5
6
  from vellum.workflows.references.constant import ConstantValueReference
6
7
  from vellum.workflows.state.base import BaseState
@@ -77,6 +78,24 @@ class DummyNode(BaseNode[FixtureState]):
77
78
  (FixtureState.zeta["foo"], "bar"),
78
79
  (ConstantValueReference(1), 1),
79
80
  (FixtureState.theta[0], "baz"),
81
+ (
82
+ ConstantValueReference(
83
+ WorkflowError(
84
+ message="This is a test",
85
+ code=WorkflowErrorCode.USER_DEFINED_ERROR,
86
+ )
87
+ ).contains("test"),
88
+ True,
89
+ ),
90
+ (
91
+ ConstantValueReference(
92
+ WorkflowError(
93
+ message="This is a test",
94
+ code=WorkflowErrorCode.USER_DEFINED_ERROR,
95
+ )
96
+ ).does_not_contain("test"),
97
+ False,
98
+ ),
80
99
  ],
81
100
  ids=[
82
101
  "or",
@@ -122,6 +141,8 @@ class DummyNode(BaseNode[FixtureState]):
122
141
  "accessor",
123
142
  "constants",
124
143
  "list_index",
144
+ "error_contains",
145
+ "error_does_not_contain",
125
146
  ],
126
147
  )
127
148
  def test_resolve_value__happy_path(descriptor, expected_value):
@@ -5,7 +5,7 @@ from typing import Any, Dict, Optional, Sequence, Set, TypeVar, Union, cast, ove
5
5
 
6
6
  from pydantic import BaseModel
7
7
 
8
- from vellum.workflows.constants import UNDEF
8
+ from vellum.workflows.constants import undefined
9
9
  from vellum.workflows.descriptors.base import BaseDescriptor
10
10
  from vellum.workflows.state.base import BaseState
11
11
 
@@ -93,10 +93,10 @@ def resolve_value(
93
93
 
94
94
  def is_unresolved(value: Any) -> bool:
95
95
  """
96
- Recursively checks if a value has an unresolved value, represented by UNDEF.
96
+ Recursively checks if a value has an unresolved value, represented by undefined.
97
97
  """
98
98
 
99
- if value is UNDEF:
99
+ if value is undefined:
100
100
  return True
101
101
 
102
102
  if dataclasses.is_dataclass(value):
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
- from typing import Dict
3
+ from typing import Any, Dict
4
4
 
5
5
  from vellum.client.types.vellum_error import VellumError
6
6
  from vellum.client.types.vellum_error_code_enum import VellumErrorCodeEnum
@@ -26,6 +26,9 @@ class WorkflowError:
26
26
  message: str
27
27
  code: WorkflowErrorCode
28
28
 
29
+ def __contains__(self, item: Any) -> bool:
30
+ return item in self.message
31
+
29
32
 
30
33
  _VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[VellumErrorCodeEnum, WorkflowErrorCode] = {
31
34
  "INVALID_REQUEST": WorkflowErrorCode.INVALID_INPUTS,
@@ -6,6 +6,7 @@ from pydantic import BaseModel, GetCoreSchemaHandler
6
6
  from pydantic_core import core_schema
7
7
 
8
8
  from vellum.workflows.descriptors.base import BaseDescriptor
9
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
9
10
  from vellum.workflows.descriptors.utils import resolve_value
10
11
  from vellum.workflows.state.base import BaseState
11
12
 
@@ -32,13 +33,13 @@ class AccessorExpression(BaseDescriptor[Any]):
32
33
 
33
34
  if dataclasses.is_dataclass(base):
34
35
  if isinstance(self._field, int):
35
- raise ValueError("Cannot access field by index on a dataclass")
36
+ raise InvalidExpressionException("Cannot access field by index on a dataclass")
36
37
 
37
38
  return getattr(base, self._field)
38
39
 
39
40
  if isinstance(base, BaseModel):
40
41
  if isinstance(self._field, int):
41
- raise ValueError("Cannot access field by index on a BaseModel")
42
+ raise InvalidExpressionException("Cannot access field by index on a BaseModel")
42
43
 
43
44
  return getattr(base, self._field)
44
45
 
@@ -49,7 +50,7 @@ class AccessorExpression(BaseDescriptor[Any]):
49
50
  index = int(self._field)
50
51
  return base[index]
51
52
 
52
- raise ValueError(f"Cannot get field {self._field} from {base}")
53
+ raise InvalidExpressionException(f"Cannot get field {self._field} from {base}")
53
54
 
54
55
  @classmethod
55
56
  def __get_pydantic_core_schema__(
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class BeginsWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return lhs.startswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -25,14 +26,14 @@ class BetweenExpression(BaseDescriptor[bool], Generic[_V, _S, _E]):
25
26
  def resolve(self, state: "BaseState") -> bool:
26
27
  value = resolve_value(self._value, state)
27
28
  if not isinstance(value, (int, float)):
28
- raise ValueError(f"Expected a numeric value, got: {value.__class__.__name__}")
29
+ raise InvalidExpressionException(f"Expected a numeric value, got: {value.__class__.__name__}")
29
30
 
30
31
  start = resolve_value(self._start, state)
31
32
  if not isinstance(start, (int, float)):
32
- raise ValueError(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
+ raise InvalidExpressionException(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
34
 
34
35
  end = resolve_value(self._end, state)
35
36
  if not isinstance(end, (int, float)):
36
- raise ValueError(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
+ raise InvalidExpressionException(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
38
 
38
39
  return start <= value <= end
@@ -1,6 +1,6 @@
1
1
  from typing import TypeVar, Union
2
2
 
3
- from vellum.workflows.constants import UNDEF
3
+ from vellum.workflows.constants import undefined
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
5
  from vellum.workflows.descriptors.utils import resolve_value
6
6
  from vellum.workflows.state.base import BaseState
@@ -27,7 +27,7 @@ class CoalesceExpression(BaseDescriptor[Union[LHS, RHS]]):
27
27
 
28
28
  def resolve(self, state: "BaseState") -> Union[LHS, RHS]:
29
29
  lhs = resolve_value(self._lhs, state)
30
- if lhs is not UNDEF and lhs is not None:
30
+ if lhs is not undefined and lhs is not None:
31
31
  return lhs
32
32
 
33
33
  return resolve_value(self._rhs, state)
@@ -1,7 +1,10 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
+ from vellum.workflows.constants import undefined
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
6
  from vellum.workflows.descriptors.utils import resolve_value
7
+ from vellum.workflows.errors.types import WorkflowError
5
8
  from vellum.workflows.state.base import BaseState
6
9
 
7
10
  LHS = TypeVar("LHS")
@@ -23,8 +26,13 @@ class ContainsExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
26
  # Support any type that implements the in operator
24
27
  # https://app.shortcut.com/vellum/story/4658
25
28
  lhs = resolve_value(self._lhs, state)
26
- if not isinstance(lhs, (list, tuple, set, dict, str)):
27
- raise ValueError(f"Expected a LHS that supported contains, got: {lhs.__class__.__name__}")
29
+ # assumes that lack of is also false
30
+ if lhs is undefined:
31
+ return False
32
+ if not isinstance(lhs, (list, tuple, set, dict, str, WorkflowError)):
33
+ raise InvalidExpressionException(
34
+ f"Expected a LHS that supported `contains`, got `{lhs.__class__.__name__}`"
35
+ )
28
36
 
29
37
  rhs = resolve_value(self._rhs, state)
30
38
  return rhs in lhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class DoesNotBeginWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return not lhs.startswith(rhs)
@@ -1,7 +1,9 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
6
+ from vellum.workflows.errors.types import WorkflowError
5
7
  from vellum.workflows.state.base import BaseState
6
8
 
7
9
  LHS = TypeVar("LHS")
@@ -23,8 +25,10 @@ class DoesNotContainExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
25
  # Support any type that implements the not in operator
24
26
  # https://app.shortcut.com/vellum/story/4658
25
27
  lhs = resolve_value(self._lhs, state)
26
- if not isinstance(lhs, (list, tuple, set, dict, str)):
27
- raise ValueError(f"Expected a LHS that supported contains, got: {lhs.__class__.__name__}")
28
+ if not isinstance(lhs, (list, tuple, set, dict, str, WorkflowError)):
29
+ raise InvalidExpressionException(
30
+ f"Expected a LHS that supported `contains`, got `{lhs.__class__.__name__}`"
31
+ )
28
32
 
29
33
  rhs = resolve_value(self._rhs, state)
30
34
  return rhs not in lhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class DoesNotEndWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return not lhs.endswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class EndsWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return lhs.endswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class GreaterThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs > rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs >= rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -26,6 +27,6 @@ class InExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
26
27
 
27
28
  rhs = resolve_value(self._rhs, state)
28
29
  if not isinstance(rhs, (list, tuple, set, dict, str)):
29
- raise ValueError(f"Expected a RHS that supported in, got: {rhs.__class__.__name__}")
30
+ raise InvalidExpressionException(f"Expected a RHS that supported in, got: {rhs.__class__.__name__}")
30
31
 
31
32
  return lhs in rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -19,6 +20,6 @@ class IsBlankExpression(BaseDescriptor[bool], Generic[_T]):
19
20
  def resolve(self, state: "BaseState") -> bool:
20
21
  expression = resolve_value(self._expression, state)
21
22
  if not isinstance(expression, str):
22
- raise ValueError(f"Expected a string expression, got: {expression.__class__.__name__}")
23
+ raise InvalidExpressionException(f"Expected a string expression, got: {expression.__class__.__name__}")
23
24
 
24
25
  return len(expression) == 0
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
- from vellum.workflows.constants import UNDEF
3
+ from vellum.workflows.constants import undefined
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
5
  from vellum.workflows.descriptors.utils import resolve_value
6
6
  from vellum.workflows.state.base import BaseState
@@ -19,4 +19,4 @@ class IsNilExpression(BaseDescriptor[bool], Generic[_T]):
19
19
 
20
20
  def resolve(self, state: "BaseState") -> bool:
21
21
  expression = resolve_value(self._expression, state)
22
- return expression is None or expression is UNDEF
22
+ return expression is None or expression is undefined
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -19,6 +20,6 @@ class IsNotBlankExpression(BaseDescriptor[bool], Generic[_T]):
19
20
  def resolve(self, state: "BaseState") -> bool:
20
21
  expression = resolve_value(self._expression, state)
21
22
  if not isinstance(expression, str):
22
- raise ValueError(f"Expected a string expression, got: {expression.__class__.__name__}")
23
+ raise InvalidExpressionException(f"Expected a string expression, got: {expression.__class__.__name__}")
23
24
 
24
25
  return len(expression) != 0
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
- from vellum.workflows.constants import UNDEF
3
+ from vellum.workflows.constants import undefined
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
5
  from vellum.workflows.descriptors.utils import resolve_value
6
6
  from vellum.workflows.state.base import BaseState
@@ -19,4 +19,4 @@ class IsNotNilExpression(BaseDescriptor[bool], Generic[_T]):
19
19
 
20
20
  def resolve(self, state: "BaseState") -> bool:
21
21
  expression = resolve_value(self._expression, state)
22
- return expression is not None and expression is not UNDEF
22
+ return expression is not None and expression is not undefined
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
- from vellum.workflows.constants import UNDEF
3
+ from vellum.workflows.constants import undefined
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
5
  from vellum.workflows.descriptors.utils import resolve_value
6
6
  from vellum.workflows.state.base import BaseState
@@ -19,4 +19,4 @@ class IsNotUndefinedExpression(BaseDescriptor[bool], Generic[_T]):
19
19
 
20
20
  def resolve(self, state: "BaseState") -> bool:
21
21
  expression = resolve_value(self._expression, state)
22
- return expression is not UNDEF
22
+ return expression is not undefined
@@ -1,6 +1,6 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
- from vellum.workflows.constants import UNDEF
3
+ from vellum.workflows.constants import undefined
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
5
  from vellum.workflows.descriptors.utils import resolve_value
6
6
  from vellum.workflows.state.base import BaseState
@@ -19,4 +19,4 @@ class IsUndefinedExpression(BaseDescriptor[bool], Generic[_T]):
19
19
 
20
20
  def resolve(self, state: "BaseState") -> bool:
21
21
  expression = resolve_value(self._expression, state)
22
- return expression is UNDEF
22
+ return expression is undefined
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs < rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs <= rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -25,14 +26,14 @@ class NotBetweenExpression(BaseDescriptor[bool], Generic[_V, _S, _E]):
25
26
  def resolve(self, state: "BaseState") -> bool:
26
27
  value = resolve_value(self._value, state)
27
28
  if not isinstance(value, (int, float)):
28
- raise ValueError(f"Expected a numeric value, got: {value.__class__.__name__}")
29
+ raise InvalidExpressionException(f"Expected a numeric value, got: {value.__class__.__name__}")
29
30
 
30
31
  start = resolve_value(self._start, state)
31
32
  if not isinstance(start, (int, float)):
32
- raise ValueError(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
+ raise InvalidExpressionException(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
34
 
34
35
  end = resolve_value(self._end, state)
35
36
  if not isinstance(end, (int, float)):
36
- raise ValueError(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
+ raise InvalidExpressionException(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
38
 
38
39
  return value < start or value > end
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -26,6 +27,6 @@ class NotInExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
26
27
 
27
28
  rhs = resolve_value(self._rhs, state)
28
29
  if not isinstance(rhs, (list, tuple, set, dict, str)):
29
- raise ValueError(f"Expected a RHS that supported contains, got: {rhs.__class__.__name__}")
30
+ raise InvalidExpressionException(f"Expected a RHS that supported contains, got: {rhs.__class__.__name__}")
30
31
 
31
32
  return lhs not in rhs
@@ -5,7 +5,7 @@ from types import MappingProxyType
5
5
  from uuid import UUID
6
6
  from typing import Any, Dict, Generic, Iterator, Optional, Set, Tuple, Type, TypeVar, Union, cast, get_args
7
7
 
8
- from vellum.workflows.constants import UNDEF
8
+ from vellum.workflows.constants import undefined
9
9
  from vellum.workflows.descriptors.base import BaseDescriptor
10
10
  from vellum.workflows.descriptors.utils import is_unresolved, resolve_value
11
11
  from vellum.workflows.errors.types import WorkflowErrorCode
@@ -43,7 +43,23 @@ class BaseNodeMeta(type):
43
43
  # TODO: Inherit the inner Output classes from every base class.
44
44
  # https://app.shortcut.com/vellum/story/4007/support-auto-inheriting-parent-node-outputs
45
45
 
46
- if "Outputs" not in dct:
46
+ if "Outputs" in dct:
47
+ outputs_class = dct["Outputs"]
48
+ if not any(issubclass(base, BaseOutputs) for base in outputs_class.__bases__):
49
+ parent_outputs_class = next(
50
+ (base.Outputs for base in bases if hasattr(base, "Outputs")),
51
+ BaseOutputs, # Default to BaseOutputs only if no parent has Outputs
52
+ )
53
+
54
+ # Filter out object from bases while preserving other inheritance
55
+ filtered_bases = tuple(base for base in outputs_class.__bases__ if base is not object)
56
+
57
+ dct["Outputs"] = type(
58
+ f"{name}.Outputs",
59
+ (parent_outputs_class,) + filtered_bases,
60
+ {**outputs_class.__dict__, "__module__": dct["__module__"]},
61
+ )
62
+ else:
47
63
  for base in reversed(bases):
48
64
  if hasattr(base, "Outputs"):
49
65
  dct["Outputs"] = type(
@@ -165,7 +181,7 @@ class BaseNodeMeta(type):
165
181
  if attr_name in yielded_attr_names:
166
182
  continue
167
183
 
168
- attr_value = getattr(resolved_cls, attr_name, UNDEF)
184
+ attr_value = getattr(resolved_cls, attr_name, undefined)
169
185
  if not isinstance(attr_value, NodeReference):
170
186
  continue
171
187
 
@@ -255,17 +271,15 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
255
271
  return False
256
272
 
257
273
  if cls.merge_behavior == MergeBehavior.AWAIT_ATTRIBUTES:
258
- is_ready = True
259
274
  for descriptor in cls.node_class:
260
275
  if not descriptor.instance:
261
276
  continue
262
277
 
263
278
  resolved_value = resolve_value(descriptor.instance, state, path=descriptor.name)
264
279
  if is_unresolved(resolved_value):
265
- is_ready = False
266
- break
280
+ return False
267
281
 
268
- return is_ready
282
+ return True
269
283
 
270
284
  if cls.merge_behavior == MergeBehavior.AWAIT_ANY:
271
285
  return True