vellum-ai 0.14.8__py3-none-any.whl → 0.14.9__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 (29) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/descriptors/base.py +6 -0
  3. vellum/workflows/descriptors/tests/test_utils.py +14 -0
  4. vellum/workflows/events/tests/test_event.py +40 -0
  5. vellum/workflows/events/workflow.py +20 -1
  6. vellum/workflows/expressions/greater_than.py +15 -8
  7. vellum/workflows/expressions/greater_than_or_equal_to.py +14 -8
  8. vellum/workflows/expressions/less_than.py +14 -8
  9. vellum/workflows/expressions/less_than_or_equal_to.py +14 -8
  10. vellum/workflows/expressions/parse_json.py +30 -0
  11. vellum/workflows/expressions/tests/__init__.py +0 -0
  12. vellum/workflows/expressions/tests/test_expressions.py +310 -0
  13. vellum/workflows/expressions/tests/test_parse_json.py +31 -0
  14. vellum/workflows/nodes/bases/base.py +5 -2
  15. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +6 -7
  16. vellum/workflows/nodes/displayable/code_execution_node/node.py +18 -8
  17. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +53 -0
  18. vellum/workflows/runner/runner.py +33 -4
  19. vellum/workflows/state/encoder.py +2 -1
  20. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.9.dist-info}/METADATA +1 -1
  21. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.9.dist-info}/RECORD +29 -25
  22. vellum_ee/workflows/display/nodes/base_node_display.py +4 -0
  23. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +31 -0
  24. vellum_ee/workflows/display/types.py +1 -14
  25. vellum_ee/workflows/display/workflows/base_workflow_display.py +2 -6
  26. vellum_ee/workflows/tests/test_server.py +1 -0
  27. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.9.dist-info}/LICENSE +0 -0
  28. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.9.dist-info}/WHEEL +0 -0
  29. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.9.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.14.8",
21
+ "X-Fern-SDK-Version": "0.14.9",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -29,6 +29,7 @@ if TYPE_CHECKING:
29
29
  from vellum.workflows.expressions.not_between import NotBetweenExpression
30
30
  from vellum.workflows.expressions.not_in import NotInExpression
31
31
  from vellum.workflows.expressions.or_ import OrExpression
32
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
32
33
  from vellum.workflows.nodes.bases import BaseNode
33
34
  from vellum.workflows.state.base import BaseState
34
35
 
@@ -349,3 +350,8 @@ class BaseDescriptor(Generic[_T]):
349
350
  from vellum.workflows.expressions.is_not_blank import IsNotBlankExpression
350
351
 
351
352
  return IsNotBlankExpression(expression=self)
353
+
354
+ def parse_json(self) -> "ParseJsonExpression[_T]":
355
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
356
+
357
+ return ParseJsonExpression(expression=self)
@@ -96,6 +96,13 @@ class DummyNode(BaseNode[FixtureState]):
96
96
  ).does_not_contain("test"),
97
97
  False,
98
98
  ),
99
+ (ConstantValueReference('{"foo": "bar"}').parse_json(), {"foo": "bar"}),
100
+ (ConstantValueReference('{"foo": "bar"}').parse_json()["foo"], "bar"),
101
+ (ConstantValueReference("[1, 2, 3]").parse_json(), [1, 2, 3]),
102
+ (ConstantValueReference("[1, 2, 3]").parse_json()[0], 1),
103
+ (ConstantValueReference(b'{"foo": "bar"}').parse_json(), {"foo": "bar"}),
104
+ (ConstantValueReference(bytearray(b'{"foo": "bar"}')).parse_json(), {"foo": "bar"}),
105
+ (ConstantValueReference(b'{"key": "\xf0\x9f\x8c\x9f"}').parse_json(), {"key": "🌟"}),
99
106
  ],
100
107
  ids=[
101
108
  "or",
@@ -143,6 +150,13 @@ class DummyNode(BaseNode[FixtureState]):
143
150
  "list_index",
144
151
  "error_contains",
145
152
  "error_does_not_contain",
153
+ "parse_json_constant",
154
+ "parse_json_accessor",
155
+ "parse_json_list",
156
+ "parse_json_list_index",
157
+ "parse_json_bytes",
158
+ "parse_json_bytearray",
159
+ "parse_json_bytes_with_utf8_chars",
146
160
  ],
147
161
  )
148
162
  def test_resolve_value__happy_path(descriptor, expected_value):
@@ -4,6 +4,7 @@ from uuid import UUID
4
4
 
5
5
  from deepdiff import DeepDiff
6
6
 
7
+ from vellum.workflows.constants import undefined
7
8
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
8
9
  from vellum.workflows.events.node import (
9
10
  NodeExecutionFulfilledBody,
@@ -86,6 +87,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
86
87
  "inputs": {
87
88
  "foo": "bar",
88
89
  },
90
+ "display_context": None,
89
91
  },
90
92
  "parent": None,
91
93
  },
@@ -330,6 +332,43 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
330
332
  "parent": None,
331
333
  },
332
334
  ),
335
+ (
336
+ NodeExecutionFulfilledEvent(
337
+ id=UUID("123e4567-e89b-12d3-a456-426614174000"),
338
+ timestamp=datetime(2024, 1, 1, 12, 0, 0),
339
+ trace_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
340
+ span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
341
+ body=NodeExecutionFulfilledBody(
342
+ node_definition=MockNode,
343
+ outputs=MockNode.Outputs(
344
+ example=undefined, # type: ignore[arg-type]
345
+ ),
346
+ invoked_ports={MockNode.Ports.default},
347
+ ),
348
+ ),
349
+ {
350
+ "id": "123e4567-e89b-12d3-a456-426614174000",
351
+ "api_version": "2024-10-25",
352
+ "timestamp": "2024-01-01T12:00:00",
353
+ "trace_id": "123e4567-e89b-12d3-a456-426614174000",
354
+ "span_id": "123e4567-e89b-12d3-a456-426614174000",
355
+ "name": "node.execution.fulfilled",
356
+ "body": {
357
+ "node_definition": {
358
+ "id": mock_node_uuid,
359
+ "name": "MockNode",
360
+ "module": module_root + ["events", "tests", "test_event"],
361
+ },
362
+ "outputs": {},
363
+ "invoked_ports": [
364
+ {
365
+ "name": "default",
366
+ }
367
+ ],
368
+ },
369
+ "parent": None,
370
+ },
371
+ ),
333
372
  ],
334
373
  ids=[
335
374
  "workflow.execution.initiated",
@@ -339,6 +378,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
339
378
  "workflow.execution.rejected",
340
379
  "node.execution.streaming",
341
380
  "node.execution.fulfilled",
381
+ "fulfilled_node_with_undefined_outputs",
342
382
  ],
343
383
  )
344
384
  def test_event_serialization(event, expected_json):
@@ -1,4 +1,5 @@
1
- from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Iterable, Literal, Type, Union
1
+ from uuid import UUID
2
+ from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Iterable, Literal, Optional, Type, Union
2
3
 
3
4
  from pydantic import field_serializer
4
5
 
@@ -38,8 +39,26 @@ class _BaseWorkflowEvent(BaseEvent):
38
39
  return self.body.workflow_definition
39
40
 
40
41
 
42
+ class NodeDisplay(UniversalBaseModel):
43
+ input_display: Dict[str, UUID]
44
+ output_display: Dict[str, UUID]
45
+ port_display: Dict[str, UUID]
46
+
47
+
48
+ class WorkflowEventDisplayContext(UniversalBaseModel):
49
+ node_displays: Dict[str, NodeDisplay]
50
+ workflow_inputs: Dict[str, UUID]
51
+ workflow_outputs: Dict[str, UUID]
52
+
53
+
41
54
  class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsType]):
42
55
  inputs: InputsType
56
+ # It is still the responsibility of the workflow server to populate this context. The SDK's
57
+ # Workflow Runner will always leave this field None.
58
+ #
59
+ # It's conceivable in a future where all `id`s are agnostic to display and reside in a third location,
60
+ # that the Workflow Runner can begin populating this field then.
61
+ display_context: Optional[WorkflowEventDisplayContext] = None
43
62
 
44
63
  @field_serializer("inputs")
45
64
  def serialize_inputs(self, inputs: InputsType, _info: Any) -> Dict[str, Any]:
@@ -1,10 +1,21 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
5
6
  from vellum.workflows.descriptors.utils import resolve_value
6
7
  from vellum.workflows.state.base import BaseState
7
8
 
9
+
10
+ @runtime_checkable
11
+ class SupportsGreaterThan(Protocol):
12
+ def __gt__(self, other: Any) -> bool: ...
13
+
14
+
15
+ def has_gt(obj: Any) -> TypeGuard[SupportsGreaterThan]:
16
+ return hasattr(obj, "__gt__")
17
+
18
+
8
19
  LHS = TypeVar("LHS")
9
20
  RHS = TypeVar("RHS")
10
21
 
@@ -21,14 +32,10 @@ class GreaterThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
32
  self._rhs = rhs
22
33
 
23
34
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the > operator
25
- # https://app.shortcut.com/vellum/story/4658
26
35
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
36
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
37
+
38
+ if not has_gt(lhs):
39
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '>' operator")
33
40
 
34
41
  return lhs > rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsGreaterThanOrEqualTo(Protocol):
15
+ def __ge__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_ge(obj: Any) -> TypeGuard[SupportsGreaterThanOrEqualTo]:
19
+ return hasattr(obj, "__ge__")
20
+
21
+
12
22
  class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the >= operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_ge(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '>=' operator")
33
39
 
34
40
  return lhs >= rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsLessThan(Protocol):
15
+ def __lt__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_lt(obj: Any) -> TypeGuard[SupportsLessThan]:
19
+ return hasattr(obj, "__lt__")
20
+
21
+
12
22
  class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the < operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_lt(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '<' operator")
33
39
 
34
40
  return lhs < rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsLessThanOrEqualTo(Protocol):
15
+ def __le__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_le(obj: Any) -> TypeGuard[SupportsLessThanOrEqualTo]:
19
+ return hasattr(obj, "__le__")
20
+
21
+
12
22
  class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the <= operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_le(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '<=' operator")
33
39
 
34
40
  return lhs <= rhs
@@ -0,0 +1,30 @@
1
+ import json
2
+ from typing import Any, Generic, TypeVar, Union
3
+
4
+ from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
6
+ from vellum.workflows.descriptors.utils import resolve_value
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+ _T = TypeVar("_T")
10
+
11
+
12
+ class ParseJsonExpression(BaseDescriptor[Any], Generic[_T]):
13
+ def __init__(
14
+ self,
15
+ *,
16
+ expression: Union[BaseDescriptor[_T], _T],
17
+ ) -> None:
18
+ super().__init__(name=f"parse_json({expression})", types=(Any,)) # type: ignore[arg-type]
19
+ self._expression = expression
20
+
21
+ def resolve(self, state: "BaseState") -> Any:
22
+ value = resolve_value(self._expression, state)
23
+
24
+ if not isinstance(value, (str, bytes, bytearray)):
25
+ raise InvalidExpressionException(f"Expected a string, but got {value} of type {type(value)}")
26
+
27
+ try:
28
+ return json.loads(value)
29
+ except json.JSONDecodeError as e:
30
+ raise InvalidExpressionException(f"Failed to parse JSON: {e}") from e
File without changes
@@ -0,0 +1,310 @@
1
+ import pytest
2
+
3
+ from vellum.workflows.expressions.greater_than import GreaterThanExpression
4
+ from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
5
+ from vellum.workflows.expressions.less_than import LessThanExpression
6
+ from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+
10
+ class Comparable:
11
+ """A custom class with two values, where comparisons use a computed metric (multiplication)."""
12
+
13
+ def __init__(self, value1, value2):
14
+ self.value1 = value1 # First numerical value
15
+ self.value2 = value2 # Second numerical value
16
+
17
+ def computed_value(self):
18
+ return self.value1 * self.value2 # Multiply for comparison
19
+
20
+ def __ge__(self, other):
21
+ if isinstance(other, Comparable):
22
+ return self.computed_value() >= other.computed_value()
23
+ elif isinstance(other, (int, float)):
24
+ return self.computed_value() >= other
25
+ return NotImplemented
26
+
27
+ def __gt__(self, other):
28
+ if isinstance(other, Comparable):
29
+ return self.computed_value() > other.computed_value()
30
+ elif isinstance(other, (int, float)):
31
+ return self.computed_value() > other
32
+ return NotImplemented
33
+
34
+ def __le__(self, other):
35
+ if isinstance(other, Comparable):
36
+ return self.computed_value() <= other.computed_value()
37
+ elif isinstance(other, (int, float)):
38
+ return self.computed_value() <= other
39
+ return NotImplemented
40
+
41
+ def __lt__(self, other):
42
+ if isinstance(other, Comparable):
43
+ return self.computed_value() < other.computed_value()
44
+ elif isinstance(other, (int, float)):
45
+ return self.computed_value() < other
46
+ return NotImplemented
47
+
48
+
49
+ class NonComparable:
50
+ """A custom class that does not support comparisons."""
51
+
52
+ def __init__(self, value1, value2):
53
+ self.value1 = value1
54
+ self.value2 = value2
55
+
56
+
57
+ class TestState(BaseState):
58
+ pass
59
+
60
+
61
+ def test_greater_than_or_equal_to():
62
+ # GIVEN objects with two values
63
+ obj1 = Comparable(4, 5) # Computed: 4 × 5 = 20
64
+ obj2 = Comparable(2, 10) # Computed: 2 × 10 = 20
65
+ obj3 = Comparable(3, 6) # Computed: 3 × 6 = 18
66
+ obj4 = Comparable(5, 5) # Computed: 5 × 5 = 25
67
+
68
+ state = TestState()
69
+
70
+ # WHEN comparing objects
71
+ assert GreaterThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state) is True # 20 >= 20
72
+ assert GreaterThanOrEqualToExpression(lhs=obj1, rhs=obj3).resolve(state) is True # 20 >= 18
73
+ assert GreaterThanOrEqualToExpression(lhs=obj3, rhs=obj4).resolve(state) is False # 18 < 25
74
+
75
+ # WHEN comparing to raw numbers
76
+ assert GreaterThanOrEqualToExpression(lhs=obj1, rhs=19).resolve(state) is True # 20 >= 19
77
+ assert GreaterThanOrEqualToExpression(lhs=obj3, rhs=20).resolve(state) is False # 18 < 20
78
+
79
+
80
+ def test_greater_than_or_equal_to_invalid():
81
+ # GIVEN objects with two values
82
+ obj1 = Comparable(4, 5)
83
+ obj2 = "invalid"
84
+
85
+ state = TestState()
86
+
87
+ # WHEN comparing objects with incompatible types
88
+ with pytest.raises(TypeError) as exc_info:
89
+ GreaterThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state)
90
+
91
+ # THEN the expected error is raised
92
+ assert str(exc_info.value) == "'>=' not supported between instances of 'Comparable' and 'str'"
93
+
94
+ # WHEN comparing objects with incompatible types
95
+ with pytest.raises(TypeError) as exc_info:
96
+ GreaterThanOrEqualToExpression(lhs=obj2, rhs=obj1).resolve(state)
97
+
98
+ # THEN the expected error is raised
99
+ assert str(exc_info.value) == "'>=' not supported between instances of 'str' and 'Comparable'"
100
+
101
+
102
+ def test_greater_than_or_equal_to_non_comparable():
103
+ # GIVEN objects with two values
104
+ obj1 = Comparable(4, 5)
105
+ obj2 = NonComparable(2, 10)
106
+
107
+ state = TestState()
108
+
109
+ # WHEN comparing objects with incompatible types
110
+ with pytest.raises(TypeError) as exc_info:
111
+ GreaterThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state)
112
+
113
+ # THEN the expected error is raised
114
+ assert str(exc_info.value) == "'>=' not supported between instances of 'Comparable' and 'NonComparable'"
115
+
116
+ # WHEN comparing objects with incompatible types
117
+ with pytest.raises(TypeError) as exc_info:
118
+ GreaterThanOrEqualToExpression(lhs=obj2, rhs=obj1).resolve(state)
119
+
120
+ # THEN the expected error is raised
121
+ assert str(exc_info.value) == "'>=' not supported between instances of 'NonComparable' and 'Comparable'"
122
+
123
+
124
+ def test_greater_than():
125
+ # GIVEN objects with two values
126
+ obj1 = Comparable(4, 5) # Computed: 4 × 5 = 20
127
+ obj2 = Comparable(2, 10) # Computed: 2 × 10 = 20
128
+ obj3 = Comparable(3, 6) # Computed: 3 × 6 = 18
129
+ obj4 = Comparable(5, 5) # Computed: 5 × 5 = 25
130
+
131
+ state = TestState()
132
+
133
+ # WHEN comparing objects
134
+ assert GreaterThanExpression(lhs=obj1, rhs=obj2).resolve(state) is False # 20 > 20
135
+ assert GreaterThanExpression(lhs=obj1, rhs=obj3).resolve(state) is True # 20 > 18
136
+ assert GreaterThanExpression(lhs=obj3, rhs=obj4).resolve(state) is False # 18 < 25
137
+
138
+ # WHEN comparing to raw numbers
139
+ assert GreaterThanExpression(lhs=obj1, rhs=19).resolve(state) is True # 20 > 19
140
+ assert GreaterThanExpression(lhs=obj3, rhs=20).resolve(state) is False # 18 < 20
141
+
142
+
143
+ def test_greater_than_invalid():
144
+ # GIVEN objects with two values
145
+ obj1 = Comparable(4, 5)
146
+ obj2 = "invalid"
147
+
148
+ state = TestState()
149
+
150
+ # WHEN comparing objects with incompatible types
151
+ with pytest.raises(TypeError) as exc_info:
152
+ GreaterThanExpression(lhs=obj1, rhs=obj2).resolve(state)
153
+
154
+ # THEN the expected error is raised
155
+ assert str(exc_info.value) == "'>' not supported between instances of 'Comparable' and 'str'"
156
+
157
+ # WHEN comparing objects with incompatible types
158
+ with pytest.raises(TypeError) as exc_info:
159
+ GreaterThanExpression(lhs=obj2, rhs=obj1).resolve(state)
160
+
161
+ # THEN the expected error is raised
162
+ assert str(exc_info.value) == "'>' not supported between instances of 'str' and 'Comparable'"
163
+
164
+
165
+ def test_greater_than_non_comparable():
166
+ # GIVEN objects with two values
167
+ obj1 = Comparable(4, 5)
168
+ obj2 = NonComparable(2, 10)
169
+
170
+ state = TestState()
171
+
172
+ # WHEN comparing objects with incompatible types
173
+ with pytest.raises(TypeError) as exc_info:
174
+ GreaterThanExpression(lhs=obj1, rhs=obj2).resolve(state)
175
+
176
+ # THEN the expected error is raised
177
+ assert str(exc_info.value) == "'>' not supported between instances of 'Comparable' and 'NonComparable'"
178
+
179
+ # WHEN comparing objects with incompatible types
180
+ with pytest.raises(TypeError) as exc_info:
181
+ GreaterThanExpression(lhs=obj2, rhs=obj1).resolve(state)
182
+
183
+ # THEN the expected error is raised
184
+ assert str(exc_info.value) == "'>' not supported between instances of 'NonComparable' and 'Comparable'"
185
+
186
+
187
+ def test_less_than_or_equal_to():
188
+ # GIVEN objects with two values
189
+ obj1 = Comparable(4, 5) # Computed: 4 × 5 = 20
190
+ obj2 = Comparable(2, 10) # Computed: 2 × 10 = 20
191
+ obj3 = Comparable(3, 6) # Computed: 3 × 6 = 18
192
+ obj4 = Comparable(5, 5) # Computed: 5 × 5 = 25
193
+
194
+ state = TestState()
195
+
196
+ # WHEN comparing objects
197
+ assert LessThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state) is True # 20 <= 20
198
+ assert LessThanOrEqualToExpression(lhs=obj1, rhs=obj3).resolve(state) is False # 20 > 18
199
+ assert LessThanOrEqualToExpression(lhs=obj3, rhs=obj4).resolve(state) is True # 18 <= 25
200
+
201
+ # WHEN comparing to raw numbers
202
+ assert LessThanOrEqualToExpression(lhs=obj1, rhs=21).resolve(state) is True # 20 <= 21
203
+ assert LessThanOrEqualToExpression(lhs=obj3, rhs=17).resolve(state) is False # 18 > 17
204
+
205
+
206
+ def test_less_than_or_equal_to_invalid():
207
+ # GIVEN objects with two values
208
+ obj1 = Comparable(4, 5)
209
+ obj2 = "invalid"
210
+
211
+ state = TestState()
212
+
213
+ # WHEN comparing objects with incompatible types
214
+ with pytest.raises(TypeError) as exc_info:
215
+ LessThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state)
216
+
217
+ # THEN the expected error is raised
218
+ assert str(exc_info.value) == "'<=' not supported between instances of 'Comparable' and 'str'"
219
+
220
+ # WHEN comparing objects with incompatible types
221
+ with pytest.raises(TypeError) as exc_info:
222
+ LessThanOrEqualToExpression(lhs=obj2, rhs=obj1).resolve(state)
223
+
224
+ # THEN the expected error is raised
225
+ assert str(exc_info.value) == "'<=' not supported between instances of 'str' and 'Comparable'"
226
+
227
+
228
+ def test_less_than_or_equal_to_non_comparable():
229
+ # GIVEN objects with two values
230
+ obj1 = Comparable(4, 5)
231
+ obj2 = NonComparable(2, 10)
232
+
233
+ state = TestState()
234
+
235
+ # WHEN comparing objects with incompatible types
236
+ with pytest.raises(TypeError) as exc_info:
237
+ LessThanOrEqualToExpression(lhs=obj1, rhs=obj2).resolve(state)
238
+
239
+ # THEN the expected error is raised
240
+ assert str(exc_info.value) == "'<=' not supported between instances of 'Comparable' and 'NonComparable'"
241
+
242
+ # WHEN comparing objects with incompatible types
243
+ with pytest.raises(TypeError) as exc_info:
244
+ LessThanOrEqualToExpression(lhs=obj2, rhs=obj1).resolve(state)
245
+
246
+ # THEN the expected error is raised
247
+ assert str(exc_info.value) == "'<=' not supported between instances of 'NonComparable' and 'Comparable'"
248
+
249
+
250
+ def test_less_than():
251
+ # GIVEN objects with two values
252
+ obj1 = Comparable(4, 5) # Computed: 4 × 5 = 20
253
+ obj2 = Comparable(2, 10) # Computed: 2 × 10 = 20
254
+ obj3 = Comparable(3, 6) # Computed: 3 × 6 = 18
255
+ obj4 = Comparable(5, 5) # Computed: 5 × 5 = 25
256
+
257
+ state = TestState()
258
+
259
+ # WHEN comparing objects
260
+ assert LessThanExpression(lhs=obj1, rhs=obj2).resolve(state) is False # 20 < 20
261
+ assert LessThanExpression(lhs=obj1, rhs=obj3).resolve(state) is False # 20 < 18
262
+ assert LessThanExpression(lhs=obj3, rhs=obj4).resolve(state) is True # 18 < 25
263
+
264
+ # WHEN comparing to raw numbers
265
+ assert LessThanExpression(lhs=obj1, rhs=21).resolve(state) is True # 20 < 21
266
+ assert LessThanExpression(lhs=obj3, rhs=17).resolve(state) is False # 18 > 17
267
+
268
+
269
+ def test_less_than_invalid():
270
+ # GIVEN objects with two values
271
+ obj1 = Comparable(4, 5)
272
+ obj2 = "invalid"
273
+
274
+ state = TestState()
275
+
276
+ # WHEN comparing objects with incompatible types
277
+ with pytest.raises(TypeError) as exc_info:
278
+ LessThanExpression(lhs=obj1, rhs=obj2).resolve(state)
279
+
280
+ # THEN the expected error is raised
281
+ assert str(exc_info.value) == "'<' not supported between instances of 'Comparable' and 'str'"
282
+
283
+ # WHEN comparing objects with incompatible types
284
+ with pytest.raises(TypeError) as exc_info:
285
+ LessThanExpression(lhs=obj2, rhs=obj1).resolve(state)
286
+
287
+ # THEN the expected error is raised
288
+ assert str(exc_info.value) == "'<' not supported between instances of 'str' and 'Comparable'"
289
+
290
+
291
+ def test_less_than_non_comparable():
292
+ # GIVEN objects with two values
293
+ obj1 = Comparable(4, 5)
294
+ obj2 = NonComparable(2, 10)
295
+
296
+ state = TestState()
297
+
298
+ # WHEN comparing objects with incompatible types
299
+ with pytest.raises(TypeError) as exc_info:
300
+ LessThanExpression(lhs=obj1, rhs=obj2).resolve(state)
301
+
302
+ # THEN the expected error is raised
303
+ assert str(exc_info.value) == "'<' not supported between instances of 'Comparable' and 'NonComparable'"
304
+
305
+ # WHEN comparing objects with incompatible types
306
+ with pytest.raises(TypeError) as exc_info:
307
+ LessThanExpression(lhs=obj2, rhs=obj1).resolve(state)
308
+
309
+ # THEN the expected error is raised
310
+ assert str(exc_info.value) == "'<' not supported between instances of 'NonComparable' and 'Comparable'"
@@ -0,0 +1,31 @@
1
+ import pytest
2
+
3
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
+ from vellum.workflows.references.constant import ConstantValueReference
5
+ from vellum.workflows.state.base import BaseState
6
+
7
+
8
+ def test_parse_json_invalid_json():
9
+ # GIVEN an invalid JSON string
10
+ state = BaseState()
11
+ expression = ConstantValueReference('{"key": value}').parse_json()
12
+
13
+ # WHEN we attempt to resolve the expression
14
+ with pytest.raises(InvalidExpressionException) as exc_info:
15
+ expression.resolve(state)
16
+
17
+ # THEN an exception should be raised
18
+ assert "Failed to parse JSON" in str(exc_info.value)
19
+
20
+
21
+ def test_parse_json_invalid_type():
22
+ # GIVEN a non-string value
23
+ state = BaseState()
24
+ expression = ConstantValueReference(123).parse_json()
25
+
26
+ # WHEN we attempt to resolve the expression
27
+ with pytest.raises(InvalidExpressionException) as exc_info:
28
+ expression.resolve(state)
29
+
30
+ # THEN an exception should be raised
31
+ assert "Expected a string, but got 123 of type <class 'int'>" == str(exc_info.value)
@@ -318,9 +318,12 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
318
318
  original_base = get_original_base(self.__class__)
319
319
 
320
320
  args = get_args(original_base)
321
- state_type = args[0]
322
321
 
323
- if isinstance(state_type, TypeVar):
322
+ if args and len(args) > 0:
323
+ state_type = args[0]
324
+ if isinstance(state_type, TypeVar):
325
+ state_type = BaseState
326
+ else:
324
327
  state_type = BaseState
325
328
 
326
329
  self.state = state_type()
@@ -5,7 +5,6 @@ from vellum.client.core.api_error import ApiError
5
5
  from vellum.workflows.constants import APIRequestMethod, AuthorizationType
6
6
  from vellum.workflows.exceptions import NodeException
7
7
  from vellum.workflows.nodes import APINode
8
- from vellum.workflows.state import BaseState
9
8
  from vellum.workflows.types.core import VellumSecret
10
9
 
11
10
 
@@ -29,7 +28,7 @@ def test_run_workflow__secrets(vellum_client):
29
28
  }
30
29
  bearer_token_value = VellumSecret(name="secret")
31
30
 
32
- node = SimpleBaseAPINode(state=BaseState())
31
+ node = SimpleBaseAPINode()
33
32
  terminal = node.run()
34
33
 
35
34
  assert vellum_client.execute_api.call_count == 1
@@ -39,7 +38,7 @@ def test_run_workflow__secrets(vellum_client):
39
38
 
40
39
 
41
40
  def test_api_node_raises_error_when_api_call_fails(vellum_client):
42
- # Mock the vellum_client to raise an ApiError
41
+ # GIVEN an API call that fails
43
42
  vellum_client.execute_api.side_effect = ApiError(status_code=400, body="API Error")
44
43
 
45
44
  class SimpleAPINode(APINode):
@@ -54,14 +53,14 @@ def test_api_node_raises_error_when_api_call_fails(vellum_client):
54
53
  }
55
54
  bearer_token_value = VellumSecret(name="api_key")
56
55
 
57
- node = SimpleAPINode(state=BaseState())
56
+ node = SimpleAPINode()
58
57
 
59
- # Assert that the NodeException is raised
58
+ # WHEN we run the node
60
59
  with pytest.raises(NodeException) as excinfo:
61
60
  node.run()
62
61
 
63
- # Verify that the exception contains some error message
62
+ # THEN an exception should be raised
64
63
  assert "Failed to prepare HTTP request" in str(excinfo.value)
65
64
 
66
- # Verify the vellum_client was called
65
+ # AND the API call should have been made
67
66
  assert vellum_client.execute_api.call_count == 1
@@ -19,6 +19,7 @@ from vellum import (
19
19
  VellumError,
20
20
  VellumValue,
21
21
  )
22
+ from vellum.client.core.api_error import ApiError
22
23
  from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInput
23
24
  from vellum.core import RequestOptions
24
25
  from vellum.workflows.errors.types import WorkflowErrorCode
@@ -103,14 +104,23 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
103
104
  input_values = self._compile_code_inputs()
104
105
  expected_output_type = primitive_type_to_vellum_variable_type(output_type)
105
106
 
106
- code_execution_result = self._context.vellum_client.execute_code(
107
- input_values=input_values,
108
- code=code,
109
- runtime=self.runtime,
110
- output_type=expected_output_type,
111
- packages=self.packages or [],
112
- request_options=self.request_options,
113
- )
107
+ try:
108
+ code_execution_result = self._context.vellum_client.execute_code(
109
+ input_values=input_values,
110
+ code=code,
111
+ runtime=self.runtime,
112
+ output_type=expected_output_type,
113
+ packages=self.packages or [],
114
+ request_options=self.request_options,
115
+ )
116
+ except ApiError as e:
117
+ if e.status_code == 400 and isinstance(e.body, dict) and "message" in e.body:
118
+ raise NodeException(
119
+ message=e.body["message"],
120
+ code=WorkflowErrorCode.INVALID_INPUTS,
121
+ )
122
+
123
+ raise
114
124
 
115
125
  if code_execution_result.output.type != expected_output_type:
116
126
  actual_type = code_execution_result.output.type
@@ -5,6 +5,7 @@ from typing import Any, List, Union
5
5
  from pydantic import BaseModel
6
6
 
7
7
  from vellum import CodeExecutorResponse, NumberVellumValue, StringInput, StringVellumValue
8
+ from vellum.client.errors.bad_request_error import BadRequestError
8
9
  from vellum.client.types.chat_message import ChatMessage
9
10
  from vellum.client.types.code_execution_package import CodeExecutionPackage
10
11
  from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInput
@@ -17,6 +18,7 @@ from vellum.workflows.inputs.base import BaseInputs
17
18
  from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
18
19
  from vellum.workflows.references.vellum_secret import VellumSecretReference
19
20
  from vellum.workflows.state.base import BaseState, StateMeta
21
+ from vellum.workflows.types.core import Json
20
22
 
21
23
 
22
24
  def test_run_node__happy_path(vellum_client):
@@ -690,3 +692,54 @@ def main():
690
692
  "result": [ChatMessage(role="USER", content=StringChatMessageContent(value="Hello, world!"))],
691
693
  "log": "",
692
694
  }
695
+
696
+
697
+ def test_run_node__execute_code_api_fails__node_exception(vellum_client):
698
+ # GIVEN a node that will throw a JSON.parse error
699
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Json]):
700
+ code = """\
701
+ async function main(inputs: {
702
+ data: string,
703
+ }): Promise<string> {
704
+ return JSON.parse(inputs.data)
705
+ }
706
+ """
707
+ code_inputs = {
708
+ "data": "not a valid json string",
709
+ }
710
+ runtime = "TYPESCRIPT_5_3_3"
711
+
712
+ # AND the execute_code API will fail
713
+ message = """\
714
+ Code execution error (exit code 1): undefined:1
715
+ not a valid json string
716
+ ^
717
+
718
+ SyntaxError: Unexpected token 'o', \"not a valid\"... is not valid JSON
719
+ at JSON.parse (<anonymous>)
720
+ at Object.eval (eval at execute (/workdir/runner.js:16:18), <anonymous>:40:40)
721
+ at step (eval at execute (/workdir/runner.js:16:18), <anonymous>:32:23)
722
+ at Object.eval [as next] (eval at execute (/workdir/runner.js:16:18), <anonymous>:13:53)
723
+ at eval (eval at execute (/workdir/runner.js:16:18), <anonymous>:7:71)
724
+ at new Promise (<anonymous>)
725
+ at __awaiter (eval at execute (/workdir/runner.js:16:18), <anonymous>:3:12)
726
+ at Object.main (eval at execute (/workdir/runner.js:16:18), <anonymous>:38:12)
727
+ at execute (/workdir/runner.js:17:33)
728
+ at Interface.<anonymous> (/workdir/runner.js:58:5)
729
+
730
+ Node.js v21.7.3
731
+ """
732
+ vellum_client.execute_code.side_effect = BadRequestError(
733
+ body={
734
+ "message": message,
735
+ "log": "",
736
+ }
737
+ )
738
+
739
+ # WHEN we run the node
740
+ node = ExampleCodeExecutionNode()
741
+ with pytest.raises(NodeException) as exc_info:
742
+ node.run()
743
+
744
+ # AND the error should contain the execution error details
745
+ assert exc_info.value.message == message
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from copy import deepcopy
3
+ from dataclasses import dataclass
3
4
  import logging
4
5
  from queue import Empty, Queue
5
6
  from threading import Event as ThreadingEvent, Thread
@@ -63,6 +64,12 @@ ExternalInputsArg = Dict[ExternalInputReference, Any]
63
64
  BackgroundThreadItem = Union[BaseState, WorkflowEvent, None]
64
65
 
65
66
 
67
+ @dataclass
68
+ class ActiveNode(Generic[StateType]):
69
+ node: BaseNode[StateType]
70
+ was_outputs_streamed: bool = False
71
+
72
+
66
73
  class WorkflowRunner(Generic[StateType]):
67
74
  _entrypoints: Iterable[Type[BaseNode]]
68
75
 
@@ -136,7 +143,7 @@ class WorkflowRunner(Generic[StateType]):
136
143
  self._dependencies: Dict[Type[BaseNode], Set[Type[BaseNode]]] = defaultdict(set)
137
144
  self._state_forks: Set[StateType] = {self._initial_state}
138
145
 
139
- self._active_nodes_by_execution_id: Dict[UUID, BaseNode[StateType]] = {}
146
+ self._active_nodes_by_execution_id: Dict[UUID, ActiveNode[StateType]] = {}
140
147
  self._cancel_signal = cancel_signal
141
148
  self._execution_context = init_execution_context or get_execution_context()
142
149
  self._parent_context = self._execution_context.parent_context
@@ -404,7 +411,7 @@ class WorkflowRunner(Generic[StateType]):
404
411
  current_parent = get_parent_context()
405
412
  node = node_class(state=state, context=self.workflow.context)
406
413
  state.meta.node_execution_cache.initiate_node_execution(node_class, node_span_id)
407
- self._active_nodes_by_execution_id[node_span_id] = node
414
+ self._active_nodes_by_execution_id[node_span_id] = ActiveNode(node=node)
408
415
 
409
416
  worker_thread = Thread(
410
417
  target=self._context_run_work_item,
@@ -413,10 +420,11 @@ class WorkflowRunner(Generic[StateType]):
413
420
  worker_thread.start()
414
421
 
415
422
  def _handle_work_item_event(self, event: WorkflowEvent) -> Optional[WorkflowError]:
416
- node = self._active_nodes_by_execution_id.get(event.span_id)
417
- if not node:
423
+ active_node = self._active_nodes_by_execution_id.get(event.span_id)
424
+ if not active_node:
418
425
  return None
419
426
 
427
+ node = active_node.node
420
428
  if event.name == "node.execution.rejected":
421
429
  self._active_nodes_by_execution_id.pop(event.span_id)
422
430
  return event.error
@@ -431,6 +439,7 @@ class WorkflowRunner(Generic[StateType]):
431
439
  if node_output_descriptor.name != event.output.name:
432
440
  continue
433
441
 
442
+ active_node.was_outputs_streamed = True
434
443
  self._workflow_event_outer_queue.put(
435
444
  self._stream_workflow_event(
436
445
  BaseOutput(
@@ -447,6 +456,26 @@ class WorkflowRunner(Generic[StateType]):
447
456
 
448
457
  if event.name == "node.execution.fulfilled":
449
458
  self._active_nodes_by_execution_id.pop(event.span_id)
459
+ if not active_node.was_outputs_streamed:
460
+ for event_node_output_descriptor, node_output_value in event.outputs:
461
+ for workflow_output_descriptor in self.workflow.Outputs:
462
+ node_output_descriptor = workflow_output_descriptor.instance
463
+ if not isinstance(node_output_descriptor, OutputReference):
464
+ continue
465
+ if node_output_descriptor.outputs_class != event.node_definition.Outputs:
466
+ continue
467
+ if node_output_descriptor.name != event_node_output_descriptor.name:
468
+ continue
469
+
470
+ self._workflow_event_outer_queue.put(
471
+ self._stream_workflow_event(
472
+ BaseOutput(
473
+ name=workflow_output_descriptor.name,
474
+ value=node_output_value,
475
+ )
476
+ )
477
+ )
478
+
450
479
  self._handle_invoked_ports(node.state, event.invoked_ports)
451
480
 
452
481
  return None
@@ -8,6 +8,7 @@ from typing import Any, Callable, Dict, Type
8
8
 
9
9
  from pydantic import BaseModel
10
10
 
11
+ from vellum.workflows.constants import undefined
11
12
  from vellum.workflows.inputs.base import BaseInputs
12
13
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
13
14
  from vellum.workflows.ports.port import Port
@@ -22,7 +23,7 @@ class DefaultStateEncoder(JSONEncoder):
22
23
  return dict(obj)
23
24
 
24
25
  if isinstance(obj, (BaseInputs, BaseOutputs)):
25
- return {descriptor.name: value for descriptor, value in obj}
26
+ return {descriptor.name: value for descriptor, value in obj if value is not undefined}
26
27
 
27
28
  if isinstance(obj, (BaseOutput, Port)):
28
29
  return obj.serialize()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.8
3
+ Version: 0.14.9
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -23,7 +23,7 @@ vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
23
23
  vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  vellum_ee/workflows/display/base.py,sha256=ak29FIsawhaFa9_paZUHThlZRFJ1xB486JWKuSt1PYY,1965
25
25
  vellum_ee/workflows/display/nodes/__init__.py,sha256=436iSAh_Ex5tC68oEYvNgPu05ZVIAVXnS4PKGrQeZ0Y,321
26
- vellum_ee/workflows/display/nodes/base_node_display.py,sha256=H0sOYd8H7bz9ggSPEs5fwqyp27NAIu4YPNuNB9g0dQA,16289
26
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=h4_JqwH_AsT92OK14S3kqssZDjL-uyXnJfo0XcLijK0,16492
27
27
  vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=pLO0dORfRu--Ne9NgoyFT_CNjfpr5fGCsgbsMkUF5GM,2845
28
28
  vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=0S6ksPp53WXWh1RQVH71nj2bkCWBj4ZaFYhTxW3N2F4,1230
29
29
  vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -56,7 +56,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=4YUaTeD_OWF-
56
56
  vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=HBfGz4yt9GlmMW9JxzaCacPnHBDNIeXE8Jhqr9DqLLw,6191
57
57
  vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=F_0BrlSszllK_BhryPbojIleLq2dGXOfQD1rVp3fNFg,4733
58
58
  vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=yfTwpPgOzxJBrUz4cb-T8QQf8lF3TYm-Of40usUNOnc,7494
59
+ vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=cdpUoDNli8ULlNTrU6rnT4TZAIR8mzsG9ZbLrikPybU,8518
60
60
  vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=A1-tIpC5KIKG9JA_rkd1nLS8zUG3Kb4QiVdvb3boFxE,2509
@@ -83,13 +83,13 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_n
83
83
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=NdhE3lm7RMQ8DqkraPSq24IbOxNla9unbs4tsMWRzm4,3781
84
84
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=eD5686C9nWC5s6t08vbAnm9qf9t53gYQM-E1FwAa75c,3035
85
85
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=huKAOeMJ2MKmp6XtbvMJTUadqynoV40Ypoz9jsBEBEQ,7431
86
- vellum_ee/workflows/display/types.py,sha256=xDC1zy4rWKNqDtSr-h6MQfWnJ6scZ_Sadxp4t8Q3PY4,2897
86
+ vellum_ee/workflows/display/types.py,sha256=ixfmcQn51Rhsm4_0hWfG0_WpzLE89ZrDZpeYBklsP1Q,2592
87
87
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh4aTVnh7CHR2ykyMUDw0,1151
89
89
  vellum_ee/workflows/display/utils/vellum.py,sha256=UjK_RxnSEmlIu9klGCPWU5RAQBmgZ7cRbRdgxaTbubE,8081
90
90
  vellum_ee/workflows/display/vellum.py,sha256=7mqQaKZPPrLMcXSAQkPIxCy5x8HkKs5PbCu3GRaC2o8,8507
91
91
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
92
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=hTX1PQGSpuEiuAjlonyxE9V48UzTy4ReczX-dn8oPeY,18655
92
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=p9Lt1AypyC0Y6WHOUCsk4lYmlgODwg5oz-IDo0UA-1o,18678
93
93
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
94
94
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=_yB3-u7_bWdD4lUBWpRdWztJmJL-DXkkZaw9Vy9HH6g,3245
95
95
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=mbAzCpswOek34ITeTkesbVreCXpulj4NFjIg3RcdVZ8,18243
@@ -115,14 +115,14 @@ vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zi
115
115
  vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
116
116
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
117
117
  vellum_ee/workflows/tests/test_display_meta.py,sha256=pzdqND4KLWs7EUIbpXuqgso7BIRpoUsO3T_bgeENs0Q,2205
118
- vellum_ee/workflows/tests/test_server.py,sha256=SvKUrUPmOf3sIInXcFjETekql60npb4cAn1GPbF0bPs,391
118
+ vellum_ee/workflows/tests/test_server.py,sha256=M6vvQ2hjIpDWtQdDM9EPbMvUrZ93niAuYnxMNJWOjPA,511
119
119
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
120
120
  vellum/__init__.py,sha256=a_aM1_A04XGma4MAIDNeBF9BKzWbiQaVVMRzImHuxjA,36438
121
121
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
122
122
  vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
123
123
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
124
124
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
125
- vellum/client/core/client_wrapper.py,sha256=NOBkPB9txdePCb8-MgjYVJwNHQWPG9yrRX2B7sxaT-o,1868
125
+ vellum/client/core/client_wrapper.py,sha256=RhAfDORGTXyVqWFHTHUIONrGJRy7OCD049LPC0pwN_k,1868
126
126
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
127
127
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
128
128
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -1298,9 +1298,9 @@ vellum/workflows/__init__.py,sha256=CssPsbNvN6rDhoLuqpEv7MMKGa51vE6dvAh6U31Pcio,
1298
1298
  vellum/workflows/constants.py,sha256=2yg4_uo5gpqViy3ZLSwfC8qTybleYCtOnhA4Rj6bacM,1310
1299
1299
  vellum/workflows/context.py,sha256=DwSf8lO9NHABiqOoD3exgrjUoRuNsKtutaL5TgRbD-A,1441
1300
1300
  vellum/workflows/descriptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1301
- vellum/workflows/descriptors/base.py,sha256=gSib3vJpcI_UC8y8jhdp-hOK3_TGTF-SuwdhxF6x5iQ,14332
1301
+ vellum/workflows/descriptors/base.py,sha256=jtT11I5oB-CPWlJBZmc1RcVe4_xOKjimBthiHQHQuT0,14597
1302
1302
  vellum/workflows/descriptors/exceptions.py,sha256=gUy4UD9JFUKSeQnQpeuDSLiRqWjWiIsxLahB7p_q3JY,54
1303
- vellum/workflows/descriptors/tests/test_utils.py,sha256=yrXgu6J6pwqCxJKY3w-i-9t9KKzAkMR7SIICzdcII4k,5266
1303
+ vellum/workflows/descriptors/tests/test_utils.py,sha256=xoojJMyG5WLG9xGtmUjirz3lDFCcDsAcxjrtbdG8dNE,6060
1304
1304
  vellum/workflows/descriptors/utils.py,sha256=gmVXJjf2yWmvlYey41J2FZHeSou0JuCHKb3826K_Jok,3838
1305
1305
  vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR_pUus,50
1306
1306
  vellum/workflows/edges/edge.py,sha256=N0SnY3gKVuxImPAdCbPMPlHJIXbkQ3fwq_LbJRvVMFc,677
@@ -1313,9 +1313,9 @@ vellum/workflows/errors/types.py,sha256=tVW7Il9zalnwWzdoDLqYPIvRTOhXIv6FPORZAbU7
1313
1313
  vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
1314
1314
  vellum/workflows/events/node.py,sha256=uHT6If0esgZ3nLjrjmUPTKf3qbjGhoV_x5YKpjDBDcU,5280
1315
1315
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1316
- vellum/workflows/events/tests/test_event.py,sha256=9rbp92dQXpGFad-LjE2nYU17neKkiHxQz9X8RsoCWs4,13718
1316
+ vellum/workflows/events/tests/test_event.py,sha256=uRfMwSOqU-ROeZKCEngGvvJYlOZuxBhnC3qH5AGi3fM,15399
1317
1317
  vellum/workflows/events/types.py,sha256=cjRE8WL8tYCFradd9NOGl_H0mN3LiWWnA1uHmyT2Q0Q,3412
1318
- vellum/workflows/events/workflow.py,sha256=lveoWXtVRLjdO_nu0z6VlKeTqlGimogamiFR-jYihlE,5184
1318
+ vellum/workflows/events/workflow.py,sha256=XtmGG7NRp0TQ4memOJPcaNOs7qMUBbd4WKSZVlxWrCk,5937
1319
1319
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1320
1320
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1321
1321
  vellum/workflows/expressions/accessor.py,sha256=ItZF7fMLzVTqsdAiaXb5SiDupXmX0X9xbIus1W6hRds,1870
@@ -1330,8 +1330,8 @@ vellum/workflows/expressions/does_not_end_with.py,sha256=idw2OSuIk02XwBM5CXYOESf
1330
1330
  vellum/workflows/expressions/does_not_equal.py,sha256=LNiCibRZZIxaIrwotjW3SIsKYHWR7BpOItFI-x5KuKU,764
1331
1331
  vellum/workflows/expressions/ends_with.py,sha256=FkWZjAudc_DFM-fK-A3_tr6WXavfs_Qi7F6JtVgMglw,1117
1332
1332
  vellum/workflows/expressions/equals.py,sha256=gbI6BKQR7cLQih226-ge_BFSLprgEjqbdiIxo7WFg_E,758
1333
- vellum/workflows/expressions/greater_than.py,sha256=fTM2YF3FY_eOkRtGpgVWcv8qNt4K3lazONUIqcD8oe8,1263
1334
- vellum/workflows/expressions/greater_than_or_equal_to.py,sha256=sazL_-Jsgjo4jcQeE-6EC0BFHZACriaYMQH4rn0dqEo,1275
1333
+ vellum/workflows/expressions/greater_than.py,sha256=1sbUH6Obf-VoBgs7ilIncwYBHYfXliKmShppTuLMtCI,1247
1334
+ vellum/workflows/expressions/greater_than_or_equal_to.py,sha256=tsD5ZalB4SlryvEsvVtDkSr5Z13B2pABmHB8oHD8ojs,1276
1335
1335
  vellum/workflows/expressions/in_.py,sha256=RgiAIFntXGN4eWoOVBj1gqLymnBxSiw5hYD3TngF3dk,1123
1336
1336
  vellum/workflows/expressions/is_blank.py,sha256=vOOmK5poXmiNRVH7MR0feIFnL4rwKn7vmmTkJ9TcfVU,904
1337
1337
  vellum/workflows/expressions/is_nil.py,sha256=xCHwhKlm2UnfC-bVedmGgENCrzNtcn4ZeCYwNflVWbU,748
@@ -1341,11 +1341,15 @@ vellum/workflows/expressions/is_not_null.py,sha256=EoHXFgZScKP_BM2a5Z7YFQN6l7RME
1341
1341
  vellum/workflows/expressions/is_not_undefined.py,sha256=9s-RUQBqM17-_nIRvwsHuarLdHVtrxVuwnqBNJEtmh0,735
1342
1342
  vellum/workflows/expressions/is_null.py,sha256=C75ALGlG_sTGcxI46tm9HtgPVfJ7DwTIyKzX8qtEiDU,660
1343
1343
  vellum/workflows/expressions/is_undefined.py,sha256=uUBK3rxYbwoeRq36AGFc7d61hXzTp8UacQAi-1JbaW0,724
1344
- vellum/workflows/expressions/less_than.py,sha256=BcfkUH6Bb2inwR8jILn1hebUiyC74foVijBA-JymwT0,1260
1345
- vellum/workflows/expressions/less_than_or_equal_to.py,sha256=4i1FR6FzlKam29cZPPnXUqACslO242Ww-wZZY4CEK6A,1272
1344
+ vellum/workflows/expressions/less_than.py,sha256=chY9puJ6jDB2EinjfyGNrSplJ1NJC-BB-GGSSB33bqI,1237
1345
+ vellum/workflows/expressions/less_than_or_equal_to.py,sha256=JtTDBa8yFKy3fGaCOA1tb_5s1JkY8FFnH6kpoeXGnT4,1267
1346
1346
  vellum/workflows/expressions/not_between.py,sha256=ZtRJeJDSSlOvajL8YoBoh5o_khjIn9xSSeQCnXYbHFE,1506
1347
1347
  vellum/workflows/expressions/not_in.py,sha256=pFvwkFPsn3WJw61ssFgM2U1dqWEeglfz4FVT4xwm5Mc,1144
1348
1348
  vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
1349
+ vellum/workflows/expressions/parse_json.py,sha256=xsk6j3HF7bU1yF6fwt5P9Ugcyd5D9ZXrdng11FRilUI,1088
1350
+ vellum/workflows/expressions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1351
+ vellum/workflows/expressions/tests/test_expressions.py,sha256=3b6k8xs-CItBBw95NygFLUNoNPKxI4VA1GyWbkMtqyI,11623
1352
+ vellum/workflows/expressions/tests/test_parse_json.py,sha256=zpB_qE5_EwWQL7ULQUJm0o1PRSfWZdAqZNW6Ah13oJE,1059
1349
1353
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
1350
1354
  vellum/workflows/graph/graph.py,sha256=GGR8cGpSuNWPIiTWNWsj6l70upb5nIxAyFcn7VdaJIs,5506
1351
1355
  vellum/workflows/graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1357,7 +1361,7 @@ vellum/workflows/inputs/tests/test_inputs.py,sha256=Haa0Px0obef7rgIddO6wwHF_bzmv
1357
1361
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1358
1362
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1359
1363
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1360
- vellum/workflows/nodes/bases/base.py,sha256=O_O4pVZy0_H-4t2umBMumh_a9TCpmQsBGLcLjlS0vtE,14928
1364
+ vellum/workflows/nodes/bases/base.py,sha256=lVHODc1mjUFCorhZfK01zHohOWz9CYz7dnUzA-KENJ4,15036
1361
1365
  vellum/workflows/nodes/bases/base_adornment_node.py,sha256=eFTgsPCYb3eyGS0-kw7C6crFnwFx437R5wh9-8bWYts,2905
1362
1366
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1363
1367
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=4SOdZzvugVtN8CIuo5RrapAxSYGXnxUwQ77dXJ64oTU,6295
@@ -1387,7 +1391,7 @@ vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDX
1387
1391
  vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
1388
1392
  vellum/workflows/nodes/displayable/api_node/node.py,sha256=QdpsyGVxo5PcN8nwGZpcpW_YMKHr3_VvmbK1BlrdOFk,2547
1389
1393
  vellum/workflows/nodes/displayable/api_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1390
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=Ta-ZkZvllPHpLamiDRdEtVlwBJUFcvBHpyKLY6q06_A,2309
1394
+ vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=RtQWnFfq0HNqY2A6c5SdwHl8A91K4Vq8un5UJwwnkSw,2180
1391
1395
  vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
1392
1396
  vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
1393
1397
  vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=-LOKjU_rY1UWgD0DS5LJwAClBI8N7zrdmwigE3y5rhc,4000
@@ -1403,11 +1407,11 @@ vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMD
1403
1407
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1404
1408
  vellum/workflows/nodes/displayable/bases/utils.py,sha256=ckMUenSsNkiYmSw6FmjSMHYaCk8Y8_sUjL6lkFFEqts,5412
1405
1409
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1406
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=_yrQn_uLwy8zVW1RjFMoeomDEsbpbbKK7ifMGEBfNlk,9120
1410
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=pdDrjI8wdqMyf52kK6TlSjSU-MRxV2SKDwKcK2LCgkU,9547
1407
1411
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1408
1412
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1409
1413
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1410
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=4ADDKsObtUs0PhcWyAjWyQcAF7PGUYE0CxjYp8d-1NM,20637
1414
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=OTnw5jgX90xWVB3vKryL5QSIr2YnGBz-W0q9C9LpNoc,22471
1411
1415
  vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=BQraIN4I3DCzXLEuBlRYCyp7ote7hQmnnKHu4jFHCCA,5174
1412
1416
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1413
1417
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
@@ -1466,12 +1470,12 @@ vellum/workflows/references/workflow_input.py,sha256=86IuhlBz-9cGxeUzizyjdp482aj
1466
1470
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1467
1471
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1468
1472
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1469
- vellum/workflows/runner/runner.py,sha256=XUaAdkG0AEkfPc0ri1j0KXUUqrUfTKmUfQXllgE1yq8,29854
1473
+ vellum/workflows/runner/runner.py,sha256=VUGw-QlUNyfwRWjXgBZ1VqKRuYdFC7YdtAmQcsgR6I0,31206
1470
1474
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1471
1475
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1472
1476
  vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
1473
1477
  vellum/workflows/state/context.py,sha256=yePVr4CCTQn5bjo1697JOO24fKFQpVNzooL07xL4gL0,2702
1474
- vellum/workflows/state/encoder.py,sha256=WdUidpOaBDx5lilJl8V8McFDHQYiCLCJR9dmktdzdZY,1836
1478
+ vellum/workflows/state/encoder.py,sha256=TnOQojc5lTQ83g9QbpA4UCqShJvutmTMxbpKt-9gNe4,1911
1475
1479
  vellum/workflows/state/store.py,sha256=VYGBQgN1bpd1as5eGiouV_7scg8QsRs4_1aqZAGIsRQ,793
1476
1480
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1477
1481
  vellum/workflows/state/tests/test_state.py,sha256=jBynFR4m74Vn51DdmKBLkxb1loTy1CnJPtzPmdAFQUo,5159
@@ -1502,8 +1506,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1502
1506
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1503
1507
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
1504
1508
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1505
- vellum_ai-0.14.8.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1506
- vellum_ai-0.14.8.dist-info/METADATA,sha256=rjXn1UMzW7AdriljPfHof7rjjp7GseRXLTfRswDoSBc,5407
1507
- vellum_ai-0.14.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1508
- vellum_ai-0.14.8.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1509
- vellum_ai-0.14.8.dist-info/RECORD,,
1509
+ vellum_ai-0.14.9.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1510
+ vellum_ai-0.14.9.dist-info/METADATA,sha256=JO_Tqa4ayXrL4ytBbrfiXfly8yXeiPyQc-4NOoL9kDk,5407
1511
+ vellum_ai-0.14.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1512
+ vellum_ai-0.14.9.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1513
+ vellum_ai-0.14.9.dist-info/RECORD,,
@@ -27,6 +27,7 @@ from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpressi
27
27
  from vellum.workflows.expressions.is_null import IsNullExpression
28
28
  from vellum.workflows.expressions.is_undefined import IsUndefinedExpression
29
29
  from vellum.workflows.expressions.not_between import NotBetweenExpression
30
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
30
31
  from vellum.workflows.nodes.bases.base import BaseNode
31
32
  from vellum.workflows.nodes.utils import get_wrapped_node
32
33
  from vellum.workflows.ports import Port
@@ -386,6 +387,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
386
387
  "node_id": str(node_class_display.node_id),
387
388
  }
388
389
 
390
+ if isinstance(value, ParseJsonExpression):
391
+ raise ValueError("ParseJsonExpression is not supported in the UI")
392
+
389
393
  if not isinstance(value, BaseDescriptor):
390
394
  vellum_value = primitive_to_vellum_value(value)
391
395
  return {
@@ -1,3 +1,4 @@
1
+ import pytest
1
2
  from uuid import UUID
2
3
  from typing import Dict
3
4
 
@@ -228,3 +229,33 @@ def test_vellum_workflow_display__serialize_with_unused_nodes_and_edges():
228
229
  break
229
230
 
230
231
  assert edge_found, "Edge between unused nodes NodeB and NodeC not found in serialized output"
232
+
233
+
234
+ def test_parse_json_not_supported_in_ui():
235
+ """
236
+ Test that verifies ParseJsonExpression is not yet supported in the UI.
237
+ This test should fail once UI support is added, at which point it should be updated.
238
+ """
239
+ # GIVEN a workflow that uses the parse_json function
240
+ from vellum.workflows.references.constant import ConstantValueReference
241
+
242
+ class JsonNode(BaseNode):
243
+ class Outputs(BaseNode.Outputs):
244
+ json_result = ConstantValueReference('{"key": "value"}').parse_json()
245
+
246
+ class Workflow(BaseWorkflow):
247
+ graph = JsonNode
248
+
249
+ class Outputs(BaseWorkflow.Outputs):
250
+ final = JsonNode.Outputs.json_result
251
+
252
+ # WHEN we attempt to serialize it
253
+ workflow_display = get_workflow_display(
254
+ base_display_class=VellumWorkflowDisplay,
255
+ workflow_class=Workflow,
256
+ )
257
+
258
+ with pytest.raises(ValueError) as exc_info:
259
+ workflow_display.serialize()
260
+
261
+ assert "ParseJsonExpression is not supported in the UI" == str(exc_info.value)
@@ -1,9 +1,8 @@
1
1
  from dataclasses import dataclass, field
2
- from uuid import UUID
3
2
  from typing import TYPE_CHECKING, Dict, Generic, Tuple, Type, TypeVar
4
3
 
5
- from vellum.client.core import UniversalBaseModel
6
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.events.workflow import WorkflowEventDisplayContext # noqa: F401
7
6
  from vellum.workflows.nodes import BaseNode
8
7
  from vellum.workflows.ports import Port
9
8
  from vellum.workflows.references import OutputReference, StateValueReference, WorkflowInputReference
@@ -25,18 +24,6 @@ NodeDisplayType = TypeVar("NodeDisplayType", bound="BaseNodeDisplay")
25
24
  WorkflowDisplayType = TypeVar("WorkflowDisplayType", bound="BaseWorkflowDisplay")
26
25
 
27
26
 
28
- class NodeDisplay(UniversalBaseModel):
29
- input_display: Dict[str, UUID]
30
- output_display: Dict[str, UUID]
31
- port_display: Dict[str, UUID]
32
-
33
-
34
- class WorkflowEventDisplayContext(UniversalBaseModel):
35
- node_displays: Dict[str, NodeDisplay]
36
- workflow_inputs: Dict[str, UUID]
37
- workflow_outputs: Dict[str, UUID]
38
-
39
-
40
27
  @dataclass
41
28
  class WorkflowDisplayContext(
42
29
  Generic[
@@ -9,6 +9,7 @@ from typing import Any, Dict, Generic, Iterator, List, Optional, Tuple, Type, Un
9
9
  from vellum.workflows import BaseWorkflow
10
10
  from vellum.workflows.descriptors.base import BaseDescriptor
11
11
  from vellum.workflows.edges import Edge
12
+ from vellum.workflows.events.workflow import NodeDisplay, WorkflowEventDisplayContext
12
13
  from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
13
14
  from vellum.workflows.nodes.bases import BaseNode
14
15
  from vellum.workflows.nodes.utils import get_wrapped_node
@@ -33,12 +34,7 @@ from vellum_ee.workflows.display.base import (
33
34
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
34
35
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
35
36
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
36
- from vellum_ee.workflows.display.types import (
37
- NodeDisplay,
38
- NodeDisplayType,
39
- WorkflowDisplayContext,
40
- WorkflowEventDisplayContext,
41
- )
37
+ from vellum_ee.workflows.display.types import NodeDisplayType, WorkflowDisplayContext
42
38
 
43
39
  logger = logging.getLogger(__name__)
44
40
 
@@ -2,6 +2,7 @@ from vellum.client.core.pydantic_utilities import UniversalBaseModel
2
2
 
3
3
 
4
4
  def test_load_workflow_event_display_context():
5
+ # DEPRECATED: Use `vellum.workflows.events.workflow.WorkflowEventDisplayContext` instead. Will be removed in 0.15.0
5
6
  from vellum_ee.workflows.display.types import WorkflowEventDisplayContext
6
7
 
7
8
  # We are actually just ensuring there are no circular dependencies when