vellum-ai 0.14.45__py3-none-any.whl → 0.14.47__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 (162) hide show
  1. vellum/client/README.md +2 -2
  2. vellum/client/__init__.py +72 -6
  3. vellum/client/core/client_wrapper.py +1 -1
  4. vellum/client/core/file.py +13 -8
  5. vellum/client/core/http_client.py +26 -14
  6. vellum/client/core/pydantic_utilities.py +2 -2
  7. vellum/client/core/request_options.py +3 -0
  8. vellum/client/resources/ad_hoc/client.py +14 -2
  9. vellum/client/resources/container_images/client.py +6 -0
  10. vellum/client/resources/deployments/client.py +12 -0
  11. vellum/client/resources/document_indexes/client.py +18 -0
  12. vellum/client/resources/documents/client.py +6 -0
  13. vellum/client/resources/folder_entities/client.py +6 -0
  14. vellum/client/resources/metric_definitions/client.py +6 -0
  15. vellum/client/resources/prompts/client.py +6 -0
  16. vellum/client/resources/sandboxes/client.py +12 -0
  17. vellum/client/resources/test_suite_runs/client.py +6 -0
  18. vellum/client/resources/test_suites/client.py +2 -2
  19. vellum/client/resources/workflow_deployments/client.py +6 -0
  20. vellum/client/resources/workflow_sandboxes/client.py +6 -0
  21. vellum/client/resources/workflows/client.py +6 -4
  22. vellum/client/resources/workspace_secrets/client.py +6 -0
  23. vellum/client/types/api_request_parent_context.py +0 -6
  24. vellum/client/types/array_input.py +0 -5
  25. vellum/client/types/code_execution_node_array_result.py +0 -5
  26. vellum/client/types/code_execution_node_result.py +0 -5
  27. vellum/client/types/code_execution_node_result_data.py +0 -5
  28. vellum/client/types/code_executor_response.py +0 -5
  29. vellum/client/types/create_test_suite_test_case_request.py +0 -5
  30. vellum/client/types/deployment_history_item.py +0 -5
  31. vellum/client/types/deployment_read.py +0 -5
  32. vellum/client/types/execute_workflow_response.py +0 -5
  33. vellum/client/types/execution_array_vellum_value.py +0 -5
  34. vellum/client/types/external_test_case_execution.py +0 -5
  35. vellum/client/types/external_test_case_execution_request.py +0 -5
  36. vellum/client/types/fulfilled_execute_workflow_workflow_result_event.py +0 -7
  37. vellum/client/types/fulfilled_workflow_node_result_event.py +0 -5
  38. vellum/client/types/initiated_workflow_node_result_event.py +0 -5
  39. vellum/client/types/metadata_filter_config_request.py +0 -5
  40. vellum/client/types/metric_definition_execution.py +0 -5
  41. vellum/client/types/metric_definition_history_item.py +0 -5
  42. vellum/client/types/named_test_case_array_variable_value.py +0 -5
  43. vellum/client/types/named_test_case_array_variable_value_request.py +0 -7
  44. vellum/client/types/node_execution_fulfilled_event.py +0 -11
  45. vellum/client/types/node_execution_initiated_event.py +0 -11
  46. vellum/client/types/node_execution_paused_event.py +0 -11
  47. vellum/client/types/node_execution_rejected_event.py +0 -11
  48. vellum/client/types/node_execution_resumed_event.py +0 -11
  49. vellum/client/types/node_execution_span.py +0 -11
  50. vellum/client/types/node_execution_streaming_event.py +0 -11
  51. vellum/client/types/node_input_compiled_array_value.py +0 -5
  52. vellum/client/types/node_output_compiled_array_value.py +0 -5
  53. vellum/client/types/node_parent_context.py +0 -6
  54. vellum/client/types/paginated_slim_deployment_read_list.py +0 -5
  55. vellum/client/types/paginated_slim_workflow_deployment_list.py +0 -5
  56. vellum/client/types/paginated_test_suite_run_execution_list.py +0 -5
  57. vellum/client/types/paginated_test_suite_test_case_list.py +0 -5
  58. vellum/client/types/prompt_deployment_parent_context.py +0 -6
  59. vellum/client/types/prompt_exec_config.py +0 -6
  60. vellum/client/types/rejected_workflow_node_result_event.py +0 -5
  61. vellum/client/types/replace_test_suite_test_case_request.py +0 -5
  62. vellum/client/types/search_filters_request.py +0 -7
  63. vellum/client/types/search_request_options_request.py +0 -7
  64. vellum/client/types/slim_deployment_read.py +0 -5
  65. vellum/client/types/slim_workflow_deployment.py +0 -5
  66. vellum/client/types/slim_workflow_execution_read.py +0 -12
  67. vellum/client/types/span_link.py +0 -6
  68. vellum/client/types/streaming_workflow_node_result_event.py +0 -5
  69. vellum/client/types/templating_node_array_result.py +0 -5
  70. vellum/client/types/templating_node_result.py +0 -5
  71. vellum/client/types/templating_node_result_data.py +0 -5
  72. vellum/client/types/terminal_node_array_result.py +0 -5
  73. vellum/client/types/terminal_node_result.py +0 -5
  74. vellum/client/types/terminal_node_result_data.py +0 -5
  75. vellum/client/types/test_case_array_variable_value.py +0 -5
  76. vellum/client/types/test_suite_run_execution.py +0 -5
  77. vellum/client/types/test_suite_run_execution_array_output.py +0 -5
  78. vellum/client/types/test_suite_run_execution_metric_result.py +0 -5
  79. vellum/client/types/test_suite_run_external_exec_config.py +0 -5
  80. vellum/client/types/test_suite_run_external_exec_config_data.py +0 -5
  81. vellum/client/types/test_suite_run_external_exec_config_data_request.py +0 -7
  82. vellum/client/types/test_suite_run_external_exec_config_request.py +0 -7
  83. vellum/client/types/test_suite_run_metric_array_output.py +0 -5
  84. vellum/client/types/test_suite_run_read.py +0 -5
  85. vellum/client/types/test_suite_test_case.py +0 -5
  86. vellum/client/types/test_suite_test_case_create_bulk_operation_request.py +0 -7
  87. vellum/client/types/test_suite_test_case_replace_bulk_operation_request.py +0 -7
  88. vellum/client/types/test_suite_test_case_upsert_bulk_operation_request.py +0 -7
  89. vellum/client/types/upsert_test_suite_test_case_request.py +0 -5
  90. vellum/client/types/vellum_value_logical_condition_group_request.py +0 -3
  91. vellum/client/types/vellum_value_logical_condition_request.py +0 -5
  92. vellum/client/types/vellum_variable.py +0 -5
  93. vellum/client/types/workflow_deployment_event_executions_response.py +0 -26
  94. vellum/client/types/workflow_deployment_history_item.py +0 -5
  95. vellum/client/types/workflow_deployment_parent_context.py +0 -6
  96. vellum/client/types/workflow_deployment_read.py +0 -5
  97. vellum/client/types/workflow_deployment_release.py +0 -5
  98. vellum/client/types/workflow_deployment_release_workflow_version.py +0 -5
  99. vellum/client/types/workflow_event_execution_read.py +0 -12
  100. vellum/client/types/workflow_execution_actual.py +0 -5
  101. vellum/client/types/workflow_execution_fulfilled_event.py +0 -11
  102. vellum/client/types/workflow_execution_initiated_event.py +0 -11
  103. vellum/client/types/workflow_execution_node_result_event.py +0 -5
  104. vellum/client/types/workflow_execution_paused_event.py +0 -11
  105. vellum/client/types/workflow_execution_rejected_event.py +0 -11
  106. vellum/client/types/workflow_execution_resumed_event.py +0 -11
  107. vellum/client/types/workflow_execution_snapshotted_event.py +0 -13
  108. vellum/client/types/workflow_execution_span.py +0 -11
  109. vellum/client/types/workflow_execution_streaming_event.py +0 -11
  110. vellum/client/types/workflow_execution_view_online_eval_metric_result.py +0 -7
  111. vellum/client/types/workflow_execution_workflow_result_event.py +0 -5
  112. vellum/client/types/workflow_output_array.py +0 -5
  113. vellum/client/types/workflow_parent_context.py +0 -6
  114. vellum/client/types/workflow_result_event.py +0 -5
  115. vellum/client/types/workflow_result_event_output_data_array.py +0 -5
  116. vellum/client/types/workflow_sandbox_parent_context.py +0 -6
  117. vellum/workflows/nodes/bases/base.py +26 -6
  118. vellum/workflows/nodes/bases/tests/test_base_node.py +30 -0
  119. vellum/workflows/nodes/core/try_node/node.py +3 -6
  120. vellum/workflows/nodes/core/try_node/tests/test_node.py +0 -24
  121. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +8 -14
  122. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +112 -0
  123. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -54
  124. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +5 -6
  125. vellum/workflows/nodes/utils.py +8 -0
  126. vellum/workflows/types/code_execution_node_wrappers.py +69 -0
  127. vellum/workflows/vellum_client.py +19 -7
  128. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/METADATA +1 -1
  129. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/RECORD +162 -161
  130. vellum_cli/config.py +7 -2
  131. vellum_cli/push.py +5 -1
  132. vellum_cli/tests/test_push.py +192 -8
  133. vellum_ee/workflows/display/nodes/base_node_display.py +17 -6
  134. vellum_ee/workflows/display/nodes/get_node_display_class.py +4 -5
  135. vellum_ee/workflows/display/nodes/vellum/api_node.py +11 -0
  136. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +5 -0
  137. vellum_ee/workflows/display/nodes/vellum/error_node.py +22 -16
  138. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +2 -0
  139. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +46 -12
  140. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +2 -0
  141. vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
  142. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +3 -5
  143. vellum_ee/workflows/display/nodes/vellum/search_node.py +8 -0
  144. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +2 -5
  145. vellum_ee/workflows/display/nodes/vellum/templating_node.py +2 -0
  146. vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +1 -1
  147. vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py +4 -0
  148. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py +4 -3
  149. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +36 -2
  150. vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +5 -4
  151. vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py +1 -1
  152. vellum_ee/workflows/display/tests/test_base_workflow_display.py +44 -0
  153. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +2 -4
  154. vellum_ee/workflows/display/types.py +3 -0
  155. vellum_ee/workflows/display/utils/expressions.py +3 -3
  156. vellum_ee/workflows/display/utils/vellum.py +1 -3
  157. vellum_ee/workflows/display/workflows/base_workflow_display.py +10 -0
  158. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +3 -0
  159. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +53 -0
  160. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/LICENSE +0 -0
  161. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/WHEEL +0 -0
  162. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.47.dist-info}/entry_points.txt +0 -0
@@ -4,7 +4,6 @@ from typing import Generic, Optional, TypeVar, cast
4
4
  from vellum.workflows.nodes.displayable.prompt_deployment_node import PromptDeploymentNode
5
5
  from vellum.workflows.references import OutputReference
6
6
  from vellum.workflows.types.core import JsonObject
7
- from vellum.workflows.vellum_client import create_vellum_client
8
7
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
9
8
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
10
9
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -14,6 +13,8 @@ _PromptDeploymentNodeType = TypeVar("_PromptDeploymentNodeType", bound=PromptDep
14
13
 
15
14
 
16
15
  class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType], Generic[_PromptDeploymentNodeType]):
16
+ __serializable_inputs__ = {PromptDeploymentNode.prompt_inputs}
17
+
17
18
  def serialize(
18
19
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
19
20
  ) -> JsonObject:
@@ -42,10 +43,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
42
43
  _, output_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.text)]
43
44
  _, array_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.results)]
44
45
 
45
- # TODO: Pass through the name instead of retrieving the ID
46
- # https://app.shortcut.com/vellum/story/4702
47
- vellum_client = create_vellum_client()
48
- deployment = vellum_client.deployments.retrieve(
46
+ deployment = display_context.client.deployments.retrieve(
49
47
  id=str(raise_if_descriptor(node.deployment)),
50
48
  )
51
49
  ml_model_fallbacks = raise_if_descriptor(node.ml_model_fallbacks)
@@ -32,6 +32,14 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
32
32
  # A mapping between the id of the operand (e.g. "lhs_variable_id" or "rhs_variable_id") and the id of the node input
33
33
  # that the operand is pointing to.
34
34
  metadata_filter_input_id_by_operand_id: Dict[UUID, UUID] = {}
35
+ __serializable_inputs__ = {
36
+ SearchNode.query,
37
+ SearchNode.document_index,
38
+ SearchNode.weights,
39
+ SearchNode.chunk_separator,
40
+ SearchNode.limit,
41
+ SearchNode.result_merging,
42
+ }
35
43
 
36
44
  def serialize(
37
45
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
@@ -3,7 +3,6 @@ from typing import Generic, Optional, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes import SubworkflowDeploymentNode
5
5
  from vellum.workflows.types.core import JsonObject
6
- from vellum.workflows.vellum_client import create_vellum_client
7
6
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
8
7
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
9
8
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -15,6 +14,7 @@ _SubworkflowDeploymentNodeType = TypeVar("_SubworkflowDeploymentNodeType", bound
15
14
  class BaseSubworkflowDeploymentNodeDisplay(
16
15
  BaseNodeDisplay[_SubworkflowDeploymentNodeType], Generic[_SubworkflowDeploymentNodeType]
17
16
  ):
17
+ __serializable_inputs__ = {SubworkflowDeploymentNode.subworkflow_inputs}
18
18
 
19
19
  def serialize(
20
20
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
@@ -37,10 +37,7 @@ class BaseSubworkflowDeploymentNodeDisplay(
37
37
  for variable_name, variable_value in subworkflow_inputs.items()
38
38
  ]
39
39
 
40
- # TODO: Pass through the name instead of retrieving the ID
41
- # https://app.shortcut.com/vellum/story/4702
42
- vellum_client = create_vellum_client()
43
- deployment = vellum_client.workflow_deployments.retrieve(
40
+ deployment = display_context.client.workflow_deployments.retrieve(
44
41
  id=str(raise_if_descriptor(node.deployment)),
45
42
  )
46
43
 
@@ -15,6 +15,8 @@ TEMPLATE_INPUT_NAME = TemplatingNode.template.name
15
15
 
16
16
 
17
17
  class BaseTemplatingNodeDisplay(BaseNodeDisplay[_TemplatingNodeType], Generic[_TemplatingNodeType]):
18
+ __serializable_inputs__ = {TemplatingNode.inputs}
19
+
18
20
  def serialize(
19
21
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
20
22
  ) -> JsonObject:
@@ -29,7 +29,7 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Cod
29
29
  @pytest.mark.parametrize(
30
30
  ["GetDisplayClass", "expected_input_id"],
31
31
  [
32
- (_no_display_class, "e3cdb222-324e-4ad1-abb2-bdd7881b3a0e"),
32
+ (_no_display_class, "a5dbe403-0b00-4df6-b8f7-ed5f7794b003"),
33
33
  (_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
34
34
  (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
35
35
  ],
@@ -41,3 +41,7 @@ def test_error_node_display__serialize_with_vellum_error() -> None:
41
41
  }
42
42
  ],
43
43
  }
44
+
45
+ # AND we serialize the DEPRECATED fields
46
+ assert "error_output_id" not in serialized_node["data"]
47
+ assert "name" not in serialized_node["data"]
@@ -5,6 +5,7 @@ from typing import Type
5
5
 
6
6
  from vellum.workflows import BaseWorkflow
7
7
  from vellum.workflows.nodes import PromptDeploymentNode
8
+ from vellum_ee.workflows.display.nodes.vellum.prompt_deployment_node import BasePromptDeploymentNodeDisplay
8
9
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
9
10
 
10
11
 
@@ -13,14 +14,14 @@ def _no_display_class(Node: Type[PromptDeploymentNode]): # type: ignore
13
14
 
14
15
 
15
16
  def _display_class_with_node_input_ids_by_name(Node: Type[PromptDeploymentNode]):
16
- class PromptDeploymentNodeDisplay(PromptDeploymentNode[Node]): # type: ignore[valid-type]
17
+ class PromptDeploymentNodeDisplay(BasePromptDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
17
18
  node_input_ids_by_name = {"foo": UUID("6037747a-1d35-4094-b363-4369fc92c5d4")}
18
19
 
19
20
  return PromptDeploymentNodeDisplay
20
21
 
21
22
 
22
23
  def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[PromptDeploymentNode]):
23
- class PromptDeploymentNodeDisplay(PromptDeploymentNode[Node]): # type: ignore[valid-type]
24
+ class PromptDeploymentNodeDisplay(BasePromptDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
24
25
  node_input_ids_by_name = {"prompt_inputs.foo": UUID("6037747a-1d35-4094-b363-4369fc92c5d4")}
25
26
 
26
27
  return PromptDeploymentNodeDisplay
@@ -51,7 +52,7 @@ def mock_fetch_deployment(mocker):
51
52
  @pytest.mark.parametrize(
52
53
  ["GetDisplayClass", "expected_input_id"],
53
54
  [
54
- (_no_display_class, "6037747a-1d35-4094-b363-4369fc92c5d4"),
55
+ (_no_display_class, "016187d6-2830-4256-a61d-e52f9bf6355e"),
55
56
  (_display_class_with_node_input_ids_by_name, "6037747a-1d35-4094-b363-4369fc92c5d4"),
56
57
  (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "6037747a-1d35-4094-b363-4369fc92c5d4"),
57
58
  ],
@@ -2,6 +2,7 @@ import pytest
2
2
  from uuid import UUID
3
3
  from typing import Type
4
4
 
5
+ from vellum.client.types.variable_prompt_block import VariablePromptBlock
5
6
  from vellum.workflows import BaseWorkflow
6
7
  from vellum.workflows.nodes import BaseNode
7
8
  from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
@@ -78,7 +79,7 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Inl
78
79
  @pytest.mark.parametrize(
79
80
  ["GetDisplayClass", "expected_input_id"],
80
81
  [
81
- (_no_display_class, "8aa4ce7f-5eb8-41b7-abd0-ea2b40c8fb88"),
82
+ (_no_display_class, "9b036991-67ff-4cd0-a4d7-b4ed581e8b6d"),
82
83
  (_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
83
84
  (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
84
85
  ],
@@ -165,7 +166,7 @@ def test_serialize_node__prompt_inputs__state_reference():
165
166
  },
166
167
  },
167
168
  {
168
- "id": "3750feb9-5d5c-4150-b62d-a9924f466888",
169
+ "id": "b83c40f7-0159-442f-af03-e80870363c52",
169
170
  "key": "bar",
170
171
  "value": {
171
172
  "rules": [
@@ -181,3 +182,36 @@ def test_serialize_node__prompt_inputs__state_reference():
181
182
  },
182
183
  },
183
184
  ]
185
+
186
+
187
+ def test_serialize_node__unreferenced_variable_block__still_serializes():
188
+ # GIVEN a prompt node with an unreferenced variable block
189
+ class MyPromptNode(InlinePromptNode):
190
+ blocks = [VariablePromptBlock(input_variable="foo")]
191
+
192
+ # AND a workflow with the prompt node
193
+ class MyWorkflow(BaseWorkflow):
194
+ graph = MyPromptNode
195
+
196
+ # WHEN the prompt node is serialized
197
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
198
+ serialized_workflow: dict = workflow_display.serialize()
199
+
200
+ # THEN the node should skip the state reference input rule
201
+ assert serialized_workflow["workflow_raw_data"]["nodes"][1]["data"]["exec_config"]["prompt_template_block_data"][
202
+ "blocks"
203
+ ] == [
204
+ {
205
+ "id": "fecbb5f3-e0a3-42ed-9774-6c68fd5db50c",
206
+ "block_type": "VARIABLE",
207
+ "input_variable_id": "ea3f6348-8553-4375-bd27-527df4e4f3c2",
208
+ "state": "ENABLED",
209
+ "cache_config": None,
210
+ }
211
+ ]
212
+
213
+ # AND we should have a warning of the invalid reference
214
+ # TODO: Come up with a proposal for how nodes should propagate warnings
215
+ # warnings = list(workflow_display.errors)
216
+ # assert len(warnings) == 1
217
+ # assert "Missing input variable 'foo' for prompt block 0" in str(warnings[0])
@@ -4,7 +4,8 @@ from uuid import UUID, uuid4
4
4
  from typing import Type
5
5
 
6
6
  from vellum.workflows import BaseWorkflow
7
- from vellum.workflows.nodes import SubworkflowDeploymentNode
7
+ from vellum.workflows.nodes.displayable.subworkflow_deployment_node.node import SubworkflowDeploymentNode
8
+ from vellum_ee.workflows.display.nodes.vellum.subworkflow_deployment_node import BaseSubworkflowDeploymentNodeDisplay
8
9
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
9
10
 
10
11
 
@@ -13,14 +14,14 @@ def _no_display_class(Node: Type[SubworkflowDeploymentNode]): # type: ignore
13
14
 
14
15
 
15
16
  def _display_class_with_node_input_ids_by_name(Node: Type[SubworkflowDeploymentNode]):
16
- class SubworkflowNodeDisplay(SubworkflowDeploymentNode[Node]): # type: ignore[valid-type]
17
+ class SubworkflowNodeDisplay(BaseSubworkflowDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
17
18
  node_input_ids_by_name = {"foo": UUID("aff4f838-577e-44b9-ac5c-6d8213abbb9c")}
18
19
 
19
20
  return SubworkflowNodeDisplay
20
21
 
21
22
 
22
23
  def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[SubworkflowDeploymentNode]):
23
- class SubworkflowNodeDisplay(SubworkflowDeploymentNode[Node]): # type: ignore[valid-type]
24
+ class SubworkflowNodeDisplay(BaseSubworkflowDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
24
25
  node_input_ids_by_name = {"subworkflow_inputs.foo": UUID("aff4f838-577e-44b9-ac5c-6d8213abbb9c")}
25
26
 
26
27
  return SubworkflowNodeDisplay
@@ -55,7 +56,7 @@ def mock_fetch_deployment(mocker):
55
56
  @pytest.mark.parametrize(
56
57
  ["GetDisplayClass", "expected_input_id"],
57
58
  [
58
- (_no_display_class, "aff4f838-577e-44b9-ac5c-6d8213abbb9c"),
59
+ (_no_display_class, "394132c2-1817-455e-9f3f-b7073eb63a2b"),
59
60
  (_display_class_with_node_input_ids_by_name, "aff4f838-577e-44b9-ac5c-6d8213abbb9c"),
60
61
  (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "aff4f838-577e-44b9-ac5c-6d8213abbb9c"),
61
62
  ],
@@ -29,7 +29,7 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Tem
29
29
  @pytest.mark.parametrize(
30
30
  ["GetDisplayClass", "expected_input_id"],
31
31
  [
32
- (_no_display_class, "d3519cec-590c-416d-8eb1-96051aed5ddd"),
32
+ (_no_display_class, "91d982a9-6c41-42ac-aff9-7b623c450a55"),
33
33
  (_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
34
34
  (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
35
35
  ],
@@ -3,6 +3,8 @@ from typing import Dict
3
3
 
4
4
  from vellum.workflows.inputs import BaseInputs
5
5
  from vellum.workflows.nodes import BaseNode
6
+ from vellum.workflows.ports.port import Port
7
+ from vellum.workflows.references.lazy import LazyReference
6
8
  from vellum.workflows.state import BaseState
7
9
  from vellum.workflows.workflows.base import BaseWorkflow
8
10
  from vellum_ee.workflows.display.vellum import WorkflowInputsVellumDisplayOverrides
@@ -285,3 +287,45 @@ def test_vellum_workflow_display__serialize_with_parse_json_expression():
285
287
  "operator": "parseJson",
286
288
  },
287
289
  }
290
+
291
+
292
+ def test_serialize__port_with_lazy_reference():
293
+ # GIVEN a node with a lazy reference in a Port
294
+ class MyNode(BaseNode):
295
+ class Ports(BaseNode.Ports):
296
+ foo = Port.on_if(LazyReference(lambda: MyNode.Outputs.bar))
297
+
298
+ class Outputs(BaseNode.Outputs):
299
+ bar: bool
300
+
301
+ # AND a workflow that uses the node
302
+ class Workflow(BaseWorkflow):
303
+ graph = MyNode
304
+
305
+ # WHEN we serialize the workflow
306
+ workflow_display = get_workflow_display(workflow_class=Workflow)
307
+ exec_config = workflow_display.serialize()
308
+
309
+ # THEN the lazy reference should be serialized correctly
310
+ raw_data = exec_config["workflow_raw_data"]
311
+ assert isinstance(raw_data, dict)
312
+
313
+ nodes = raw_data["nodes"]
314
+ assert isinstance(nodes, list)
315
+
316
+ my_node = nodes[1]
317
+ assert isinstance(my_node, dict)
318
+ ports = my_node.get("ports")
319
+ assert isinstance(ports, list)
320
+ assert ports == [
321
+ {
322
+ "id": "6c26bc2b-6469-47c1-b858-d63f0d311ea6",
323
+ "name": "foo",
324
+ "type": "IF",
325
+ "expression": {
326
+ "type": "NODE_OUTPUT",
327
+ "node_id": str(MyNode.__id__),
328
+ "node_output_id": str(MyNode.__output_ids__["bar"]),
329
+ },
330
+ }
331
+ ]
@@ -87,7 +87,7 @@ def test_serialize_workflow():
87
87
  "type": "ERROR",
88
88
  "inputs": [
89
89
  {
90
- "id": "690d825f-6ffd-493e-8141-c86d384e6150",
90
+ "id": "8e4c8d76-2e02-4d7e-a7bf-d71af392dc49",
91
91
  "key": "error_source_input_id",
92
92
  "value": {
93
93
  "rules": [
@@ -101,11 +101,9 @@ def test_serialize_workflow():
101
101
  }
102
102
  ],
103
103
  "data": {
104
- "name": "error-node",
105
104
  "label": "Fail Node",
106
105
  "target_handle_id": "70c19f1c-309c-4a5d-ba65-664c0bb2fedf",
107
- "error_source_input_id": "None",
108
- "error_output_id": "None",
106
+ "error_source_input_id": "8e4c8d76-2e02-4d7e-a7bf-d71af392dc49",
109
107
  },
110
108
  "display_data": {"position": {"x": 0.0, "y": 0.0}},
111
109
  "base": {
@@ -1,11 +1,13 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import TYPE_CHECKING, Dict, Tuple, Type
3
3
 
4
+ from vellum.client import Vellum as VellumClient
4
5
  from vellum.workflows.descriptors.base import BaseDescriptor
5
6
  from vellum.workflows.events.workflow import WorkflowEventDisplayContext # noqa: F401
6
7
  from vellum.workflows.nodes import BaseNode
7
8
  from vellum.workflows.ports import Port
8
9
  from vellum.workflows.references import OutputReference, StateValueReference, WorkflowInputReference
10
+ from vellum.workflows.vellum_client import create_vellum_client
9
11
  from vellum.workflows.workflows.base import BaseWorkflow
10
12
  from vellum_ee.workflows.display.base import (
11
13
  EdgeDisplay,
@@ -35,6 +37,7 @@ PortDisplays = Dict[Port, PortDisplay]
35
37
 
36
38
  @dataclass
37
39
  class WorkflowDisplayContext:
40
+ client: VellumClient = field(default_factory=create_vellum_client)
38
41
  workflow_display_class: Type["BaseWorkflowDisplay"] = field(default_factory=get_default_workflow_display_class)
39
42
  workflow_display: WorkflowMetaDisplay = field(default_factory=lambda: WorkflowMetaDisplay.get_default(BaseWorkflow))
40
43
  workflow_input_displays: WorkflowInputsDisplays = field(default_factory=dict)
@@ -107,7 +107,7 @@ def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplay
107
107
  )
108
108
 
109
109
  node_class_name = ".".join(reference_parts[:-2])
110
- for node in display_context.node_displays.keys():
110
+ for node in display_context.global_node_displays.keys():
111
111
  if node.__name__ == node_class_name:
112
112
  return getattr(node.Outputs, output_name)
113
113
 
@@ -116,7 +116,7 @@ def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplay
116
116
  return value._get()
117
117
 
118
118
 
119
- def serialize_condition(display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
119
+ def _serialize_condition(display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
120
120
  if isinstance(
121
121
  condition,
122
122
  (
@@ -247,4 +247,4 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
247
247
 
248
248
  # If it's not any of the references we know about,
249
249
  # then try to serialize it as a nested value
250
- return serialize_condition(display_context, value)
250
+ return _serialize_condition(display_context, value)
@@ -13,7 +13,6 @@ from vellum.workflows.references.lazy import LazyReference
13
13
  from vellum.workflows.references.node import NodeReference
14
14
  from vellum.workflows.references.vellum_secret import VellumSecretReference
15
15
  from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
16
- from vellum.workflows.vellum_client import create_vellum_client
17
16
  from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
18
17
  from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
19
18
 
@@ -118,8 +117,7 @@ def create_node_input_value_pointer_rule(
118
117
  workflow_input_display = display_context.global_workflow_input_displays[value]
119
118
  return InputVariablePointer(data=InputVariableData(input_variable_id=str(workflow_input_display.id)))
120
119
  if isinstance(value, VellumSecretReference):
121
- vellum_client = create_vellum_client()
122
- workspace_secret = vellum_client.workspace_secrets.retrieve(
120
+ workspace_secret = display_context.client.workspace_secrets.retrieve(
123
121
  id=value.name,
124
122
  )
125
123
  return WorkspaceSecretPointer(
@@ -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,
@@ -84,9 +86,16 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
84
86
  self,
85
87
  *,
86
88
  parent_display_context: Optional[WorkflowDisplayContext] = None,
89
+ client: Optional[VellumClient] = None,
87
90
  dry_run: bool = False,
88
91
  ):
89
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
+ )
90
99
  self._errors: List[Exception] = []
91
100
  self._dry_run = dry_run
92
101
 
@@ -494,6 +503,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
494
503
  )
495
504
 
496
505
  return WorkflowDisplayContext(
506
+ client=self._client,
497
507
  workflow_display=workflow_meta_display,
498
508
  workflow_input_displays=workflow_input_displays,
499
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
  )
@@ -7,6 +7,7 @@ from vellum.workflows.nodes.core.retry_node.node import RetryNode
7
7
  from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
8
8
  from vellum.workflows.nodes.core.try_node.node import TryNode
9
9
  from vellum.workflows.nodes.displayable.final_output_node.node import FinalOutputNode
10
+ from vellum.workflows.references.lazy import LazyReference
10
11
  from vellum.workflows.workflows.base import BaseWorkflow
11
12
  from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
12
13
  from vellum_ee.workflows.display.nodes import BaseNodeDisplay
@@ -372,3 +373,55 @@ def test_serialize_workflow__terminal_node_mismatches_workflow_output_name():
372
373
  "node_id": str(ExitNode.__id__),
373
374
  "node_output_id": str(ExitNode.__output_ids__["value"]),
374
375
  }
376
+
377
+
378
+ def test_serialize_workflow__nested_lazy_reference():
379
+ # GIVEN an inner node that references the output of an outer node
380
+ class InnerNode(BaseNode):
381
+ foo = LazyReference[str]("OuterNode.Outputs.bar")
382
+
383
+ class Outputs(BaseNode.Outputs):
384
+ foo = "foo"
385
+
386
+ # AND a workflow that uses the inner node
387
+ class InnerWorkflow(BaseWorkflow):
388
+ graph = InnerNode
389
+
390
+ class Outputs(BaseWorkflow.Outputs):
391
+ foo = InnerNode.Outputs.foo
392
+
393
+ # AND a subworkflow that uses the inner workflow
394
+ class SubworkflowNode(InlineSubworkflowNode):
395
+ subworkflow = InnerWorkflow
396
+
397
+ # AND the outer node
398
+ class OuterNode(BaseNode):
399
+ class Outputs(BaseNode.Outputs):
400
+ bar: str
401
+
402
+ # AND a workflow that uses the subworkflow node and the outer node
403
+ class MyWorkflow(BaseWorkflow):
404
+ graph = SubworkflowNode >> OuterNode
405
+
406
+ class Outputs(BaseWorkflow.Outputs):
407
+ answer = SubworkflowNode.Outputs.foo
408
+
409
+ # WHEN we serialize it
410
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
411
+ data: dict = workflow_display.serialize()
412
+
413
+ # THEN it should have properly serialized the lazy reference
414
+ subworkflow_node = next(
415
+ node for node in data["workflow_raw_data"]["nodes"] if isinstance(node, dict) and node["type"] == "SUBWORKFLOW"
416
+ )
417
+ inner_node = next(
418
+ node
419
+ for node in subworkflow_node["data"]["workflow_raw_data"]["nodes"]
420
+ if isinstance(node, dict) and node["type"] == "GENERIC"
421
+ )
422
+
423
+ assert inner_node["attributes"][0]["value"] == {
424
+ "type": "NODE_OUTPUT",
425
+ "node_id": str(OuterNode.__id__),
426
+ "node_output_id": str(OuterNode.__output_ids__["bar"]),
427
+ }