vellum-ai 0.14.72__py3-none-any.whl → 0.14.74__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 (74) hide show
  1. vellum/__init__.py +8 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +8 -0
  4. vellum/client/types/build_status_enum.py +5 -0
  5. vellum/client/types/container_image_build_config.py +20 -0
  6. vellum/client/types/container_image_read.py +4 -0
  7. vellum/client/types/execute_api_response.py +2 -2
  8. vellum/client/types/folder_entity.py +2 -0
  9. vellum/client/types/folder_entity_dataset.py +26 -0
  10. vellum/client/types/folder_entity_dataset_data.py +25 -0
  11. vellum/client/types/secret_type_enum.py +3 -1
  12. vellum/types/build_status_enum.py +3 -0
  13. vellum/types/container_image_build_config.py +3 -0
  14. vellum/types/folder_entity_dataset.py +3 -0
  15. vellum/types/folder_entity_dataset_data.py +3 -0
  16. vellum/workflows/nodes/core/retry_node/tests/test_node.py +1 -1
  17. vellum/workflows/nodes/displayable/api_node/node.py +2 -0
  18. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +43 -0
  19. vellum/workflows/nodes/displayable/bases/api_node/node.py +6 -0
  20. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +30 -4
  21. vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +43 -3
  22. vellum/workflows/nodes/displayable/bases/utils.py +2 -0
  23. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +68 -58
  24. vellum/workflows/nodes/experimental/tool_calling_node/node.py +15 -11
  25. vellum/workflows/nodes/experimental/tool_calling_node/tests/__init__.py +0 -0
  26. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +13 -0
  27. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_utils.py +49 -0
  28. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +78 -7
  29. vellum/workflows/ports/utils.py +26 -6
  30. vellum/workflows/runner/runner.py +35 -3
  31. vellum/workflows/state/encoder.py +2 -0
  32. vellum/workflows/types/core.py +12 -0
  33. vellum/workflows/types/definition.py +6 -0
  34. vellum/workflows/utils/functions.py +12 -12
  35. vellum/workflows/utils/pydantic_schema.py +38 -0
  36. vellum/workflows/utils/tests/test_functions.py +18 -18
  37. {vellum_ai-0.14.72.dist-info → vellum_ai-0.14.74.dist-info}/METADATA +1 -1
  38. {vellum_ai-0.14.72.dist-info → vellum_ai-0.14.74.dist-info}/RECORD +74 -61
  39. vellum_cli/push.py +6 -8
  40. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +36 -7
  41. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +8 -1
  42. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +102 -0
  43. vellum_ee/workflows/display/tests/test_base_workflow_display.py +1 -1
  44. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +1 -1
  45. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +91 -1
  46. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +5 -5
  47. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +12 -12
  48. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +10 -10
  49. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +3 -3
  50. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +3 -3
  51. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +20 -9
  52. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +3 -3
  53. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +120 -3
  54. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +8 -8
  55. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +6 -6
  56. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -3
  57. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +8 -8
  58. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +3 -3
  59. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +4 -4
  60. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +3 -3
  61. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +2 -2
  62. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +12 -5
  63. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +8 -1
  64. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +62 -0
  65. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +1 -1
  66. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +2 -2
  67. vellum_ee/workflows/display/utils/auto_layout.py +1 -1
  68. vellum_ee/workflows/display/utils/expressions.py +33 -2
  69. vellum_ee/workflows/display/workflows/base_workflow_display.py +199 -10
  70. vellum_ee/workflows/tests/test_display_meta.py +41 -0
  71. vellum_ee/workflows/tests/test_serialize_module.py +47 -0
  72. {vellum_ai-0.14.72.dist-info → vellum_ai-0.14.74.dist-info}/LICENSE +0 -0
  73. {vellum_ai-0.14.72.dist-info → vellum_ai-0.14.74.dist-info}/WHEEL +0 -0
  74. {vellum_ai-0.14.72.dist-info → vellum_ai-0.14.74.dist-info}/entry_points.txt +0 -0
@@ -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.prompt_parameters import PromptParameters
5
6
  from vellum.client.types.variable_prompt_block import VariablePromptBlock
6
7
  from vellum.workflows import BaseWorkflow
7
8
  from vellum.workflows.inputs import BaseInputs
@@ -280,3 +281,104 @@ def test_serialize_node__port_groups():
280
281
 
281
282
  # AND the legacy source_handle_id should be the default port
282
283
  assert my_prompt_node["data"]["source_handle_id"] == "149d97a4-3da3-44a9-95f7-ea7b8d38b877"
284
+
285
+
286
+ def test_serialize_node__prompt_parameters__dynamic_references():
287
+ # GIVEN input definition
288
+ class MyInputs(BaseInputs):
289
+ input_value: str
290
+
291
+ # AND a prompt node with PromptParameters containing dynamic references
292
+ class DynamicPromptNode(InlinePromptNode):
293
+ blocks = []
294
+ ml_model = "gpt-4o"
295
+ parameters = PromptParameters(custom_parameters={"json_schema": MyInputs.input_value})
296
+
297
+ # AND a workflow with the prompt node
298
+ class Workflow(BaseWorkflow[MyInputs, BaseState]):
299
+ graph = DynamicPromptNode
300
+
301
+ # WHEN the workflow is serialized
302
+ workflow_display = get_workflow_display(workflow_class=Workflow)
303
+ serialized_workflow: dict = workflow_display.serialize()
304
+
305
+ # THEN the node should properly serialize the PromptParameters
306
+ dynamic_prompt_node = next(
307
+ node
308
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
309
+ if node["id"] == str(DynamicPromptNode.__id__)
310
+ )
311
+
312
+ # AND the parameters should be properly serialized in exec_config
313
+ exec_config = dynamic_prompt_node["data"]["exec_config"]
314
+ assert "parameters" in exec_config
315
+
316
+ parameters = exec_config["parameters"]
317
+ assert parameters == {}
318
+
319
+ # AND the parameters should also be serialized in the attributes array
320
+ parameters_attribute = next(
321
+ (attr for attr in dynamic_prompt_node.get("attributes", []) if attr["name"] == "parameters"), None
322
+ )
323
+ assert parameters_attribute is not None
324
+ assert parameters_attribute["name"] == "parameters"
325
+ assert parameters_attribute["value"]["type"] == "DICTIONARY_REFERENCE"
326
+ assert parameters_attribute["value"]["entries"] == [
327
+ {
328
+ "id": "6b63ff96-a2eb-4c6e-bad1-bde01605fa86",
329
+ "key": "stop",
330
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
331
+ },
332
+ {
333
+ "id": "265a1c17-2089-4ac1-b2ce-361b6b9a3335",
334
+ "key": "temperature",
335
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
336
+ },
337
+ {
338
+ "id": "699976ec-8ec2-476a-a011-7cf810a8a307",
339
+ "key": "max_tokens",
340
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
341
+ },
342
+ {
343
+ "id": "a87e23da-9794-41ff-ba80-c3a77e976e75",
344
+ "key": "top_p",
345
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
346
+ },
347
+ {
348
+ "id": "18eb53c2-ec1a-4115-9f21-083af430df67",
349
+ "key": "top_k",
350
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
351
+ },
352
+ {
353
+ "id": "295509a2-5837-452c-893d-f47b67c63c8a",
354
+ "key": "frequency_penalty",
355
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
356
+ },
357
+ {
358
+ "id": "5fc64379-5566-426a-a909-dd56c3305aa5",
359
+ "key": "presence_penalty",
360
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
361
+ },
362
+ {
363
+ "id": "5d326da0-c096-4425-8bf1-3a18764e96e3",
364
+ "key": "logit_bias",
365
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
366
+ },
367
+ {
368
+ "id": "cd1a0e1b-6667-48a0-9964-257e1ec8851d",
369
+ "key": "custom_parameters",
370
+ "value": {
371
+ "entries": [
372
+ {
373
+ "id": "a9a3092e-dd18-4533-b6b5-24588ebd8f7f",
374
+ "key": "json_schema",
375
+ "value": {
376
+ "input_variable_id": "c02d1201-86d1-4364-b3b3-4fc6824db8a4",
377
+ "type": "WORKFLOW_INPUT",
378
+ },
379
+ }
380
+ ],
381
+ "type": "DICTIONARY_REFERENCE",
382
+ },
383
+ },
384
+ ]
@@ -39,7 +39,7 @@ def test_base_workflow_display__serialize_empty_workflow():
39
39
  "data": {"label": "Entrypoint Node", "source_handle_id": "0af025a4-3b25-457d-a7ae-e3a7ba15c86c"},
40
40
  "base": None,
41
41
  "definition": None,
42
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
42
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
43
43
  "id": "3c41cdd9-999a-48b8-9088-f6dfa1369bfd",
44
44
  "inputs": [],
45
45
  "type": "ENTRYPOINT",
@@ -249,7 +249,7 @@ def test_serialize_node__stacked():
249
249
  "id": "074833b0-e142-4bbc-8dec-209a35e178a3",
250
250
  "label": "test_serialize_node__stacked.<locals>.InnerStackedGenericNode",
251
251
  "type": "GENERIC",
252
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
252
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
253
253
  "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
254
254
  "definition": {
255
255
  "name": "InnerStackedGenericNode",
@@ -1,7 +1,10 @@
1
+ import pytest
2
+ from dataclasses import dataclass
1
3
  from uuid import uuid4
2
4
  from typing import List
3
5
 
4
6
  from deepdiff import DeepDiff
7
+ from pydantic import BaseModel
5
8
 
6
9
  from vellum.client.types.chat_message import ChatMessage
7
10
  from vellum.workflows.inputs.base import BaseInputs
@@ -70,6 +73,28 @@ def test_serialize_node__constant_value(serialize_node):
70
73
  )
71
74
 
72
75
 
76
+ @pytest.mark.parametrize(
77
+ "boolean_value, expected_value",
78
+ [
79
+ (True, True),
80
+ (False, False),
81
+ ],
82
+ )
83
+ def test_serialize_node__constant_boolean_value(serialize_node, boolean_value, expected_value):
84
+ class BooleanValueGenericNode(BaseNode):
85
+ attr: bool = boolean_value
86
+
87
+ serialized_node = serialize_node(BooleanValueGenericNode)
88
+
89
+ assert serialized_node["attributes"][0]["value"] == {
90
+ "type": "CONSTANT_VALUE",
91
+ "value": {
92
+ "type": "JSON",
93
+ "value": expected_value,
94
+ },
95
+ }
96
+
97
+
73
98
  def test_serialize_node__constant_value_reference(serialize_node):
74
99
  class ConstantValueReferenceGenericNode(BaseNode):
75
100
  attr: str = ConstantValueReference("hello")
@@ -269,7 +294,7 @@ def test_serialize_node__workflow_input_as_nested_chat_history():
269
294
  "id": "11be9d37-0069-4695-a317-14a3b6519d4e",
270
295
  "label": "test_serialize_node__workflow_input_as_nested_chat_history.<locals>.GenericNode",
271
296
  "type": "GENERIC",
272
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
297
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
273
298
  "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
274
299
  "definition": {
275
300
  "name": "GenericNode",
@@ -580,3 +605,68 @@ def test_serialize_node__coalesce(serialize_node):
580
605
  serialized_node,
581
606
  ignore_order=True,
582
607
  )
608
+
609
+
610
+ def test_serialize_node__dataclass_with_node_output_reference(serialize_node):
611
+ @dataclass
612
+ class MyDataClass:
613
+ name: str
614
+ node_ref: str
615
+
616
+ class NodeWithOutput(BaseNode):
617
+ class Outputs(BaseNode.Outputs):
618
+ result: str
619
+
620
+ class NodeWithOutputDisplay(BaseNodeDisplay[NodeWithOutput]):
621
+ pass
622
+
623
+ class GenericNodeWithDataclass(BaseNode):
624
+ attr = MyDataClass(name="test", node_ref=NodeWithOutput.Outputs.result)
625
+
626
+ node_output_id = uuid4()
627
+ serialized_node = serialize_node(
628
+ node_class=GenericNodeWithDataclass,
629
+ global_node_displays={NodeWithOutput: NodeWithOutputDisplay()},
630
+ global_node_output_displays={
631
+ NodeWithOutput.Outputs.result: (NodeWithOutput, NodeOutputDisplay(id=node_output_id, name="result"))
632
+ },
633
+ )
634
+
635
+ attr_value = serialized_node["attributes"][0]["value"]
636
+ assert attr_value["type"] == "DICTIONARY_REFERENCE"
637
+
638
+ assert any(
639
+ entry["key"] == "node_ref" and entry["value"]["type"] == "NODE_OUTPUT" for entry in attr_value["entries"]
640
+ )
641
+
642
+
643
+ def test_serialize_node__pydantic_with_node_output_reference(serialize_node):
644
+ class MyPydanticModel(BaseModel):
645
+ name: str
646
+ node_ref: str
647
+
648
+ class NodeWithOutput(BaseNode):
649
+ class Outputs(BaseNode.Outputs):
650
+ result: str
651
+
652
+ class NodeWithOutputDisplay(BaseNodeDisplay[NodeWithOutput]):
653
+ pass
654
+
655
+ class GenericNodeWithPydantic(BaseNode):
656
+ attr = MyPydanticModel(name="test", node_ref=NodeWithOutput.Outputs.result)
657
+
658
+ node_output_id = uuid4()
659
+ serialized_node = serialize_node(
660
+ node_class=GenericNodeWithPydantic,
661
+ global_node_displays={NodeWithOutput: NodeWithOutputDisplay()},
662
+ global_node_output_displays={
663
+ NodeWithOutput.Outputs.result: (NodeWithOutput, NodeOutputDisplay(id=node_output_id, name="result"))
664
+ },
665
+ )
666
+
667
+ attr_value = serialized_node["attributes"][0]["value"]
668
+ assert attr_value["type"] == "DICTIONARY_REFERENCE"
669
+
670
+ assert any(
671
+ entry["key"] == "node_ref" and entry["value"]["type"] == "NODE_OUTPUT" for entry in attr_value["entries"]
672
+ )
@@ -64,7 +64,7 @@ def test_serialize_workflow(vellum_client):
64
64
  "type": "ENTRYPOINT",
65
65
  "inputs": [],
66
66
  "data": {"label": "Entrypoint Node", "source_handle_id": "8eaa7f02-25ff-4a00-9b0a-5185718d89b3"},
67
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
67
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
68
68
  "base": None,
69
69
  "definition": None,
70
70
  }
@@ -184,7 +184,7 @@ def test_serialize_workflow(vellum_client):
184
184
  "json_output_id": "12e4a99d-883d-4da5-aa51-35817d94013e",
185
185
  "status_code_output_id": "fecc16c3-400e-4fd3-8223-08366070e3b1",
186
186
  },
187
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
187
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
188
188
  "base": {
189
189
  "name": "APINode",
190
190
  "module": ["vellum", "workflows", "nodes", "displayable", "api_node", "node"],
@@ -230,7 +230,7 @@ def test_serialize_workflow(vellum_client):
230
230
  },
231
231
  }
232
232
  ],
233
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
233
+ "display_data": {"position": {"x": 400.0, "y": 200.0}},
234
234
  "base": {
235
235
  "name": "FinalOutputNode",
236
236
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -266,7 +266,7 @@ def test_serialize_workflow(vellum_client):
266
266
  },
267
267
  }
268
268
  ],
269
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
269
+ "display_data": {"position": {"x": 400.0, "y": -50.0}},
270
270
  "base": {
271
271
  "name": "FinalOutputNode",
272
272
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -302,7 +302,7 @@ def test_serialize_workflow(vellum_client):
302
302
  },
303
303
  }
304
304
  ],
305
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
305
+ "display_data": {"position": {"x": 400.0, "y": -300.0}},
306
306
  "base": {
307
307
  "name": "FinalOutputNode",
308
308
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -50,7 +50,7 @@ def test_serialize_workflow_with_filepath():
50
50
  "type": "ENTRYPOINT",
51
51
  "inputs": [],
52
52
  "data": {"label": "Entrypoint Node", "source_handle_id": "118e4298-aa79-467c-b8b4-2df540905e86"},
53
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
53
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
54
54
  "base": None,
55
55
  "definition": None,
56
56
  }
@@ -94,7 +94,7 @@ def test_serialize_workflow_with_filepath():
94
94
  "output_id": "0fde9607-353f-42c2-85c4-20f720ebc1ec",
95
95
  "log_output_id": "7cac05e3-b7c3-475e-8df8-422b496c3398",
96
96
  },
97
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
97
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
98
98
  "base": {
99
99
  "module": [
100
100
  "vellum",
@@ -143,7 +143,7 @@ def test_serialize_workflow_with_filepath():
143
143
  },
144
144
  }
145
145
  ],
146
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
146
+ "display_data": {"position": {"x": 400.0, "y": -175.0}},
147
147
  "base": {
148
148
  "name": "FinalOutputNode",
149
149
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -179,7 +179,7 @@ def test_serialize_workflow_with_filepath():
179
179
  },
180
180
  }
181
181
  ],
182
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
182
+ "display_data": {"position": {"x": 400.0, "y": 75.0}},
183
183
  "base": {
184
184
  "name": "FinalOutputNode",
185
185
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -289,7 +289,7 @@ def test_serialize_workflow_with_code():
289
289
  "type": "ENTRYPOINT",
290
290
  "inputs": [],
291
291
  "data": {"label": "Entrypoint Node", "source_handle_id": "e82390bb-c68c-48c1-9f87-7fbfff494c45"},
292
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
292
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
293
293
  "base": None,
294
294
  "definition": None,
295
295
  }
@@ -333,7 +333,7 @@ def test_serialize_workflow_with_code():
333
333
  "output_id": "0fde9607-353f-42c2-85c4-20f720ebc1ec",
334
334
  "log_output_id": "7cac05e3-b7c3-475e-8df8-422b496c3398",
335
335
  },
336
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
336
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
337
337
  "base": {
338
338
  "name": "CodeExecutionNode",
339
339
  "module": ["vellum", "workflows", "nodes", "displayable", "code_execution_node", "node"],
@@ -375,7 +375,7 @@ def test_serialize_workflow_with_code():
375
375
  },
376
376
  }
377
377
  ],
378
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
378
+ "display_data": {"position": {"x": 400.0, "y": -175.0}},
379
379
  "base": {
380
380
  "name": "FinalOutputNode",
381
381
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -411,7 +411,7 @@ def test_serialize_workflow_with_code():
411
411
  },
412
412
  }
413
413
  ],
414
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
414
+ "display_data": {"position": {"x": 400.0, "y": 75.0}},
415
415
  "base": {
416
416
  "name": "FinalOutputNode",
417
417
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -522,7 +522,7 @@ def test_serialize_workflow__try_wrapped():
522
522
  "base": None,
523
523
  "definition": None,
524
524
  "display_data": {
525
- "position": {"x": 0.0, "y": 0.0},
525
+ "position": {"x": 0.0, "y": -50.0},
526
526
  },
527
527
  }
528
528
 
@@ -565,7 +565,7 @@ def test_serialize_workflow__try_wrapped():
565
565
  "output_id": "0fde9607-353f-42c2-85c4-20f720ebc1ec",
566
566
  "log_output_id": "7cac05e3-b7c3-475e-8df8-422b496c3398",
567
567
  },
568
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
568
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
569
569
  "base": {
570
570
  "module": [
571
571
  "vellum",
@@ -637,7 +637,7 @@ def test_serialize_workflow__try_wrapped():
637
637
  },
638
638
  }
639
639
  ],
640
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
640
+ "display_data": {"position": {"x": 400.0, "y": 75.0}},
641
641
  "base": {
642
642
  "name": "FinalOutputNode",
643
643
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -673,7 +673,7 @@ def test_serialize_workflow__try_wrapped():
673
673
  },
674
674
  }
675
675
  ],
676
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
676
+ "display_data": {"position": {"x": 400.0, "y": -175.0}},
677
677
  "base": {
678
678
  "name": "FinalOutputNode",
679
679
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -111,7 +111,7 @@ def test_serialize_workflow():
111
111
  "source_handle_id": "c2f0871d-0d9d-417f-8b0e-c813ccf880ac",
112
112
  },
113
113
  "display_data": {
114
- "position": {"x": 0.0, "y": 0.0},
114
+ "position": {"x": 0.0, "y": -50.0},
115
115
  },
116
116
  }
117
117
 
@@ -415,7 +415,7 @@ def test_serialize_workflow():
415
415
  ],
416
416
  "version": "2",
417
417
  },
418
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
418
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
419
419
  "base": {
420
420
  "name": "ConditionalNode",
421
421
  "module": ["vellum", "workflows", "nodes", "displayable", "conditional_node", "node"],
@@ -544,7 +544,7 @@ def test_serialize_workflow():
544
544
  },
545
545
  }
546
546
  ],
547
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
547
+ "display_data": {"position": {"x": 600.0, "y": -50.0}},
548
548
  "base": {
549
549
  "name": "FinalOutputNode",
550
550
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -580,7 +580,7 @@ def test_serialize_workflow():
580
580
  },
581
581
  }
582
582
  ],
583
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
583
+ "display_data": {"position": {"x": 600.0, "y": -550.0}},
584
584
  "base": {
585
585
  "name": "FinalOutputNode",
586
586
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -616,7 +616,7 @@ def test_serialize_workflow():
616
616
  },
617
617
  }
618
618
  ],
619
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
619
+ "display_data": {"position": {"x": 600.0, "y": 200.0}},
620
620
  "base": {
621
621
  "name": "FinalOutputNode",
622
622
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -652,7 +652,7 @@ def test_serialize_workflow():
652
652
  },
653
653
  }
654
654
  ],
655
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
655
+ "display_data": {"position": {"x": 600.0, "y": -300.0}},
656
656
  "base": {
657
657
  "name": "FinalOutputNode",
658
658
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -688,7 +688,7 @@ def test_serialize_workflow():
688
688
  },
689
689
  }
690
690
  ],
691
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
691
+ "display_data": {"position": {"x": 600.0, "y": 450.0}},
692
692
  "base": {
693
693
  "name": "FinalOutputNode",
694
694
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -943,7 +943,7 @@ def test_conditional_node_serialize_all_operators_with_lhs_and_rhs(descriptor, o
943
943
  ],
944
944
  "version": "2",
945
945
  },
946
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
946
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
947
947
  "base": {
948
948
  "name": "ConditionalNode",
949
949
  "module": ["vellum", "workflows", "nodes", "displayable", "conditional_node", "node"],
@@ -1054,7 +1054,7 @@ def test_conditional_node_serialize_all_operators_with_expression(descriptor, op
1054
1054
  ],
1055
1055
  "version": "2",
1056
1056
  },
1057
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
1057
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
1058
1058
  "base": {
1059
1059
  "name": "ConditionalNode",
1060
1060
  "module": ["vellum", "workflows", "nodes", "displayable", "conditional_node", "node"],
@@ -1177,7 +1177,7 @@ def test_conditional_node_serialize_all_operators_with_value_and_start_and_end(d
1177
1177
  ],
1178
1178
  "version": "2",
1179
1179
  },
1180
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
1180
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
1181
1181
  "base": {
1182
1182
  "name": "ConditionalNode",
1183
1183
  "module": ["vellum", "workflows", "nodes", "displayable", "conditional_node", "node"],
@@ -93,7 +93,7 @@ def test_serialize_workflow():
93
93
  "base": None,
94
94
  "definition": None,
95
95
  "display_data": {
96
- "position": {"x": 0.0, "y": 0.0},
96
+ "position": {"x": 0.0, "y": -50.0},
97
97
  },
98
98
  }
99
99
 
@@ -133,7 +133,7 @@ def test_serialize_workflow():
133
133
  },
134
134
  }
135
135
  ],
136
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
136
+ "display_data": {"position": {"x": 400.0, "y": -175.0}},
137
137
  "base": {
138
138
  "name": "FinalOutputNode",
139
139
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -173,7 +173,7 @@ def test_serialize_workflow():
173
173
  },
174
174
  }
175
175
  ],
176
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
176
+ "display_data": {"position": {"x": 400.0, "y": 75.0}},
177
177
  "base": {
178
178
  "name": "FinalOutputNode",
179
179
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -68,7 +68,7 @@ def test_serialize_workflow():
68
68
  "label": "Entrypoint Node",
69
69
  "source_handle_id": "7d86498b-84ed-4feb-8e62-2188058c2c4e",
70
70
  },
71
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
71
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
72
72
  "base": None,
73
73
  "definition": None,
74
74
  }
@@ -105,7 +105,7 @@ def test_serialize_workflow():
105
105
  "target_handle_id": "70c19f1c-309c-4a5d-ba65-664c0bb2fedf",
106
106
  "error_source_input_id": "8e4c8d76-2e02-4d7e-a7bf-d71af392dc49",
107
107
  },
108
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
108
+ "display_data": {"position": {"x": 400.0, "y": 75.0}},
109
109
  "base": {
110
110
  "name": "ErrorNode",
111
111
  "module": ["vellum", "workflows", "nodes", "core", "error_node", "node"],
@@ -153,7 +153,7 @@ def test_serialize_workflow():
153
153
  },
154
154
  }
155
155
  ],
156
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
156
+ "display_data": {"position": {"x": 600.0, "y": -50.0}},
157
157
  "base": {
158
158
  "name": "FinalOutputNode",
159
159
  "module": [
@@ -56,15 +56,15 @@ def test_serialize_workflow():
56
56
 
57
57
  # AND each node should be serialized correctly
58
58
  entrypoint_node = workflow_raw_data["nodes"][0]
59
- assert entrypoint_node == {
60
- "id": "f1e4678f-c470-400b-a40e-c8922cc99a86",
61
- "type": "ENTRYPOINT",
62
- "inputs": [],
63
- "data": {"label": "Entrypoint Node", "source_handle_id": "40201804-8beb-43ad-8873-a027759512f1"},
64
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
65
- "base": None,
66
- "definition": None,
59
+ assert entrypoint_node["id"] == "f1e4678f-c470-400b-a40e-c8922cc99a86"
60
+ assert entrypoint_node["type"] == "ENTRYPOINT"
61
+ assert entrypoint_node["inputs"] == []
62
+ assert entrypoint_node["data"] == {
63
+ "label": "Entrypoint Node",
64
+ "source_handle_id": "40201804-8beb-43ad-8873-a027759512f1",
67
65
  }
66
+ assert entrypoint_node["base"] is None
67
+ assert entrypoint_node["definition"] is None
68
68
 
69
69
  api_node = workflow_raw_data["nodes"][1]
70
70
  assert api_node["id"] == "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f"
@@ -100,7 +100,7 @@ def test_serialize_workflow():
100
100
  },
101
101
  }
102
102
  ],
103
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
103
+ "display_data": {"position": {"x": 400.0, "y": -50.0}},
104
104
  "base": {
105
105
  "name": "FinalOutputNode",
106
106
  "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
@@ -146,6 +146,17 @@ def test_serialize_workflow():
146
146
  }
147
147
  }
148
148
 
149
+ # AND the nodes should have been auto-positioned since they all started at (0,0)
150
+ nodes = workflow_raw_data["nodes"]
151
+ positions = [node["display_data"]["position"] for node in nodes]
152
+
153
+ nodes_at_zero = sum(1 for pos in positions if pos["x"] == 0.0 and pos["y"] == 0.0)
154
+ assert nodes_at_zero < len(nodes), "Auto layout should have positioned nodes away from (0,0)"
155
+
156
+ x_positions = [pos["x"] for pos in positions]
157
+ assert all(x >= 0 for x in x_positions), "All x positions should be non-negative"
158
+ assert len(set(x_positions)) > 1 or len(nodes) == 1, "Nodes should be spread across different x positions"
159
+
149
160
  # AND the definition should be what we expect
150
161
  definition = workflow_raw_data["definition"]
151
162
  assert definition == {
@@ -65,7 +65,7 @@ def test_serialize_workflow():
65
65
  "definition": None,
66
66
  "inputs": [],
67
67
  "data": {"label": "Entrypoint Node", "source_handle_id": "41840690-8d85-486e-a864-b0661ccf0f2e"},
68
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
68
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
69
69
  }
70
70
 
71
71
  guardrail_node = workflow_raw_data["nodes"][1]
@@ -108,7 +108,7 @@ def test_serialize_workflow():
108
108
  "metric_definition_id": "example_metric_definition",
109
109
  "release_tag": "LATEST",
110
110
  },
111
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
111
+ "display_data": {"position": {"x": 200.0, "y": -50.0}},
112
112
  "base": {
113
113
  "module": ["vellum", "workflows", "nodes", "displayable", "guardrail_node", "node"],
114
114
  "name": "GuardrailNode",
@@ -162,7 +162,7 @@ def test_serialize_workflow():
162
162
  },
163
163
  }
164
164
  ],
165
- "display_data": {"position": {"x": 0.0, "y": 0.0}},
165
+ "display_data": {"position": {"x": 400.0, "y": -50.0}},
166
166
  }
167
167
 
168
168
  # AND each edge should be serialized correctly