vellum-ai 0.14.11__py3-none-any.whl → 0.14.13__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 (28) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/descriptors/base.py +3 -0
  3. vellum/workflows/events/workflow.py +23 -0
  4. vellum/workflows/inputs/base.py +26 -18
  5. vellum/workflows/inputs/tests/test_inputs.py +1 -1
  6. vellum/workflows/nodes/bases/base.py +7 -0
  7. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +7 -0
  8. vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +32 -0
  9. vellum/workflows/nodes/core/map_node/node.py +28 -7
  10. vellum/workflows/nodes/core/map_node/tests/test_node.py +31 -0
  11. vellum/workflows/nodes/core/try_node/node.py +7 -0
  12. vellum/workflows/nodes/core/try_node/tests/test_node.py +32 -0
  13. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +5 -4
  14. vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +111 -0
  15. vellum/workflows/nodes/mocks.py +229 -2
  16. vellum/workflows/nodes/tests/__init__.py +0 -0
  17. vellum/workflows/nodes/tests/test_mocks.py +207 -0
  18. vellum/workflows/outputs/base.py +1 -1
  19. {vellum_ai-0.14.11.dist-info → vellum_ai-0.14.13.dist-info}/METADATA +2 -2
  20. {vellum_ai-0.14.11.dist-info → vellum_ai-0.14.13.dist-info}/RECORD +28 -26
  21. vellum_ee/workflows/display/nodes/base_node_display.py +20 -4
  22. vellum_ee/workflows/display/nodes/get_node_display_class.py +9 -0
  23. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +24 -1
  24. vellum_ee/workflows/display/workflows/base_workflow_display.py +2 -2
  25. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +20 -0
  26. {vellum_ai-0.14.11.dist-info → vellum_ai-0.14.13.dist-info}/LICENSE +0 -0
  27. {vellum_ai-0.14.11.dist-info → vellum_ai-0.14.13.dist-info}/WHEEL +0 -0
  28. {vellum_ai-0.14.11.dist-info → vellum_ai-0.14.13.dist-info}/entry_points.txt +0 -0
@@ -59,11 +59,27 @@ _NodeDisplayAttrType = TypeVar("_NodeDisplayAttrType")
59
59
  class BaseNodeDisplayMeta(type):
60
60
  def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
61
61
  cls = super().__new__(mcs, name, bases, dct)
62
- if isinstance(dct.get("node_id"), UUID):
62
+ base_node_display_class = cast(Type["BaseNodeDisplay"], cls)
63
+ node_class = base_node_display_class.infer_node_class()
64
+ if not issubclass(node_class, BaseNode):
65
+ return cls
66
+
67
+ display_node_id = dct.get("node_id")
68
+ if isinstance(display_node_id, UUID):
63
69
  # Display classes are able to override the id of the node class it's parameterized by
64
- base_node_display_class = cast(Type["BaseNodeDisplay"], cls)
65
- node_class = base_node_display_class.infer_node_class()
66
- node_class.__id__ = dct["node_id"]
70
+ node_class.__id__ = display_node_id
71
+
72
+ output_display = dct.get("output_display")
73
+ if isinstance(output_display, dict):
74
+ # And the node class' output ids
75
+ for reference, node_output_display in output_display.items():
76
+ if not isinstance(reference, OutputReference):
77
+ continue
78
+ if not isinstance(node_output_display, NodeOutputDisplay):
79
+ continue
80
+
81
+ node_class.__output_ids__[reference.name] = node_output_display.id
82
+
67
83
  return cls
68
84
 
69
85
 
@@ -2,6 +2,7 @@ import types
2
2
  from typing import TYPE_CHECKING, Optional, Type
3
3
 
4
4
  from vellum.workflows.types.generics import NodeType
5
+ from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from vellum_ee.workflows.display.types import NodeDisplayType
@@ -29,4 +30,12 @@ def get_node_display_class(
29
30
  f"{node_class.__name__}Display",
30
31
  bases=(NodeDisplayBaseClass,),
31
32
  )
33
+ output_display = {
34
+ ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
35
+ for ref in node_class.Outputs
36
+ if ref.name in node_class.__output_ids__
37
+ }
38
+ if output_display:
39
+ setattr(NodeDisplayClass, "output_display", output_display)
40
+
32
41
  return NodeDisplayClass
@@ -115,7 +115,8 @@ def test_serialize_node__retry(serialize_node):
115
115
  )
116
116
 
117
117
 
118
- def test_serialize_node__retry__no_display(): # GIVEN an adornment node
118
+ def test_serialize_node__retry__no_display():
119
+ # GIVEN an adornment node
119
120
  @RetryNode.wrap(max_attempts=5)
120
121
  class StartNode(BaseNode):
121
122
  pass
@@ -212,6 +213,28 @@ def test_serialize_node__try(serialize_node):
212
213
  )
213
214
 
214
215
 
216
+ def test_serialize_node__try__no_display():
217
+ # GIVEN an adornment node
218
+ @TryNode.wrap()
219
+ class StartNode(BaseNode):
220
+ pass
221
+
222
+ # AND a workflow that uses the adornment node
223
+ class MyWorkflow(BaseWorkflow):
224
+ graph = StartNode
225
+
226
+ # WHEN we serialize the workflow
227
+ workflow_display = get_workflow_display(
228
+ base_display_class=VellumWorkflowDisplay,
229
+ workflow_class=MyWorkflow,
230
+ )
231
+
232
+ exec_config = workflow_display.serialize()
233
+
234
+ # THEN the workflow display is created successfully
235
+ assert exec_config is not None
236
+
237
+
215
238
  def test_serialize_node__stacked():
216
239
  @TryNode.wrap()
217
240
  @RetryNode.wrap(max_attempts=5)
@@ -411,7 +411,7 @@ class BaseWorkflowDisplay(
411
411
  input_display = {}
412
412
  if isinstance(current_node_display, BaseNodeVellumDisplay):
413
413
  input_display = current_node_display.node_input_ids_by_name
414
- node_display_meta = {
414
+ output_display = {
415
415
  output.name: current_node_display.output_display[output].id
416
416
  for output in current_node_display.output_display
417
417
  }
@@ -434,7 +434,7 @@ class BaseWorkflowDisplay(
434
434
 
435
435
  node_event_displays[node_id] = NodeEventDisplayContext(
436
436
  input_display=input_display,
437
- output_display=node_display_meta,
437
+ output_display=output_display,
438
438
  port_display=port_display_meta,
439
439
  subworkflow_display=subworkflow_display_context,
440
440
  )
@@ -95,6 +95,26 @@ def test_serialize_workflow__node_display_class_not_registered():
95
95
  assert data is not None
96
96
 
97
97
 
98
+ def test_get_event_display_context__node_display_filled_without_base_display():
99
+ # GIVEN a simple workflow
100
+ class StartNode(BaseNode):
101
+ class Outputs(BaseNode.Outputs):
102
+ foo: str
103
+
104
+ class MyWorkflow(BaseWorkflow):
105
+ graph = StartNode
106
+
107
+ # WHEN we gather the event display context
108
+ display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
109
+
110
+ # THEN the node display should be included
111
+ assert str(StartNode.__id__) in display_context.node_displays
112
+ node_event_display = display_context.node_displays[str(StartNode.__id__)]
113
+
114
+ # AND so should their output ids
115
+ assert StartNode.__output_ids__ == node_event_display.output_display
116
+
117
+
98
118
  def test_get_event_display_context__node_display_to_include_subworkflow_display():
99
119
  # GIVEN a simple workflow
100
120
  class InnerNode(BaseNode):