vellum-ai 0.10.6__py3-none-any.whl → 0.10.8__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) 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/events/types.py +1 -1
  15. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -0
  16. vellum/workflows/nodes/core/templating_node/node.py +5 -0
  17. vellum/workflows/nodes/displayable/api_node/node.py +1 -1
  18. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +1 -2
  19. vellum/workflows/nodes/displayable/code_execution_node/node.py +1 -2
  20. vellum/workflows/nodes/displayable/code_execution_node/utils.py +13 -2
  21. vellum/workflows/nodes/displayable/conditional_node/node.py +2 -2
  22. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +10 -3
  23. vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +6 -1
  24. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +1 -2
  25. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +1 -2
  26. vellum/workflows/ports/node_ports.py +2 -2
  27. vellum/workflows/ports/port.py +14 -0
  28. vellum/workflows/references/__init__.py +2 -0
  29. vellum/workflows/runner/runner.py +49 -8
  30. vellum/workflows/runner/types.py +1 -3
  31. vellum/workflows/state/encoder.py +2 -1
  32. vellum/workflows/types/__init__.py +5 -0
  33. vellum/workflows/types/tests/test_utils.py +6 -3
  34. vellum/workflows/types/utils.py +3 -0
  35. {vellum_ai-0.10.6.dist-info → vellum_ai-0.10.8.dist-info}/METADATA +1 -1
  36. {vellum_ai-0.10.6.dist-info → vellum_ai-0.10.8.dist-info}/RECORD +49 -47
  37. vellum_cli/__init__.py +23 -4
  38. vellum_cli/pull.py +28 -13
  39. vellum_cli/tests/test_pull.py +45 -2
  40. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  41. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +17 -2
  42. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +4 -2
  43. vellum_ee/workflows/display/nodes/vellum/map_node.py +20 -48
  44. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +5 -16
  45. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +11 -8
  46. vellum_ee/workflows/display/utils/vellum.py +3 -2
  47. {vellum_ai-0.10.6.dist-info → vellum_ai-0.10.8.dist-info}/LICENSE +0 -0
  48. {vellum_ai-0.10.6.dist-info → vellum_ai-0.10.8.dist-info}/WHEEL +0 -0
  49. {vellum_ai-0.10.6.dist-info → vellum_ai-0.10.8.dist-info}/entry_points.txt +0 -0
vellum_cli/pull.py CHANGED
@@ -7,28 +7,38 @@ from typing import Optional
7
7
  from dotenv import load_dotenv
8
8
 
9
9
  from vellum.workflows.vellum_client import create_vellum_client
10
- from vellum_cli.config import load_vellum_cli_config
10
+ from vellum_cli.config import WorkflowConfig, load_vellum_cli_config
11
11
  from vellum_cli.logger import load_cli_logger
12
12
 
13
13
 
14
14
  def pull_command(
15
- module: Optional[str], legacy_module: Optional[bool] = None, include_json: Optional[bool] = None
15
+ module: Optional[str] = None,
16
+ workflow_sandbox_id: Optional[str] = None,
17
+ include_json: Optional[bool] = None,
18
+ exclude_code: Optional[bool] = None,
16
19
  ) -> None:
17
20
  load_dotenv()
18
21
  logger = load_cli_logger()
19
22
  config = load_vellum_cli_config()
20
23
 
21
- if not config.workflows:
22
- raise ValueError("No Workflows found in project to pull.")
23
-
24
- if len(config.workflows) > 1 and not module:
25
- raise ValueError("Multiple workflows found in project to pull. Pulling only a single workflow is supported.")
26
-
27
24
  workflow_config = (
28
- next((w for w in config.workflows if w.module == module), None) if module else config.workflows[0]
25
+ next((w for w in config.workflows if w.module == module), None)
26
+ if module
27
+ else (config.workflows[0] if config.workflows else None)
29
28
  )
29
+ save_lock_file = False
30
30
  if workflow_config is None:
31
- raise ValueError(f"No workflow config for '{module}' found in project to push.")
31
+ if module:
32
+ raise ValueError(f"No workflow config for '{module}' found in project to pull.")
33
+ elif workflow_sandbox_id:
34
+ workflow_config = WorkflowConfig(
35
+ workflow_sandbox_id=workflow_sandbox_id,
36
+ module=f"workflow_{workflow_sandbox_id.split('-')[0]}",
37
+ )
38
+ config.workflows.append(workflow_config)
39
+ save_lock_file = True
40
+ else:
41
+ raise ValueError("No workflow config found in project to pull from.")
32
42
 
33
43
  if not workflow_config.workflow_sandbox_id:
34
44
  raise ValueError("No workflow sandbox ID found in project to pull from.")
@@ -36,10 +46,10 @@ def pull_command(
36
46
  logger.info(f"Pulling workflow into {workflow_config.module}")
37
47
  client = create_vellum_client()
38
48
  query_parameters = {}
39
- if legacy_module:
40
- query_parameters["legacyModule"] = legacy_module
41
49
  if include_json:
42
50
  query_parameters["include_json"] = include_json
51
+ if exclude_code:
52
+ query_parameters["exclude_code"] = exclude_code
43
53
 
44
54
  response = client.workflows.pull(
45
55
  workflow_config.workflow_sandbox_id,
@@ -81,6 +91,11 @@ def pull_command(
81
91
  target.write(source.read().decode("utf-8"))
82
92
 
83
93
  if include_json:
84
- logger.warning("The pulled JSON representation of the Workflow should be used for debugging purposely only. Its schema should be considered unstable and subject to change at any time.")
94
+ logger.warning(
95
+ "The pulled JSON representation of the Workflow should be used for debugging purposely only. Its schema should be considered unstable and subject to change at any time."
96
+ )
97
+
98
+ if save_lock_file:
99
+ config.save()
85
100
 
86
101
  logger.info(f"Successfully pulled Workflow into {workflow_config.module}")
@@ -69,8 +69,33 @@ def test_pull(vellum_client, mock_module):
69
69
  pull_command(module)
70
70
 
71
71
  # THEN the workflow.py file is written to the module directory
72
- assert os.path.exists(os.path.join(temp_dir, *module.split("."), "workflow.py"))
73
- with open(os.path.join(temp_dir, *module.split("."), "workflow.py")) as f:
72
+ workflow_py = os.path.join(temp_dir, *module.split("."), "workflow.py")
73
+ assert os.path.exists(workflow_py)
74
+ with open(workflow_py) as f:
75
+ assert f.read() == "print('hello')"
76
+
77
+
78
+ def test_pull__sandbox_id_with_no_config(vellum_client):
79
+ # GIVEN a workflow sandbox id
80
+ workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
81
+
82
+ # AND the workflow pull API call returns a zip file
83
+ vellum_client.workflows.pull.return_value = iter([zip_file_map({"workflow.py": "print('hello')"})])
84
+
85
+ # AND we are currently in a new directory
86
+ current_dir = os.getcwd()
87
+ temp_dir = tempfile.mkdtemp()
88
+ os.chdir(temp_dir)
89
+
90
+ # WHEN the user runs the pull command with the workflow sandbox id and no module
91
+ pull_command(workflow_sandbox_id=workflow_sandbox_id)
92
+ os.chdir(current_dir)
93
+
94
+ # THEN the pull api is called with exclude_code=True
95
+ vellum_client.workflows.pull.assert_called_once()
96
+ workflow_py = os.path.join(temp_dir, "workflow_87654321", "workflow.py")
97
+ assert os.path.exists(workflow_py)
98
+ with open(workflow_py) as f:
74
99
  assert f.read() == "print('hello')"
75
100
 
76
101
 
@@ -168,3 +193,21 @@ def test_pull__include_json(vellum_client, mock_module):
168
193
  vellum_client.workflows.pull.assert_called_once()
169
194
  call_args = vellum_client.workflows.pull.call_args.kwargs
170
195
  assert call_args["request_options"]["additional_query_parameters"] == {"include_json": True}
196
+
197
+
198
+ def test_pull__exclude_code(vellum_client, mock_module):
199
+ # GIVEN a module on the user's filesystem
200
+ _, module = mock_module
201
+
202
+ # AND the workflow pull API call returns a zip file
203
+ vellum_client.workflows.pull.return_value = iter(
204
+ [zip_file_map({"workflow.py": "print('hello')", "workflow.json": "{}"})]
205
+ )
206
+
207
+ # WHEN the user runs the pull command
208
+ pull_command(module, exclude_code=True)
209
+
210
+ # THEN the pull api is called with exclude_code=True
211
+ vellum_client.workflows.pull.assert_called_once()
212
+ call_args = vellum_client.workflows.pull.call_args.kwargs
213
+ assert call_args["request_options"]["additional_query_parameters"] == {"exclude_code": True}
@@ -1,7 +1,7 @@
1
1
  from functools import cached_property
2
2
  import inspect
3
3
  from uuid import UUID
4
- from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Type, TypeVar, get_args, get_origin, cast
4
+ from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Type, TypeVar, cast, get_args, get_origin
5
5
 
6
6
  from vellum.workflows.nodes.bases.base import BaseNode
7
7
  from vellum.workflows.nodes.utils import get_wrapped_node, has_wrapped_node
@@ -1,5 +1,5 @@
1
1
  from uuid import UUID
2
- from typing import ClassVar, Generic, Optional, TypeVar
2
+ from typing import ClassVar, Dict, Generic, Optional, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
5
5
  from vellum.workflows.nodes.displayable.code_execution_node.utils import read_file_from_path
@@ -20,6 +20,8 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
20
20
  output_id: ClassVar[Optional[UUID]] = None
21
21
  log_output_id: ClassVar[Optional[UUID]] = None
22
22
 
23
+ node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
24
+
23
25
  def serialize(
24
26
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
25
27
  ) -> JsonObject:
@@ -27,6 +29,19 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
27
29
  node_id = self.node_id
28
30
 
29
31
  code = read_file_from_path(raise_if_descriptor(node.filepath))
32
+ code_inputs = raise_if_descriptor(node.code_inputs)
33
+
34
+ inputs = [
35
+ create_node_input(
36
+ node_id=node_id,
37
+ input_name=variable_name,
38
+ value=variable_value,
39
+ display_context=display_context,
40
+ input_id=self.node_input_ids_by_name.get(variable_name),
41
+ )
42
+ for variable_name, variable_value in code_inputs.items()
43
+ ]
44
+
30
45
  code_node_input = create_node_input(
31
46
  node_id=node_id,
32
47
  input_name="code",
@@ -41,7 +56,7 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
41
56
  display_context=display_context,
42
57
  input_id=self.runtime_input_id,
43
58
  )
44
- inputs = [code_node_input, runtime_node_input]
59
+ inputs.extend([code_node_input, runtime_node_input])
45
60
 
46
61
  packages = raise_if_descriptor(node.packages)
47
62
 
@@ -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,59 +1,32 @@
1
1
  from uuid import UUID
2
- from typing import Any, ClassVar, Dict, Generic, List, Optional, Type, TypeVar
2
+ from typing import Collection, Dict, Generic, List, Optional, TypeVar, cast
3
3
 
4
- from vellum import VellumVariable
5
4
  from vellum.workflows.nodes import MapNode
6
5
  from vellum.workflows.types.core import JsonObject
7
6
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
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
10
9
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
11
- from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
12
- from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
13
10
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
14
11
 
15
12
  _MapNodeType = TypeVar("_MapNodeType", bound=MapNode)
16
13
 
17
14
 
18
15
  class BaseMapNodeDisplay(BaseNodeVellumDisplay[_MapNodeType], Generic[_MapNodeType]):
19
- workflow_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
20
-
21
16
  def serialize(
22
17
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
23
18
  ) -> JsonObject:
24
19
  node = self._node
25
20
  node_id = self.node_id
26
21
 
27
- workflow_inputs: List[VellumVariable] = []
28
22
  subworkflow = raise_if_descriptor(node.subworkflow)
29
- for descriptor in subworkflow.get_inputs_class():
30
- # In WaC it's always 'all_items'
31
- # In Vellum it's always 'items'
32
- variable_name = descriptor.name if descriptor.name != "all_items" else "items"
33
- variable_id = str(
34
- self.workflow_input_ids_by_name.get(variable_name)
35
- 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
@@ -13,30 +13,20 @@ from vellum.workflows.expressions.does_not_equal import DoesNotEqualExpression
13
13
  from vellum.workflows.expressions.ends_with import EndsWithExpression
14
14
  from vellum.workflows.expressions.equals import EqualsExpression
15
15
  from vellum.workflows.expressions.greater_than import GreaterThanExpression
16
- from vellum.workflows.expressions.greater_than_or_equal_to import (
17
- GreaterThanOrEqualToExpression,
18
- )
16
+ from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
19
17
  from vellum.workflows.expressions.in_ import InExpression
20
18
  from vellum.workflows.expressions.is_not_null import IsNotNullExpression
21
19
  from vellum.workflows.expressions.is_null import IsNullExpression
22
20
  from vellum.workflows.expressions.less_than import LessThanExpression
23
- from vellum.workflows.expressions.less_than_or_equal_to import (
24
- LessThanOrEqualToExpression,
25
- )
21
+ from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
26
22
  from vellum.workflows.expressions.not_between import NotBetweenExpression
27
23
  from vellum.workflows.expressions.not_in import NotInExpression
28
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import (
29
- BaseNodeVellumDisplay,
30
- )
24
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
31
25
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
32
- from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import (
33
- get_workflow_display,
34
- )
26
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
35
27
 
36
28
  from tests.workflows.basic_conditional_node.workflow import CategoryWorkflow
37
- from tests.workflows.basic_conditional_node.workflow_with_only_one_conditional_node import (
38
- create_simple_workflow,
39
- )
29
+ from tests.workflows.basic_conditional_node.workflow_with_only_one_conditional_node import create_simple_workflow
40
30
 
41
31
 
42
32
  def test_serialize_workflow():
@@ -965,7 +955,6 @@ def test_conditional_node_serialize_all_operators_with_lhs_and_rhs(
965
955
 
966
956
  # AND the conditional node should be what we expect
967
957
  conditional_node = workflow_raw_data["nodes"][1]
968
- print(conditional_node)
969
958
  assert not DeepDiff(
970
959
  {
971
960
  "id": "a9143814-6bb0-4cb3-a817-4fc076417121",
@@ -228,15 +228,15 @@ def test_serialize_workflow():
228
228
  },
229
229
  },
230
230
  "input_variables": [
231
- {"id": "0fe9dff5-7595-4ff3-a420-416f3fc6f809", "key": "item", "type": "JSON"},
232
- {"id": "27dfe8ba-8be7-4de9-a930-2ec3e375b149", "key": "index", "type": "NUMBER"},
233
- {"id": "4c0a109f-599e-4d04-a396-a51474fc2996", "key": "items", "type": "JSON"},
231
+ {"id": "b29bb546-9bc8-4136-857d-8c7a464ba9d4", "key": "item", "type": "JSON", "required": True, "default": None, "extensions": {"color": None}},
232
+ {"id": "17e7ca49-668f-450d-a792-e1f97d13db67", "key": "index", "type": "NUMBER", "required": True, "default": None, "extensions": {"color": None}},
233
+ {"id": "d6fc6c7a-235f-4b98-86f3-e258d1198f93", "key": "items", "type": "JSON", "required": True, "default": None, "extensions": {"color": None}},
234
234
  ],
235
- "output_variables": [{"id": "56b34d4a-d3a6-4bdb-80a4-dbf644791274", "key": "count", "type": "NUMBER"}],
235
+ "output_variables": [{"id": "2a957315-fae0-4366-8a35-f0b315c5eade", "key": "count", "type": "NUMBER"}],
236
236
  "concurrency": None,
237
- "items_input_id": "4c0a109f-599e-4d04-a396-a51474fc2996",
238
- "item_input_id": "0fe9dff5-7595-4ff3-a420-416f3fc6f809",
239
- "index_input_id": "27dfe8ba-8be7-4de9-a930-2ec3e375b149",
237
+ "items_input_id": "d6fc6c7a-235f-4b98-86f3-e258d1198f93",
238
+ "item_input_id": "b29bb546-9bc8-4136-857d-8c7a464ba9d4",
239
+ "index_input_id": "17e7ca49-668f-450d-a792-e1f97d13db67",
240
240
  },
241
241
  "display_data": {"position": {"x": 0.0, "y": 0.0}},
242
242
  "definition": {
@@ -288,7 +288,7 @@ def test_serialize_workflow():
288
288
  "type": "NODE_OUTPUT",
289
289
  "data": {
290
290
  "node_id": "bf83099a-40df-4445-b90d-1f6f1067ebe3",
291
- "output_id": "56b34d4a-d3a6-4bdb-80a4-dbf644791274",
291
+ "output_id": "2a957315-fae0-4366-8a35-f0b315c5eade",
292
292
  },
293
293
  }
294
294
  ],
@@ -323,6 +323,9 @@ def test_serialize_workflow():
323
323
  },
324
324
  },
325
325
  workflow_raw_data["nodes"][2],
326
+ # TODO: Fix output ID not referencing map node workflow output
327
+ # https://app.shortcut.com/vellum/story/5667/fix-output-display-reference-on-map-nodes
328
+ exclude_regex_paths=r"root\['inputs'\]\[0\]\['value'\]\['rules'\]\[0\]\['data'\]\['output_id'\]",
326
329
  )
327
330
 
328
331
  # AND each edge should be serialized correctly
@@ -4,7 +4,9 @@ import typing
4
4
  from typing import Any, List, Union, cast
5
5
 
6
6
  from vellum import ChatMessage, SearchResult, SearchResultRequest, VellumVariableType
7
+ from vellum.client.types.array_vellum_value import ArrayVellumValue
7
8
  from vellum.workflows.descriptors.base import BaseDescriptor
9
+ from vellum.workflows.nodes.core.map_node.node import MapNodeItemType
8
10
  from vellum.workflows.references import OutputReference, WorkflowInputReference
9
11
  from vellum.workflows.references.execution_count import ExecutionCountReference
10
12
  from vellum.workflows.references.node import NodeReference
@@ -45,9 +47,8 @@ def infer_vellum_variable_type(value: Any) -> VellumVariableType:
45
47
  raise ValueError(
46
48
  f"Expected NodeReference {descriptor.name} to have an instance pointing to a descriptor"
47
49
  )
48
-
49
50
  descriptor = descriptor.instance
50
-
51
+
51
52
  inferred_type = primitive_type_to_vellum_variable_type(descriptor)
52
53
  else:
53
54
  vellum_variable_value = primitive_to_vellum_value(value)