vellum-ai 0.10.4__py3-none-any.whl → 0.10.7__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/README.md +7 -52
  3. vellum/client/__init__.py +16 -136
  4. vellum/client/core/client_wrapper.py +1 -1
  5. vellum/client/resources/ad_hoc/client.py +14 -104
  6. vellum/client/resources/metric_definitions/client.py +113 -0
  7. vellum/client/resources/test_suites/client.py +8 -16
  8. vellum/client/resources/workflows/client.py +0 -32
  9. vellum/client/types/__init__.py +2 -0
  10. vellum/client/types/metric_definition_history_item.py +39 -0
  11. vellum/types/metric_definition_history_item.py +3 -0
  12. vellum/workflows/events/node.py +36 -3
  13. vellum/workflows/events/tests/test_event.py +89 -9
  14. vellum/workflows/nodes/__init__.py +6 -7
  15. vellum/workflows/nodes/bases/base.py +0 -1
  16. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -1
  17. vellum/workflows/nodes/core/templating_node/node.py +5 -1
  18. vellum/workflows/nodes/core/try_node/node.py +65 -27
  19. vellum/workflows/nodes/core/try_node/tests/test_node.py +17 -10
  20. vellum/workflows/nodes/displayable/__init__.py +2 -0
  21. vellum/workflows/nodes/displayable/bases/api_node/node.py +3 -3
  22. vellum/workflows/nodes/displayable/code_execution_node/node.py +5 -2
  23. vellum/workflows/nodes/displayable/conditional_node/node.py +2 -2
  24. vellum/workflows/nodes/displayable/final_output_node/node.py +6 -2
  25. vellum/workflows/nodes/displayable/note_node/__init__.py +5 -0
  26. vellum/workflows/nodes/displayable/note_node/node.py +10 -0
  27. vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +10 -11
  28. vellum/workflows/nodes/utils.py +2 -0
  29. vellum/workflows/outputs/base.py +26 -2
  30. vellum/workflows/ports/node_ports.py +2 -2
  31. vellum/workflows/ports/port.py +14 -0
  32. vellum/workflows/references/__init__.py +2 -0
  33. vellum/workflows/runner/runner.py +46 -33
  34. vellum/workflows/runner/types.py +1 -3
  35. vellum/workflows/state/encoder.py +2 -1
  36. vellum/workflows/types/tests/test_utils.py +15 -3
  37. vellum/workflows/types/utils.py +4 -1
  38. vellum/workflows/utils/vellum_variables.py +13 -1
  39. vellum/workflows/workflows/base.py +24 -1
  40. {vellum_ai-0.10.4.dist-info → vellum_ai-0.10.7.dist-info}/METADATA +8 -6
  41. {vellum_ai-0.10.4.dist-info → vellum_ai-0.10.7.dist-info}/RECORD +76 -69
  42. vellum_cli/CONTRIBUTING.md +66 -0
  43. vellum_cli/README.md +3 -0
  44. vellum_ee/workflows/display/base.py +2 -1
  45. vellum_ee/workflows/display/nodes/base_node_display.py +27 -4
  46. vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
  47. vellum_ee/workflows/display/nodes/vellum/api_node.py +3 -3
  48. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +4 -4
  49. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +86 -41
  50. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +4 -2
  51. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +3 -3
  52. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +4 -5
  53. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -9
  54. vellum_ee/workflows/display/nodes/vellum/map_node.py +23 -51
  55. vellum_ee/workflows/display/nodes/vellum/note_node.py +32 -0
  56. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +5 -5
  57. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
  58. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +2 -2
  59. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  60. vellum_ee/workflows/display/nodes/vellum/try_node.py +16 -4
  61. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +7 -3
  62. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +122 -107
  63. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +6 -5
  64. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -64
  65. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +15 -11
  66. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +6 -6
  67. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +6 -6
  68. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +4 -3
  69. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +7 -6
  70. vellum_ee/workflows/display/utils/vellum.py +3 -2
  71. vellum_ee/workflows/display/workflows/base_workflow_display.py +14 -9
  72. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +2 -7
  73. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +18 -16
  74. {vellum_ai-0.10.4.dist-info → vellum_ai-0.10.7.dist-info}/LICENSE +0 -0
  75. {vellum_ai-0.10.4.dist-info → vellum_ai-0.10.7.dist-info}/WHEEL +0 -0
  76. {vellum_ai-0.10.4.dist-info → vellum_ai-0.10.7.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from uuid import UUID
3
- from typing import Any, ClassVar, Dict, Generic, List, Optional, TypeVar
3
+ from typing import Any, ClassVar, Dict, Generic, List, Optional, TypeVar, Union, Tuple
4
4
 
5
5
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
6
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -29,7 +29,7 @@ from vellum.workflows.expressions.not_between import NotBetweenExpression
29
29
  from vellum.workflows.expressions.not_in import NotInExpression
30
30
  from vellum.workflows.expressions.or_ import OrExpression
31
31
  from vellum.workflows.nodes.displayable import ConditionalNode
32
- from vellum.workflows.types.core import JsonObject
32
+ from vellum.workflows.types.core import JsonObject, ConditionType
33
33
 
34
34
  _ConditionalNodeType = TypeVar("_ConditionalNodeType", bound=ConditionalNode)
35
35
 
@@ -39,11 +39,13 @@ class RuleIdMap:
39
39
  id: UUID
40
40
  lhs: Optional["RuleIdMap"]
41
41
  rhs: Optional["RuleIdMap"]
42
+ field_node_input_id: Optional[UUID]
43
+ value_node_input_id: Optional[UUID]
42
44
 
43
45
  @dataclass
44
46
  class ConditionId:
45
47
  id: UUID
46
- rule_group_id: UUID
48
+ rule_group_id: Optional[UUID]
47
49
 
48
50
 
49
51
  class BaseConditionalNodeDisplay(BaseNodeVellumDisplay[_ConditionalNodeType], Generic[_ConditionalNodeType]):
@@ -73,8 +75,11 @@ class BaseConditionalNodeDisplay(BaseNodeVellumDisplay[_ConditionalNodeType], Ge
73
75
  if rule_id_map is not None
74
76
  else str(uuid4_from_hash(f"{node_id}|rule|{','.join(str(p) for p in path)}"))
75
77
  )
76
- field_node_input_id = None
77
- value_node_input_id = None
78
+
79
+ result = self.get_nested_rule_details_by_path(rule_ids, path) if rule_ids else None
80
+ if result is None:
81
+ result = self._generate_hash_for_rule_ids(node_id, rule_id)
82
+ current_id, field_node_input_id, value_node_input_id = result
78
83
 
79
84
  # Recursive step: Keep recursing until we hit the other descriptors
80
85
  if isinstance(descriptor, (AndExpression, OrExpression)):
@@ -97,79 +102,89 @@ class BaseConditionalNodeDisplay(BaseNodeVellumDisplay[_ConditionalNodeType], Ge
97
102
  # Base cases for other descriptors
98
103
  elif isinstance(descriptor, (IsNullExpression, IsNotNullExpression)):
99
104
  expression_node_input = create_node_input(
100
- node_id, f"{rule_id}.field", descriptor._expression, display_context, None
105
+ node_id, f"{current_id}.field", descriptor._expression, display_context, field_node_input_id
101
106
  )
102
107
  node_inputs.append(expression_node_input)
103
- field_node_input_id = expression_node_input.id
108
+ field_node_input_id = UUID(expression_node_input.id)
104
109
  operator = self._convert_descriptor_to_operator(descriptor)
105
110
 
106
111
  elif isinstance(descriptor, (BetweenExpression, NotBetweenExpression)):
107
112
  field_node_input = create_node_input(
108
- node_id, f"{rule_id}.field", descriptor._value, display_context, None
113
+ node_id, f"{current_id}.field", descriptor._value, display_context, field_node_input_id
109
114
  )
110
115
  value_node_input = create_node_input(
111
- node_id, f"{rule_id}.value", f"{descriptor._start},{descriptor._end}", display_context, None
116
+ node_id, f"{current_id}.value", f"{descriptor._start},{descriptor._end}", display_context, value_node_input_id
112
117
  )
113
118
  node_inputs.extend([field_node_input, value_node_input])
114
119
  operator = self._convert_descriptor_to_operator(descriptor)
115
- field_node_input_id = field_node_input.id
116
- value_node_input_id = value_node_input.id
120
+ field_node_input_id = UUID(field_node_input.id)
121
+ value_node_input_id = UUID(value_node_input.id)
117
122
 
118
123
  else:
119
124
  lhs = descriptor._lhs # type: ignore[attr-defined]
120
125
  rhs = descriptor._rhs # type: ignore[attr-defined]
121
126
 
122
- lhs_node_input = create_node_input(node_id, f"{rule_id}.field", lhs, display_context, None)
127
+ lhs_node_input = create_node_input(node_id, f"{current_id}.field", lhs, display_context, field_node_input_id)
123
128
  node_inputs.append(lhs_node_input)
124
129
 
125
130
  if descriptor._rhs is not None: # type: ignore[attr-defined]
126
- rhs_node_input = create_node_input(node_id, f"{rule_id}.value", rhs, display_context, None)
131
+ rhs_node_input = create_node_input(node_id, f"{current_id}.value", rhs, display_context, value_node_input_id)
127
132
  node_inputs.append(rhs_node_input)
128
- value_node_input_id = rhs_node_input.id
133
+ value_node_input_id = UUID(rhs_node_input.id)
129
134
 
130
135
  operator = self._convert_descriptor_to_operator(descriptor)
131
- field_node_input_id = lhs_node_input.id
136
+ field_node_input_id = UUID(lhs_node_input.id)
132
137
 
133
138
  return {
134
- "id": rule_id,
139
+ "id": str(current_id),
135
140
  "rules": None,
136
141
  "combinator": None,
137
142
  "negated": False,
138
- "field_node_input_id": field_node_input_id,
143
+ "field_node_input_id": str(field_node_input_id),
139
144
  "operator": operator,
140
- "value_node_input_id": value_node_input_id,
145
+ "value_node_input_id": str(value_node_input_id),
141
146
  }
142
147
 
143
148
  conditions = []
144
149
  for idx, port in enumerate(node.Ports):
145
- if port._condition is None or port._condition_type is None:
146
- continue
147
-
148
150
 
149
151
  condition_id = str(condition_ids[idx].id if condition_ids else uuid4_from_hash(f"{node_id}|conditions|{idx}"))
150
152
  rule_group_id = str(condition_ids[idx].rule_group_id if condition_ids else uuid4_from_hash(f"{condition_id}|rule_group"))
151
153
  source_handle_id = str(source_handle_ids.get(idx) or uuid4_from_hash(f"{node_id}|handles|{idx}"))
152
154
 
153
- rule_ids = self._get_rule_ids()
154
- condition_rule = serialize_rule(port._condition, [idx], rule_ids[idx] if len(rule_ids) > idx else None)
155
- rules = condition_rule["rules"] if condition_rule["rules"] else [condition_rule]
156
-
157
- conditions.append(
158
- {
159
- "id": condition_id,
160
- "type": port._condition_type.value,
161
- "source_handle_id": source_handle_id,
162
- "data": {
163
- "id": rule_group_id,
164
- "rules": rules,
165
- "combinator": "AND",
166
- "negated": False,
167
- "field_node_input_id": None,
168
- "operator": None,
169
- "value_node_input_id": None,
170
- },
171
- }
172
- )
155
+ if port._condition is None:
156
+ if port._condition_type == ConditionType.ELSE:
157
+ conditions.append({
158
+ "id": condition_id,
159
+ "type": ConditionType.ELSE.value,
160
+ "source_handle_id": source_handle_id,
161
+ "data": None
162
+ })
163
+ else:
164
+ continue
165
+
166
+ else:
167
+ rule_ids = self._get_rule_ids()
168
+ condition_rule = serialize_rule(port._condition, [idx], rule_ids[idx] if len(rule_ids) > idx else None)
169
+ rules = condition_rule["rules"] if condition_rule["rules"] else [condition_rule]
170
+ if port._condition_type is None:
171
+ raise ValueError("Condition type is None, but a valid ConditionType is expected.")
172
+ conditions.append(
173
+ {
174
+ "id": condition_id,
175
+ "type": port._condition_type.value,
176
+ "source_handle_id": source_handle_id,
177
+ "data": {
178
+ "id": rule_group_id,
179
+ "rules": rules,
180
+ "combinator": "AND",
181
+ "negated": False,
182
+ "field_node_input_id": None,
183
+ "operator": None,
184
+ "value_node_input_id": None,
185
+ },
186
+ }
187
+ )
173
188
 
174
189
  return {
175
190
  "id": str(node_id),
@@ -225,6 +240,36 @@ class BaseConditionalNodeDisplay(BaseNodeVellumDisplay[_ConditionalNodeType], Ge
225
240
  else:
226
241
  raise ValueError(f"Unsupported descriptor type: {descriptor}")
227
242
 
243
+ def get_nested_rule_details_by_path(
244
+ self, rule_ids: List[RuleIdMap], path: List[int]
245
+ ) -> Union[Tuple[UUID, Optional[UUID], Optional[UUID]], None]:
246
+ current_rule = rule_ids[path[0]]
247
+
248
+ for step in path[1:]:
249
+ if step == 0 and current_rule.lhs:
250
+ current_rule = current_rule.lhs
251
+ elif step == 1 and current_rule.rhs:
252
+ current_rule = current_rule.rhs
253
+ else:
254
+ return None
255
+
256
+ # This is essentially a leaf
257
+ if current_rule.lhs and current_rule.lhs.lhs is None and current_rule.lhs.rhs is None:
258
+ return (
259
+ current_rule.lhs.id,
260
+ current_rule.lhs.field_node_input_id,
261
+ current_rule.lhs.value_node_input_id,
262
+ )
263
+
264
+ return None
265
+
266
+ def _generate_hash_for_rule_ids(self, node_id, rule_id) -> Tuple[UUID, UUID, UUID]:
267
+ return (
268
+ uuid4_from_hash(f"{node_id}|{rule_id}|current"),
269
+ uuid4_from_hash(f"{node_id}|{rule_id}||field"),
270
+ uuid4_from_hash(f"{node_id}|{rule_id}||value")
271
+ )
272
+
228
273
  def _get_source_handle_ids(self) -> Dict[int, UUID]:
229
274
  return self._get_explicit_node_display_attr("source_handle_ids", Dict[int, UUID]) or {}
230
275
 
@@ -1,14 +1,16 @@
1
1
  from uuid import UUID
2
2
  from typing import Any, ClassVar, Generic, Optional, TypeVar
3
3
 
4
+ from vellum.workflows.nodes.core.map_node.node import MapNode
5
+ from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
6
+ from vellum.workflows.references.output import OutputReference
7
+ from vellum.workflows.types.core import JsonObject
4
8
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
5
9
  from vellum_ee.workflows.display.nodes.utils import to_kebab_case
6
10
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
7
11
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
8
12
  from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
9
13
  from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
10
- from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
11
- from vellum.workflows.types.core import JsonObject
12
14
 
13
15
  _FinalOutputNodeType = TypeVar("_FinalOutputNodeType", bound=FinalOutputNode)
14
16
 
@@ -1,12 +1,12 @@
1
1
  from uuid import UUID
2
2
  from typing import Any, ClassVar, Dict, Generic, Optional, TypeVar
3
3
 
4
+ from vellum.workflows.nodes import GuardrailNode
5
+ from vellum.workflows.types.core import JsonObject
4
6
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
5
7
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
6
8
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
7
9
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
8
- from vellum.workflows.nodes import GuardrailNode
9
- from vellum.workflows.types.core import JsonObject
10
10
 
11
11
  _GuardrailNodeType = TypeVar("_GuardrailNodeType", bound=GuardrailNode)
12
12
 
@@ -15,7 +15,7 @@ class BaseGuardrailNodeDisplay(BaseNodeVellumDisplay[_GuardrailNodeType], Generi
15
15
  metric_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
16
16
 
17
17
  def serialize(
18
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
18
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
19
19
  ) -> JsonObject:
20
20
  node = self._node
21
21
  node_id = self.node_id
@@ -2,7 +2,9 @@ from uuid import UUID
2
2
  from typing import Any, ClassVar, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union, cast
3
3
 
4
4
  from vellum import PromptBlock, RichTextChildBlock, VellumVariable
5
-
5
+ from vellum.workflows.nodes import InlinePromptNode
6
+ from vellum.workflows.references import OutputReference
7
+ from vellum.workflows.types.core import JsonObject
6
8
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
9
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
8
10
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -10,9 +12,6 @@ from vellum_ee.workflows.display.types import WorkflowDisplayContext
10
12
  from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
11
13
  from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
12
14
  from vellum_ee.workflows.display.vellum import NodeInput
13
- from vellum.workflows.nodes import InlinePromptNode
14
- from vellum.workflows.references import OutputReference
15
- from vellum.workflows.types.core import JsonObject
16
15
 
17
16
  _InlinePromptNodeType = TypeVar("_InlinePromptNodeType", bound=InlinePromptNode)
18
17
 
@@ -23,7 +22,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType],
23
22
  prompt_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
24
23
 
25
24
  def serialize(
26
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
25
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
27
26
  ) -> JsonObject:
28
27
  node = self._node
29
28
  node_id = self.node_id
@@ -1,17 +1,16 @@
1
1
  from uuid import UUID
2
- from typing import Any, ClassVar, Dict, Generic, List, Optional, Tuple, Type, TypeVar
2
+ from typing import Any, ClassVar, Dict, Generic, List, Optional, Tuple, Type, TypeVar, cast
3
3
 
4
4
  from vellum import VellumVariable
5
-
5
+ from vellum.workflows.nodes import InlineSubworkflowNode
6
+ from vellum.workflows.types.core import JsonObject
6
7
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
8
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
8
9
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
9
10
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
10
11
  from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
11
- from vellum_ee.workflows.display.vellum import NodeInput
12
+ from vellum_ee.workflows.display.vellum import NodeInput, WorkflowOutputVellumDisplay
12
13
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
13
- from vellum.workflows.nodes import InlineSubworkflowNode
14
- from vellum.workflows.types.core import JsonObject
15
14
 
16
15
  _InlineSubworkflowNodeType = TypeVar("_InlineSubworkflowNodeType", bound=InlineSubworkflowNode)
17
16
 
@@ -22,19 +21,19 @@ class BaseInlineSubworkflowNodeDisplay(
22
21
  workflow_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
23
22
 
24
23
  def serialize(
25
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
24
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
26
25
  ) -> JsonObject:
27
26
  node = self._node
28
27
  node_id = self.node_id
29
28
 
30
29
  node_inputs, workflow_inputs = self._generate_node_and_workflow_inputs(node_id, node, display_context)
31
- workflow_outputs = self._generate_workflow_outputs(node)
32
30
 
33
31
  subworkflow_display = get_workflow_display(
34
32
  base_display_class=display_context.workflow_display_class,
35
33
  workflow_class=raise_if_descriptor(node.subworkflow),
36
34
  parent_display_context=display_context,
37
35
  )
36
+ workflow_outputs = self._generate_workflow_outputs(node, subworkflow_display.display_context)
38
37
  serialized_subworkflow = subworkflow_display.serialize()
39
38
 
40
39
  return {
@@ -87,13 +86,14 @@ class BaseInlineSubworkflowNodeDisplay(
87
86
  def _generate_workflow_outputs(
88
87
  self,
89
88
  node: Type[InlineSubworkflowNode],
89
+ display_context: WorkflowDisplayContext,
90
90
  ) -> List[VellumVariable]:
91
91
  workflow_outputs: List[VellumVariable] = []
92
92
  for output_descriptor in raise_if_descriptor(node.subworkflow).Outputs: # type: ignore[union-attr]
93
- node_output_display = self.get_node_output_display(output_descriptor)
93
+ workflow_output_display = cast(WorkflowOutputVellumDisplay, display_context.workflow_output_displays[output_descriptor])
94
94
  output_type = infer_vellum_variable_type(output_descriptor)
95
95
  workflow_outputs.append(
96
- VellumVariable(id=str(node_output_display.id), key=node_output_display.name, type=output_type)
96
+ VellumVariable(id=str(workflow_output_display.id), key=workflow_output_display.name, type=output_type)
97
97
  )
98
98
 
99
99
  return workflow_outputs
@@ -1,59 +1,32 @@
1
1
  from uuid import UUID
2
- from typing import Any, ClassVar, Dict, Generic, List, Optional, Type, TypeVar
3
-
4
- from vellum import VellumVariable
2
+ from typing import Collection, Dict, Generic, List, Optional, TypeVar, cast
5
3
 
4
+ from vellum.workflows.nodes import MapNode
5
+ from vellum.workflows.types.core import JsonObject
6
6
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
7
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
8
8
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
9
9
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
10
- from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
11
- from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
12
10
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
13
- from vellum.workflows.nodes import MapNode
14
- from vellum.workflows.types.core import JsonObject
15
11
 
16
12
  _MapNodeType = TypeVar("_MapNodeType", bound=MapNode)
17
13
 
18
14
 
19
15
  class BaseMapNodeDisplay(BaseNodeVellumDisplay[_MapNodeType], Generic[_MapNodeType]):
20
- workflow_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
21
-
22
16
  def serialize(
23
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
17
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
24
18
  ) -> JsonObject:
25
19
  node = self._node
26
20
  node_id = self.node_id
27
21
 
28
- workflow_inputs: List[VellumVariable] = []
29
22
  subworkflow = raise_if_descriptor(node.subworkflow)
30
- for descriptor in subworkflow.get_inputs_class():
31
- # In WaC it's always 'all_items'
32
- # In Vellum it's always 'items'
33
- variable_name = descriptor.name if descriptor.name != "all_items" else "items"
34
- variable_id = str(
35
- self.workflow_input_ids_by_name.get(variable_name) or uuid4_from_hash(f"{self.node_id}|{variable_name}")
36
- )
37
- workflow_inputs.append(
38
- VellumVariable(
39
- id=variable_id,
40
- key=variable_name,
41
- type=infer_vellum_variable_type(descriptor),
42
- )
43
- )
44
-
45
- items_workflow_input = next(input for input in workflow_inputs if input.key == "items")
46
- item_workflow_input = next(input for input in workflow_inputs if input.key == "item")
47
- index_workflow_input = next(input for input in workflow_inputs if input.key == "index")
48
-
49
- workflow_outputs = self._generate_workflow_outputs(node)
50
23
 
51
24
  items_node_input = create_node_input(
52
25
  node_id=node_id,
53
26
  input_name="items",
54
27
  value=node.items,
55
28
  display_context=display_context,
56
- input_id=UUID(items_workflow_input.id),
29
+ input_id=self.node_input_ids_by_name.get("items"),
57
30
  )
58
31
  node_inputs = [items_node_input]
59
32
 
@@ -63,6 +36,19 @@ class BaseMapNodeDisplay(BaseNodeVellumDisplay[_MapNodeType], Generic[_MapNodeTy
63
36
  )
64
37
  serialized_subworkflow = subworkflow_display.serialize()
65
38
 
39
+ renamed_input_variables = []
40
+ for input_variable in cast(List[Dict[str, str]], serialized_subworkflow["input_variables"]):
41
+ if input_variable["key"] == "all_items":
42
+ renamed_item = { **input_variable, "key": "items" }
43
+ renamed_input_variables.append(renamed_item)
44
+ else:
45
+ renamed_input_variables.append(input_variable)
46
+
47
+ # Note: This must match the items input ID for the map node's node input
48
+ items_workflow_input_id = next(input_variable["id"] for input_variable in renamed_input_variables if input_variable["key"] == "items")
49
+ item_workflow_input_id = next(input_variable["id"] for input_variable in renamed_input_variables if input_variable["key"] == "item")
50
+ index_workflow_input_id = next(input_variable["id"] for input_variable in renamed_input_variables if input_variable["key"] == "index")
51
+
66
52
  return {
67
53
  "id": str(node_id),
68
54
  "type": "MAP",
@@ -74,27 +60,13 @@ class BaseMapNodeDisplay(BaseNodeVellumDisplay[_MapNodeType], Generic[_MapNodeTy
74
60
  "target_handle_id": str(self.get_target_handle_id()),
75
61
  "variant": "INLINE",
76
62
  "workflow_raw_data": serialized_subworkflow["workflow_raw_data"],
77
- "input_variables": [workflow_input.dict() for workflow_input in workflow_inputs],
78
- "output_variables": [workflow_output.dict() for workflow_output in workflow_outputs],
63
+ "input_variables": cast(JsonObject, renamed_input_variables),
64
+ "output_variables": serialized_subworkflow["output_variables"],
79
65
  "concurrency": raise_if_descriptor(node.concurrency),
80
- "items_input_id": str(items_workflow_input.id),
81
- "item_input_id": str(item_workflow_input.id),
82
- "index_input_id": str(index_workflow_input.id),
66
+ "items_input_id": items_workflow_input_id,
67
+ "item_input_id": item_workflow_input_id,
68
+ "index_input_id": index_workflow_input_id,
83
69
  },
84
70
  "display_data": self.get_display_data().dict(),
85
71
  "definition": self.get_definition().dict(),
86
72
  }
87
-
88
- def _generate_workflow_outputs(
89
- self,
90
- node: Type[MapNode],
91
- ) -> List[VellumVariable]:
92
- workflow_outputs: List[VellumVariable] = []
93
- for output_descriptor in raise_if_descriptor(node.subworkflow).Outputs: # type: ignore[union-attr]
94
- node_output_display = self.get_node_output_display(output_descriptor)
95
- output_type = infer_vellum_variable_type(output_descriptor)
96
- workflow_outputs.append(
97
- VellumVariable(id=str(node_output_display.id), key=node_output_display.name, type=output_type)
98
- )
99
-
100
- return workflow_outputs
@@ -0,0 +1,32 @@
1
+ import json
2
+ from typing import Any, ClassVar, Dict, Generic, TypeVar, Union
3
+
4
+ from vellum.workflows.nodes import NoteNode
5
+ from vellum.workflows.types.core import JsonObject
6
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
8
+
9
+ _NoteNodeType = TypeVar("_NoteNodeType", bound=NoteNode)
10
+
11
+
12
+ class BaseNoteNodeDisplay(BaseNodeVellumDisplay[_NoteNodeType], Generic[_NoteNodeType]):
13
+ text: ClassVar[str] = ""
14
+ style: ClassVar[Union[Dict[str, Any], None]] = None
15
+
16
+ def serialize(
17
+ self, display_context: WorkflowDisplayContext, **kwargs: Any
18
+ ) -> JsonObject:
19
+ node_id = self.node_id
20
+
21
+ return {
22
+ "id": str(node_id),
23
+ "type": "NOTE",
24
+ "inputs": [],
25
+ "data": {
26
+ "label": self.label,
27
+ "text": self.text,
28
+ "style": json.dumps(self.style) if self.style else None,
29
+ },
30
+ "display_data": self.get_display_data().dict(),
31
+ "definition": self.get_definition().dict(),
32
+ }
@@ -1,14 +1,14 @@
1
1
  from uuid import UUID
2
2
  from typing import Any, ClassVar, Dict, Generic, Optional, TypeVar, cast
3
3
 
4
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
5
- from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
6
- from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
7
- from vellum_ee.workflows.display.types import WorkflowDisplayContext
8
4
  from vellum.workflows.nodes.displayable.prompt_deployment_node import PromptDeploymentNode
9
5
  from vellum.workflows.references import OutputReference
10
6
  from vellum.workflows.types.core import JsonObject
11
7
  from vellum.workflows.vellum_client import create_vellum_client
8
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
9
+ from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
10
+ from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
11
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
12
12
 
13
13
  _PromptDeploymentNodeType = TypeVar("_PromptDeploymentNodeType", bound=PromptDeploymentNode)
14
14
 
@@ -21,7 +21,7 @@ class BasePromptDeploymentNodeDisplay(
21
21
  prompt_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
22
22
 
23
23
  def serialize(
24
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
24
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
25
25
  ) -> JsonObject:
26
26
  node = self._node
27
27
  node_id = self.node_id
@@ -31,7 +31,7 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
31
31
  variable_ids: Optional[VariableIdMap] = None
32
32
 
33
33
  def serialize(
34
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
34
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
35
35
  ) -> JsonObject:
36
36
  node = self._node
37
37
  node_id = self.node_id
@@ -16,9 +16,9 @@ class BaseSubworkflowDeploymentNodeDisplay(
16
16
  BaseNodeVellumDisplay[_SubworkflowDeploymentNodeType], Generic[_SubworkflowDeploymentNodeType]
17
17
  ):
18
18
  subworkflow_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
19
-
19
+
20
20
  def serialize(
21
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
21
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
22
22
  ) -> JsonObject:
23
23
  node = self._node
24
24
  node_id = self.node_id
@@ -17,7 +17,7 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
17
17
  input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
18
18
 
19
19
  def serialize(
20
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
20
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
21
21
  ) -> JsonObject:
22
22
  node = self._node
23
23
  node_id = self.node_id
@@ -1,13 +1,13 @@
1
1
  from uuid import UUID
2
2
  from typing import Any, ClassVar, Generic, Optional, TypeVar
3
3
 
4
+ from vellum.workflows.nodes.core.try_node.node import TryNode
5
+ from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME, get_wrapped_node
6
+ from vellum.workflows.types.core import JsonObject
4
7
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
5
8
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
6
9
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
7
10
  from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
8
- from vellum.workflows.nodes.core.try_node.node import TryNode
9
- from vellum.workflows.nodes.utils import get_wrapped_node
10
- from vellum.workflows.types.core import JsonObject
11
11
 
12
12
  _TryNodeType = TypeVar("_TryNodeType", bound=TryNode)
13
13
 
@@ -32,7 +32,19 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
32
32
 
33
33
  serialized_node = node_display.serialize(
34
34
  display_context,
35
- error_output_id=str(self.error_output_id or uuid4_from_hash(f"{node_display.node_id}|error_output_id")),
35
+ error_output_id=self.error_output_id or uuid4_from_hash(f"{node_display.node_id}|error_output_id"),
36
36
  )
37
37
 
38
+ serialized_node_definition = serialized_node.get("definition")
39
+ if isinstance(serialized_node_definition, dict):
40
+ serialized_node_definition_module = serialized_node_definition.get("module")
41
+ if isinstance(serialized_node_definition_module, list):
42
+ serialized_node_definition_module.extend(
43
+ [
44
+ serialized_node_definition["name"],
45
+ ADORNMENT_MODULE_NAME,
46
+ ]
47
+ )
48
+ serialized_node_definition["name"] = node.__name__
49
+
38
50
  return serialized_node
@@ -1,10 +1,12 @@
1
1
  from deepdiff import DeepDiff
2
2
 
3
- from tests.workflows.basic_code_execution_node.try_workflow import TrySimpleCodeExecutionWorkflow
4
- from tests.workflows.basic_code_execution_node.workflow import SimpleCodeExecutionWorkflow
3
+ from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
5
4
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
6
5
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
7
6
 
7
+ from tests.workflows.basic_code_execution_node.try_workflow import TrySimpleCodeExecutionWorkflow
8
+ from tests.workflows.basic_code_execution_node.workflow import SimpleCodeExecutionWorkflow
9
+
8
10
 
9
11
  def test_serialize_workflow():
10
12
  # GIVEN a Workflow with a code execution node
@@ -428,8 +430,10 @@ def test_serialize_workflow__try_wrapped():
428
430
  "workflows",
429
431
  "basic_code_execution_node",
430
432
  "try_workflow",
433
+ "SimpleCodeExecutionNode",
434
+ ADORNMENT_MODULE_NAME,
431
435
  ],
432
- "name": "SimpleCodeExecutionNode",
436
+ "name": "TryNode",
433
437
  },
434
438
  }
435
439