vellum-ai 0.14.44__py3-none-any.whl → 0.14.46__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 (56) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/client/core/pydantic_utilities.py +7 -1
  3. vellum/workflows/nodes/bases/base.py +1 -0
  4. vellum/workflows/nodes/bases/tests/test_base_node.py +20 -0
  5. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +8 -14
  6. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +62 -0
  7. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -54
  8. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +5 -6
  9. vellum/workflows/nodes/utils.py +4 -0
  10. vellum/workflows/ports/port.py +13 -3
  11. vellum/workflows/types/code_execution_node_wrappers.py +64 -0
  12. vellum/workflows/types/tests/test_utils.py +3 -3
  13. vellum/workflows/types/utils.py +31 -10
  14. vellum/workflows/vellum_client.py +19 -7
  15. {vellum_ai-0.14.44.dist-info → vellum_ai-0.14.46.dist-info}/METADATA +1 -1
  16. {vellum_ai-0.14.44.dist-info → vellum_ai-0.14.46.dist-info}/RECORD +56 -53
  17. vellum_cli/config.py +7 -2
  18. vellum_cli/push.py +5 -1
  19. vellum_cli/tests/test_push.py +192 -8
  20. vellum_ee/workflows/display/nodes/base_node_display.py +4 -173
  21. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  22. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -1
  23. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +5 -6
  24. vellum_ee/workflows/display/nodes/vellum/retry_node.py +3 -3
  25. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +5 -6
  26. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py +106 -0
  27. vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +109 -0
  28. vellum_ee/workflows/display/nodes/vellum/try_node.py +3 -3
  29. vellum_ee/workflows/display/tests/test_base_workflow_display.py +1 -0
  30. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +73 -111
  31. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +0 -1
  32. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +0 -3
  33. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +0 -4
  34. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +0 -1
  35. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +0 -1
  36. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +0 -1
  37. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +0 -1
  38. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +18 -2
  39. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +10 -1
  40. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +0 -1
  41. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +2 -3
  42. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +0 -1
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +2 -3
  44. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +0 -1
  45. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +1 -2
  46. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +0 -1
  47. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +5 -55
  48. vellum_ee/workflows/display/types.py +3 -0
  49. vellum_ee/workflows/display/utils/expressions.py +222 -2
  50. vellum_ee/workflows/display/utils/vellum.py +1 -79
  51. vellum_ee/workflows/display/workflows/base_workflow_display.py +59 -37
  52. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +3 -0
  53. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +98 -0
  54. {vellum_ai-0.14.44.dist-info → vellum_ai-0.14.46.dist-info}/LICENSE +0 -0
  55. {vellum_ai-0.14.44.dist-info → vellum_ai-0.14.46.dist-info}/WHEEL +0 -0
  56. {vellum_ai-0.14.44.dist-info → vellum_ai-0.14.46.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,98 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Any
2
2
 
3
+ from vellum.client.types.logical_operator import LogicalOperator
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.expressions.accessor import AccessorExpression
6
+ from vellum.workflows.expressions.and_ import AndExpression
7
+ from vellum.workflows.expressions.begins_with import BeginsWithExpression
8
+ from vellum.workflows.expressions.between import BetweenExpression
9
+ from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
10
+ from vellum.workflows.expressions.contains import ContainsExpression
11
+ from vellum.workflows.expressions.does_not_begin_with import DoesNotBeginWithExpression
12
+ from vellum.workflows.expressions.does_not_contain import DoesNotContainExpression
13
+ from vellum.workflows.expressions.does_not_end_with import DoesNotEndWithExpression
14
+ from vellum.workflows.expressions.does_not_equal import DoesNotEqualExpression
15
+ from vellum.workflows.expressions.ends_with import EndsWithExpression
16
+ from vellum.workflows.expressions.equals import EqualsExpression
17
+ from vellum.workflows.expressions.greater_than import GreaterThanExpression
18
+ from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
19
+ from vellum.workflows.expressions.in_ import InExpression
20
+ from vellum.workflows.expressions.is_nil import IsNilExpression
21
+ from vellum.workflows.expressions.is_not_nil import IsNotNilExpression
22
+ from vellum.workflows.expressions.is_not_null import IsNotNullExpression
23
+ from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpression
24
+ from vellum.workflows.expressions.is_null import IsNullExpression
25
+ from vellum.workflows.expressions.is_undefined import IsUndefinedExpression
26
+ from vellum.workflows.expressions.less_than import LessThanExpression
27
+ from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
28
+ from vellum.workflows.expressions.not_between import NotBetweenExpression
29
+ from vellum.workflows.expressions.not_in import NotInExpression
30
+ from vellum.workflows.expressions.or_ import OrExpression
31
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
32
+ from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
33
+ from vellum.workflows.references.constant import ConstantValueReference
34
+ from vellum.workflows.references.execution_count import ExecutionCountReference
4
35
  from vellum.workflows.references.lazy import LazyReference
36
+ from vellum.workflows.references.output import OutputReference
37
+ from vellum.workflows.references.state_value import StateValueReference
38
+ from vellum.workflows.references.vellum_secret import VellumSecretReference
39
+ from vellum.workflows.references.workflow_input import WorkflowInputReference
40
+ from vellum.workflows.types.core import JsonObject
41
+ from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
5
42
 
6
43
  if TYPE_CHECKING:
7
44
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
8
45
 
9
46
 
47
+ def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperator:
48
+ if isinstance(descriptor, EqualsExpression):
49
+ return "="
50
+ elif isinstance(descriptor, DoesNotEqualExpression):
51
+ return "!="
52
+ elif isinstance(descriptor, LessThanExpression):
53
+ return "<"
54
+ elif isinstance(descriptor, GreaterThanExpression):
55
+ return ">"
56
+ elif isinstance(descriptor, LessThanOrEqualToExpression):
57
+ return "<="
58
+ elif isinstance(descriptor, GreaterThanOrEqualToExpression):
59
+ return ">="
60
+ elif isinstance(descriptor, ContainsExpression):
61
+ return "contains"
62
+ elif isinstance(descriptor, BeginsWithExpression):
63
+ return "beginsWith"
64
+ elif isinstance(descriptor, EndsWithExpression):
65
+ return "endsWith"
66
+ elif isinstance(descriptor, DoesNotContainExpression):
67
+ return "doesNotContain"
68
+ elif isinstance(descriptor, DoesNotBeginWithExpression):
69
+ return "doesNotBeginWith"
70
+ elif isinstance(descriptor, DoesNotEndWithExpression):
71
+ return "doesNotEndWith"
72
+ elif isinstance(descriptor, (IsNullExpression, IsNilExpression, IsUndefinedExpression)):
73
+ return "null"
74
+ elif isinstance(descriptor, (IsNotNullExpression, IsNotNilExpression, IsNotUndefinedExpression)):
75
+ return "notNull"
76
+ elif isinstance(descriptor, InExpression):
77
+ return "in"
78
+ elif isinstance(descriptor, NotInExpression):
79
+ return "notIn"
80
+ elif isinstance(descriptor, BetweenExpression):
81
+ return "between"
82
+ elif isinstance(descriptor, NotBetweenExpression):
83
+ return "notBetween"
84
+ elif isinstance(descriptor, AndExpression):
85
+ return "and"
86
+ elif isinstance(descriptor, OrExpression):
87
+ return "or"
88
+ elif isinstance(descriptor, CoalesceExpression):
89
+ return "coalesce"
90
+ elif isinstance(descriptor, ParseJsonExpression):
91
+ return "parseJson"
92
+ else:
93
+ raise ValueError(f"Unsupported descriptor type: {descriptor}")
94
+
95
+
10
96
  def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplayContext") -> BaseDescriptor:
11
97
  if isinstance(value._get, str):
12
98
  reference_parts = value._get.split(".")
@@ -21,10 +107,144 @@ def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplay
21
107
  )
22
108
 
23
109
  node_class_name = ".".join(reference_parts[:-2])
24
- for node in display_context.node_displays.keys():
110
+ for node in display_context.global_node_displays.keys():
25
111
  if node.__name__ == node_class_name:
26
112
  return getattr(node.Outputs, output_name)
27
113
 
28
114
  raise Exception(f"Failed to parse lazy reference: {value._get}")
29
115
 
30
116
  return value._get()
117
+
118
+
119
+ def serialize_condition(display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
120
+ if isinstance(
121
+ condition,
122
+ (
123
+ IsNullExpression,
124
+ IsNotNullExpression,
125
+ IsNilExpression,
126
+ IsNotNilExpression,
127
+ IsUndefinedExpression,
128
+ IsNotUndefinedExpression,
129
+ ParseJsonExpression,
130
+ ),
131
+ ):
132
+ lhs = serialize_value(display_context, condition._expression)
133
+ return {
134
+ "type": "UNARY_EXPRESSION",
135
+ "lhs": lhs,
136
+ "operator": convert_descriptor_to_operator(condition),
137
+ }
138
+ elif isinstance(condition, (BetweenExpression, NotBetweenExpression)):
139
+ base = serialize_value(display_context, condition._value)
140
+ lhs = serialize_value(display_context, condition._start)
141
+ rhs = serialize_value(display_context, condition._end)
142
+
143
+ return {
144
+ "type": "TERNARY_EXPRESSION",
145
+ "base": base,
146
+ "operator": convert_descriptor_to_operator(condition),
147
+ "lhs": lhs,
148
+ "rhs": rhs,
149
+ }
150
+ elif isinstance(
151
+ condition,
152
+ (
153
+ AndExpression,
154
+ BeginsWithExpression,
155
+ CoalesceExpression,
156
+ ContainsExpression,
157
+ DoesNotBeginWithExpression,
158
+ DoesNotContainExpression,
159
+ DoesNotEndWithExpression,
160
+ DoesNotEqualExpression,
161
+ EndsWithExpression,
162
+ EqualsExpression,
163
+ GreaterThanExpression,
164
+ GreaterThanOrEqualToExpression,
165
+ InExpression,
166
+ LessThanExpression,
167
+ LessThanOrEqualToExpression,
168
+ NotInExpression,
169
+ OrExpression,
170
+ ),
171
+ ):
172
+ lhs = serialize_value(display_context, condition._lhs)
173
+ rhs = serialize_value(display_context, condition._rhs)
174
+
175
+ return {
176
+ "type": "BINARY_EXPRESSION",
177
+ "lhs": lhs,
178
+ "operator": convert_descriptor_to_operator(condition),
179
+ "rhs": rhs,
180
+ }
181
+ elif isinstance(condition, AccessorExpression):
182
+ return {
183
+ "type": "BINARY_EXPRESSION",
184
+ "lhs": serialize_value(display_context, condition._base),
185
+ "operator": "accessField",
186
+ "rhs": serialize_value(display_context, condition._field),
187
+ }
188
+
189
+ raise UnsupportedSerializationException(f"Unsupported condition type: {condition.__class__.__name__}")
190
+
191
+
192
+ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> JsonObject:
193
+ if isinstance(value, ConstantValueReference):
194
+ return serialize_value(display_context, value._value)
195
+
196
+ if isinstance(value, LazyReference):
197
+ child_descriptor = get_child_descriptor(value, display_context)
198
+ return serialize_value(display_context, child_descriptor)
199
+
200
+ if isinstance(value, WorkflowInputReference):
201
+ workflow_input_display = display_context.global_workflow_input_displays[value]
202
+ return {
203
+ "type": "WORKFLOW_INPUT",
204
+ "input_variable_id": str(workflow_input_display.id),
205
+ }
206
+
207
+ if isinstance(value, StateValueReference):
208
+ state_value_display = display_context.global_state_value_displays[value]
209
+ return {
210
+ "type": "STATE_VALUE",
211
+ "state_variable_id": str(state_value_display.id),
212
+ }
213
+
214
+ if isinstance(value, OutputReference):
215
+ upstream_node, output_display = display_context.global_node_output_displays[value]
216
+ upstream_node_display = display_context.global_node_displays[upstream_node]
217
+
218
+ return {
219
+ "type": "NODE_OUTPUT",
220
+ "node_id": str(upstream_node_display.node_id),
221
+ "node_output_id": str(output_display.id),
222
+ }
223
+
224
+ if isinstance(value, VellumSecretReference):
225
+ return {
226
+ "type": "VELLUM_SECRET",
227
+ "vellum_secret_name": value.name,
228
+ }
229
+
230
+ if isinstance(value, ExecutionCountReference):
231
+ node_class_display = display_context.global_node_displays[value.node_class]
232
+
233
+ return {
234
+ "type": "EXECUTION_COUNTER",
235
+ "node_id": str(node_class_display.node_id),
236
+ }
237
+
238
+ if isinstance(value, dict) and any(isinstance(v, BaseDescriptor) for v in value.values()):
239
+ raise ValueError("Nested references are not supported.")
240
+
241
+ if not isinstance(value, BaseDescriptor):
242
+ vellum_value = primitive_to_vellum_value(value)
243
+ return {
244
+ "type": "CONSTANT_VALUE",
245
+ "value": vellum_value.dict(),
246
+ }
247
+
248
+ # If it's not any of the references we know about,
249
+ # then try to serialize it as a nested value
250
+ return serialize_condition(display_context, value)
@@ -2,36 +2,9 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
2
2
 
3
3
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
4
4
  from vellum.client.types.array_vellum_value import ArrayVellumValue
5
- from vellum.client.types.logical_operator import LogicalOperator
6
5
  from vellum.client.types.vellum_value import VellumValue
7
6
  from vellum.client.types.vellum_variable_type import VellumVariableType
8
7
  from vellum.workflows.descriptors.base import BaseDescriptor
9
- from vellum.workflows.expressions.and_ import AndExpression
10
- from vellum.workflows.expressions.begins_with import BeginsWithExpression
11
- from vellum.workflows.expressions.between import BetweenExpression
12
- from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
13
- from vellum.workflows.expressions.contains import ContainsExpression
14
- from vellum.workflows.expressions.does_not_begin_with import DoesNotBeginWithExpression
15
- from vellum.workflows.expressions.does_not_contain import DoesNotContainExpression
16
- from vellum.workflows.expressions.does_not_end_with import DoesNotEndWithExpression
17
- from vellum.workflows.expressions.does_not_equal import DoesNotEqualExpression
18
- from vellum.workflows.expressions.ends_with import EndsWithExpression
19
- from vellum.workflows.expressions.equals import EqualsExpression
20
- from vellum.workflows.expressions.greater_than import GreaterThanExpression
21
- from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
22
- from vellum.workflows.expressions.in_ import InExpression
23
- from vellum.workflows.expressions.is_nil import IsNilExpression
24
- from vellum.workflows.expressions.is_not_nil import IsNotNilExpression
25
- from vellum.workflows.expressions.is_not_null import IsNotNullExpression
26
- from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpression
27
- from vellum.workflows.expressions.is_null import IsNullExpression
28
- from vellum.workflows.expressions.is_undefined import IsUndefinedExpression
29
- from vellum.workflows.expressions.less_than import LessThanExpression
30
- from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
31
- from vellum.workflows.expressions.not_between import NotBetweenExpression
32
- from vellum.workflows.expressions.not_in import NotInExpression
33
- from vellum.workflows.expressions.or_ import OrExpression
34
- from vellum.workflows.expressions.parse_json import ParseJsonExpression
35
8
  from vellum.workflows.nodes.bases.base import BaseNode
36
9
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
37
10
  from vellum.workflows.references import OutputReference, WorkflowInputReference
@@ -40,7 +13,6 @@ from vellum.workflows.references.lazy import LazyReference
40
13
  from vellum.workflows.references.node import NodeReference
41
14
  from vellum.workflows.references.vellum_secret import VellumSecretReference
42
15
  from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
43
- from vellum.workflows.vellum_client import create_vellum_client
44
16
  from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
45
17
  from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
46
18
 
@@ -145,8 +117,7 @@ def create_node_input_value_pointer_rule(
145
117
  workflow_input_display = display_context.global_workflow_input_displays[value]
146
118
  return InputVariablePointer(data=InputVariableData(input_variable_id=str(workflow_input_display.id)))
147
119
  if isinstance(value, VellumSecretReference):
148
- vellum_client = create_vellum_client()
149
- workspace_secret = vellum_client.workspace_secrets.retrieve(
120
+ workspace_secret = display_context.client.workspace_secrets.retrieve(
150
121
  id=value.name,
151
122
  )
152
123
  return WorkspaceSecretPointer(
@@ -166,52 +137,3 @@ def create_node_input_value_pointer_rule(
166
137
  return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_value)
167
138
 
168
139
  raise UnsupportedSerializationException(f"Unsupported descriptor type: {value.__class__.__name__}")
169
-
170
-
171
- def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperator:
172
- if isinstance(descriptor, EqualsExpression):
173
- return "="
174
- elif isinstance(descriptor, DoesNotEqualExpression):
175
- return "!="
176
- elif isinstance(descriptor, LessThanExpression):
177
- return "<"
178
- elif isinstance(descriptor, GreaterThanExpression):
179
- return ">"
180
- elif isinstance(descriptor, LessThanOrEqualToExpression):
181
- return "<="
182
- elif isinstance(descriptor, GreaterThanOrEqualToExpression):
183
- return ">="
184
- elif isinstance(descriptor, ContainsExpression):
185
- return "contains"
186
- elif isinstance(descriptor, BeginsWithExpression):
187
- return "beginsWith"
188
- elif isinstance(descriptor, EndsWithExpression):
189
- return "endsWith"
190
- elif isinstance(descriptor, DoesNotContainExpression):
191
- return "doesNotContain"
192
- elif isinstance(descriptor, DoesNotBeginWithExpression):
193
- return "doesNotBeginWith"
194
- elif isinstance(descriptor, DoesNotEndWithExpression):
195
- return "doesNotEndWith"
196
- elif isinstance(descriptor, (IsNullExpression, IsNilExpression, IsUndefinedExpression)):
197
- return "null"
198
- elif isinstance(descriptor, (IsNotNullExpression, IsNotNilExpression, IsNotUndefinedExpression)):
199
- return "notNull"
200
- elif isinstance(descriptor, InExpression):
201
- return "in"
202
- elif isinstance(descriptor, NotInExpression):
203
- return "notIn"
204
- elif isinstance(descriptor, BetweenExpression):
205
- return "between"
206
- elif isinstance(descriptor, NotBetweenExpression):
207
- return "notBetween"
208
- elif isinstance(descriptor, AndExpression):
209
- return "and"
210
- elif isinstance(descriptor, OrExpression):
211
- return "or"
212
- elif isinstance(descriptor, CoalesceExpression):
213
- return "coalesce"
214
- elif isinstance(descriptor, ParseJsonExpression):
215
- return "parseJson"
216
- else:
217
- raise ValueError(f"Unsupported descriptor type: {descriptor}")
@@ -6,6 +6,7 @@ import logging
6
6
  from uuid import UUID
7
7
  from typing import Any, Dict, ForwardRef, Generic, Iterator, List, Optional, Tuple, Type, TypeVar, Union, cast, get_args
8
8
 
9
+ from vellum.client import Vellum as VellumClient
9
10
  from vellum.workflows import BaseWorkflow
10
11
  from vellum.workflows.constants import undefined
11
12
  from vellum.workflows.descriptors.base import BaseDescriptor
@@ -21,6 +22,7 @@ from vellum.workflows.types.core import JsonArray, JsonObject
21
22
  from vellum.workflows.types.generics import WorkflowType
22
23
  from vellum.workflows.types.utils import get_original_base
23
24
  from vellum.workflows.utils.uuids import uuid4_from_hash
25
+ from vellum.workflows.vellum_client import create_vellum_client
24
26
  from vellum_ee.workflows.display.base import (
25
27
  EdgeDisplay,
26
28
  EntrypointDisplay,
@@ -46,6 +48,7 @@ from vellum_ee.workflows.display.types import (
46
48
  WorkflowInputsDisplays,
47
49
  WorkflowOutputDisplays,
48
50
  )
51
+ from vellum_ee.workflows.display.utils.expressions import serialize_value
49
52
  from vellum_ee.workflows.display.utils.registry import register_workflow_display_class
50
53
  from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
51
54
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
@@ -83,9 +86,16 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
83
86
  self,
84
87
  *,
85
88
  parent_display_context: Optional[WorkflowDisplayContext] = None,
89
+ client: Optional[VellumClient] = None,
86
90
  dry_run: bool = False,
87
91
  ):
88
92
  self._parent_display_context = parent_display_context
93
+ self._client = client or (
94
+ # propagate the client from the parent display context if it is not provided
95
+ self._parent_display_context.client
96
+ if self._parent_display_context
97
+ else create_vellum_client()
98
+ )
89
99
  self._errors: List[Exception] = []
90
100
  self._dry_run = dry_run
91
101
 
@@ -124,26 +134,24 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
124
134
  }
125
135
  )
126
136
 
127
- nodes: JsonArray = []
137
+ serialized_nodes: Dict[UUID, JsonObject] = {}
128
138
  edges: JsonArray = []
129
139
 
130
140
  # Add a single synthetic node for the workflow entrypoint
131
141
  entrypoint_node_id = self.display_context.workflow_display.entrypoint_node_id
132
142
  entrypoint_node_source_handle_id = self.display_context.workflow_display.entrypoint_node_source_handle_id
133
- nodes.append(
134
- {
135
- "id": str(entrypoint_node_id),
136
- "type": "ENTRYPOINT",
137
- "inputs": [],
138
- "data": {
139
- "label": "Entrypoint Node",
140
- "source_handle_id": str(entrypoint_node_source_handle_id),
141
- },
142
- "display_data": self.display_context.workflow_display.entrypoint_node_display.dict(),
143
- "base": None,
144
- "definition": None,
143
+ serialized_nodes[entrypoint_node_id] = {
144
+ "id": str(entrypoint_node_id),
145
+ "type": "ENTRYPOINT",
146
+ "inputs": [],
147
+ "data": {
148
+ "label": "Entrypoint Node",
149
+ "source_handle_id": str(entrypoint_node_source_handle_id),
145
150
  },
146
- )
151
+ "display_data": self.display_context.workflow_display.entrypoint_node_display.dict(),
152
+ "base": None,
153
+ "definition": None,
154
+ }
147
155
 
148
156
  # Add all the nodes in the workflow
149
157
  for node in self._workflow.get_nodes():
@@ -155,7 +163,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
155
163
  self.add_error(e)
156
164
  continue
157
165
 
158
- nodes.append(serialized_node)
166
+ serialized_nodes[node_display.node_id] = serialized_node
159
167
 
160
168
  # Add all unused nodes in the workflow
161
169
  for node in self._workflow.get_unused_nodes():
@@ -167,10 +175,11 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
167
175
  self.add_error(e)
168
176
  continue
169
177
 
170
- nodes.append(serialized_node)
178
+ serialized_nodes[node_display.node_id] = serialized_node
171
179
 
172
180
  synthetic_output_edges: JsonArray = []
173
181
  output_variables: JsonArray = []
182
+ output_values: JsonArray = []
174
183
  final_output_nodes = [
175
184
  node for node in self.display_context.node_displays.keys() if issubclass(node, FinalOutputNode)
176
185
  ]
@@ -185,9 +194,9 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
185
194
  for workflow_output, workflow_output_display in self.display_context.workflow_output_displays.items():
186
195
  final_output_node_id = uuid4_from_hash(f"{self.workflow_id}|node_id|{workflow_output.name}")
187
196
  inferred_type = infer_vellum_variable_type(workflow_output)
188
-
189
197
  # Remove the terminal node output from the unreferenced set
190
- unreferenced_final_output_node_outputs.discard(cast(OutputReference, workflow_output.instance))
198
+ if isinstance(workflow_output.instance, OutputReference):
199
+ unreferenced_final_output_node_outputs.discard(workflow_output.instance)
191
200
 
192
201
  if workflow_output.instance not in final_output_node_outputs:
193
202
  # Create a synthetic terminal node only if there is no terminal node for this output
@@ -220,24 +229,22 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
220
229
  )
221
230
  synthetic_display_data = NodeDisplayData().dict()
222
231
  synthetic_node_label = "Final Output"
223
- nodes.append(
224
- {
225
- "id": str(final_output_node_id),
226
- "type": "TERMINAL",
227
- "data": {
228
- "label": synthetic_node_label,
229
- "name": workflow_output_display.name,
230
- "target_handle_id": synthetic_target_handle_id,
231
- "output_id": str(workflow_output_display.id),
232
- "output_type": inferred_type,
233
- "node_input_id": str(node_input.id),
234
- },
235
- "inputs": [node_input.dict()],
236
- "display_data": synthetic_display_data,
237
- "base": final_output_node_base,
238
- "definition": None,
239
- }
240
- )
232
+ serialized_nodes[final_output_node_id] = {
233
+ "id": str(final_output_node_id),
234
+ "type": "TERMINAL",
235
+ "data": {
236
+ "label": synthetic_node_label,
237
+ "name": workflow_output_display.name,
238
+ "target_handle_id": synthetic_target_handle_id,
239
+ "output_id": str(workflow_output_display.id),
240
+ "output_type": inferred_type,
241
+ "node_input_id": str(node_input.id),
242
+ },
243
+ "inputs": [node_input.dict()],
244
+ "display_data": synthetic_display_data,
245
+ "base": final_output_node_base,
246
+ "definition": None,
247
+ }
241
248
 
242
249
  if source_node_display:
243
250
  source_handle_id = source_node_display.get_source_handle_id(
@@ -255,6 +262,19 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
255
262
  }
256
263
  )
257
264
 
265
+ elif isinstance(workflow_output.instance, OutputReference):
266
+ terminal_node_id = workflow_output.instance.outputs_class._node_class.__id__
267
+ serialized_terminal_node = serialized_nodes.get(terminal_node_id)
268
+ if serialized_terminal_node and isinstance(serialized_terminal_node["data"], dict):
269
+ serialized_terminal_node["data"]["name"] = workflow_output_display.name
270
+
271
+ output_values.append(
272
+ {
273
+ "output_variable_id": str(workflow_output_display.id),
274
+ "value": serialize_value(self.display_context, workflow_output.instance),
275
+ }
276
+ )
277
+
258
278
  output_variables.append(
259
279
  {
260
280
  "id": str(workflow_output_display.id),
@@ -309,13 +329,14 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
309
329
 
310
330
  return {
311
331
  "workflow_raw_data": {
312
- "nodes": nodes,
332
+ "nodes": list(serialized_nodes.values()),
313
333
  "edges": edges,
314
334
  "display_data": self.display_context.workflow_display.display_data.dict(),
315
335
  "definition": {
316
336
  "name": self._workflow.__name__,
317
337
  "module": cast(JsonArray, self._workflow.__module__.split(".")),
318
338
  },
339
+ "output_values": output_values,
319
340
  },
320
341
  "input_variables": input_variables,
321
342
  "state_variables": state_variables,
@@ -482,6 +503,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
482
503
  )
483
504
 
484
505
  return WorkflowDisplayContext(
506
+ client=self._client,
485
507
  workflow_display=workflow_meta_display,
486
508
  workflow_input_displays=workflow_input_displays,
487
509
  global_workflow_input_displays=global_workflow_input_displays,
@@ -1,6 +1,7 @@
1
1
  import types
2
2
  from typing import TYPE_CHECKING, Generic, Optional, Type, TypeVar
3
3
 
4
+ from vellum.client import Vellum as VellumClient
4
5
  from vellum.workflows.types.generics import WorkflowType
5
6
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
6
7
  from vellum_ee.workflows.display.utils.registry import get_from_workflow_display_registry
@@ -35,6 +36,7 @@ def get_workflow_display(
35
36
  *,
36
37
  workflow_class: Type[WorkflowType],
37
38
  parent_display_context: Optional[WorkflowDisplayContext] = None,
39
+ client: Optional[VellumClient] = None,
38
40
  dry_run: bool = False,
39
41
  # DEPRECATED: The following arguments will be removed in 0.15.0
40
42
  root_workflow_class: Optional[Type[WorkflowType]] = None,
@@ -42,5 +44,6 @@ def get_workflow_display(
42
44
  ) -> "BaseWorkflowDisplay":
43
45
  return _get_workflow_display_class(workflow_class=workflow_class)(
44
46
  parent_display_context=parent_display_context,
47
+ client=client,
45
48
  dry_run=dry_run,
46
49
  )