vellum-ai 0.14.45__py3-none-any.whl → 0.14.46__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 (26) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/nodes/core/try_node/node.py +3 -6
  3. vellum/workflows/nodes/core/try_node/tests/test_node.py +0 -24
  4. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +8 -14
  5. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +62 -0
  6. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -54
  7. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +5 -6
  8. vellum/workflows/nodes/utils.py +4 -0
  9. vellum/workflows/types/code_execution_node_wrappers.py +64 -0
  10. vellum/workflows/vellum_client.py +19 -7
  11. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.46.dist-info}/METADATA +1 -1
  12. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.46.dist-info}/RECORD +26 -25
  13. vellum_cli/config.py +7 -2
  14. vellum_cli/push.py +5 -1
  15. vellum_cli/tests/test_push.py +192 -8
  16. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -5
  17. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -5
  18. vellum_ee/workflows/display/types.py +3 -0
  19. vellum_ee/workflows/display/utils/expressions.py +1 -1
  20. vellum_ee/workflows/display/utils/vellum.py +1 -3
  21. vellum_ee/workflows/display/workflows/base_workflow_display.py +10 -0
  22. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +3 -0
  23. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +53 -0
  24. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.46.dist-info}/LICENSE +0 -0
  25. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.46.dist-info}/WHEEL +0 -0
  26. {vellum_ai-0.14.45.dist-info → vellum_ai-0.14.46.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.45",
21
+ "X-Fern-SDK-Version": "0.14.46",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -1,4 +1,4 @@
1
- from typing import Callable, Generic, Iterator, Optional, Set, Type, Union
1
+ from typing import Callable, Generic, Iterator, Optional, Set, Type
2
2
 
3
3
  from vellum.workflows.context import execution_context, get_parent_context
4
4
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
@@ -21,8 +21,7 @@ class TryNode(BaseAdornmentNode[StateType], Generic[StateType]):
21
21
  subworkflow: Type["BaseWorkflow"] - The Subworkflow to execute
22
22
  """
23
23
 
24
- # TODO: We could remove str after we support generating enum values.
25
- on_error_code: Optional[Union[WorkflowErrorCode, str]] = None
24
+ on_error_code: Optional[WorkflowErrorCode] = None
26
25
 
27
26
  class Outputs(BaseAdornmentNode.Outputs):
28
27
  error: Optional[WorkflowError] = None
@@ -66,9 +65,7 @@ class TryNode(BaseAdornmentNode[StateType], Generic[StateType]):
66
65
  message="Subworkflow unexpectedly paused within Try Node",
67
66
  )
68
67
  elif event.name == "workflow.execution.rejected":
69
- # TODO: We should remove this once we support generating enum values.
70
- event_error_code = event.error.code.value if isinstance(self.on_error_code, str) else event.error.code
71
- if self.on_error_code and self.on_error_code != event_error_code:
68
+ if self.on_error_code and self.on_error_code != event.error.code:
72
69
  exception = NodeException(
73
70
  code=WorkflowErrorCode.INVALID_OUTPUTS,
74
71
  message=f"""Unexpected rejection: {event.error.code.value}.
@@ -57,30 +57,6 @@ def test_try_node__retry_on_error_code__missed():
57
57
  assert exc_info.value.code == WorkflowErrorCode.INVALID_OUTPUTS
58
58
 
59
59
 
60
- def test_try_node__on_error_code__str():
61
- # GIVEN a try node that is configured to catch PROVIDER_ERROR
62
- @TryNode.wrap(on_error_code="PROVIDER_ERROR") # type: ignore
63
- class TestNode(BaseNode):
64
- class Outputs(BaseOutputs):
65
- value: int
66
-
67
- def run(self) -> Outputs:
68
- raise NodeException(message="This will be caught", code=WorkflowErrorCode.PROVIDER_ERROR)
69
-
70
- # WHEN the node is run and throws a PROVIDER_ERROR
71
- node = TestNode(state=BaseState())
72
- outputs = [o for o in node.run()]
73
-
74
- # THEN the exception is caught
75
- assert len(outputs) == 2
76
- assert set(outputs) == {
77
- BaseOutput(name="value"),
78
- BaseOutput(
79
- name="error", value=WorkflowError(message="This will be caught", code=WorkflowErrorCode.PROVIDER_ERROR)
80
- ),
81
- }
82
-
83
-
84
60
  def test_try_node__use_parent_inputs_and_state():
85
61
  # GIVEN a parent workflow Inputs and State
86
62
  class Inputs(BaseInputs):
@@ -15,7 +15,7 @@ from vellum import (
15
15
  )
16
16
  from vellum.client import ApiError, RequestOptions
17
17
  from vellum.client.types.chat_message_request import ChatMessageRequest
18
- from vellum.workflows.constants import LATEST_RELEASE_TAG, OMIT
18
+ from vellum.workflows.constants import LATEST_RELEASE_TAG
19
19
  from vellum.workflows.context import get_execution_context
20
20
  from vellum.workflows.errors import WorkflowErrorCode
21
21
  from vellum.workflows.errors.types import vellum_error_to_workflow_error
@@ -48,13 +48,13 @@ class BasePromptDeploymentNode(BasePromptNode, Generic[StateType]):
48
48
  deployment: ClassVar[Union[UUID, str]]
49
49
 
50
50
  release_tag: str = LATEST_RELEASE_TAG
51
- external_id: Optional[str] = OMIT
51
+ external_id: Optional[str] = None
52
52
 
53
- expand_meta: Optional[PromptDeploymentExpandMetaRequest] = OMIT
54
- raw_overrides: Optional[RawPromptExecutionOverridesRequest] = OMIT
55
- expand_raw: Optional[Sequence[str]] = OMIT
56
- metadata: Optional[Dict[str, Optional[Any]]] = OMIT
57
- ml_model_fallbacks: Optional[Sequence[str]] = OMIT
53
+ expand_meta: Optional[PromptDeploymentExpandMetaRequest] = None
54
+ raw_overrides: Optional[RawPromptExecutionOverridesRequest] = None
55
+ expand_raw: Optional[Sequence[str]] = None
56
+ metadata: Optional[Dict[str, Optional[Any]]] = None
57
+ ml_model_fallbacks: Optional[Sequence[str]] = None
58
58
 
59
59
  class Trigger(BasePromptNode.Trigger):
60
60
  merge_behavior = MergeBehavior.AWAIT_ANY
@@ -103,12 +103,7 @@ class BasePromptDeploymentNode(BasePromptNode, Generic[StateType]):
103
103
  prompt_event_stream = self._get_prompt_event_stream()
104
104
  next(prompt_event_stream)
105
105
  except ApiError as e:
106
- if (
107
- e.status_code
108
- and e.status_code < 500
109
- and self.ml_model_fallbacks is not OMIT
110
- and self.ml_model_fallbacks is not None
111
- ):
106
+ if e.status_code and e.status_code < 500 and self.ml_model_fallbacks is not None:
112
107
  prompt_event_stream = self._retry_prompt_stream_with_fallbacks(tried_fallbacks)
113
108
  else:
114
109
  self._handle_api_error(e)
@@ -127,7 +122,6 @@ class BasePromptDeploymentNode(BasePromptNode, Generic[StateType]):
127
122
  if (
128
123
  event.error
129
124
  and event.error.code == WorkflowErrorCode.PROVIDER_ERROR.value
130
- and self.ml_model_fallbacks is not OMIT
131
125
  and self.ml_model_fallbacks is not None
132
126
  ):
133
127
  try:
@@ -821,3 +821,65 @@ def main(arg1: list) -> str:
821
821
 
822
822
  # AND the result should be the correct output
823
823
  assert outputs == {"result": "bar", "log": ""}
824
+
825
+
826
+ def test_run_node__string_value_wrapper__get_attr():
827
+ # GIVEN a node that accesses the 'value' property of a string input
828
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
829
+ code = """\
830
+ def main(text: str) -> str:
831
+ return text.value
832
+ """
833
+ code_inputs = {
834
+ "text": "hello",
835
+ }
836
+ runtime = "PYTHON_3_11_6"
837
+
838
+ # WHEN we run the node
839
+ node = ExampleCodeExecutionNode()
840
+ outputs = node.run()
841
+
842
+ # THEN the node should successfully access the string value through the .value property
843
+ assert outputs == {"result": "hello", "log": ""}
844
+
845
+
846
+ def test_run_node__string_value_wrapper__get_item():
847
+ # GIVEN a node that accesses the 'value' property of a string input
848
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
849
+ code = """\
850
+ def main(text: str) -> str:
851
+ return text["value"]
852
+ """
853
+ code_inputs = {
854
+ "text": "hello",
855
+ }
856
+ runtime = "PYTHON_3_11_6"
857
+
858
+ # WHEN we run the node
859
+ node = ExampleCodeExecutionNode()
860
+ outputs = node.run()
861
+
862
+ # THEN the node should successfully access the string value through the .value property
863
+ assert outputs == {"result": "hello", "log": ""}
864
+
865
+
866
+ def test_run_node__string_value_wrapper__list_of_dicts():
867
+ # GIVEN a node that accesses the 'value' property of a string input
868
+ class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Any]):
869
+ code = """\
870
+ def main(output: list[str]) -> list[str]:
871
+ results = []
872
+ for item in output:
873
+ results.append(item['value'])
874
+
875
+ return results
876
+ """
877
+ code_inputs = {"output": ['{"foo": "bar"}', '{"foo2": "bar2"}']}
878
+ runtime = "PYTHON_3_11_6"
879
+
880
+ # WHEN we run the node
881
+ node = ExampleCodeExecutionNode()
882
+ outputs = node.run()
883
+
884
+ # THEN the node should successfully access the string value
885
+ assert outputs == {"result": ['{"foo": "bar"}', '{"foo2": "bar2"}'], "log": ""}
@@ -8,6 +8,7 @@ from vellum.workflows.errors.types import WorkflowErrorCode
8
8
  from vellum.workflows.exceptions import NodeException
9
9
  from vellum.workflows.nodes.utils import cast_to_output_type
10
10
  from vellum.workflows.state.context import WorkflowContext
11
+ from vellum.workflows.types.code_execution_node_wrappers import ListWrapper, clean_for_dict_wrapper
11
12
  from vellum.workflows.types.core import EntityInputsInterface
12
13
 
13
14
 
@@ -35,58 +36,6 @@ def read_file_from_path(
35
36
  return None
36
37
 
37
38
 
38
- class ListWrapper(list):
39
- def __getitem__(self, key):
40
- item = super().__getitem__(key)
41
- if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
42
- self.__setitem__(key, _clean_for_dict_wrapper(item))
43
-
44
- return super().__getitem__(key)
45
-
46
-
47
- class DictWrapper(dict):
48
- """
49
- This wraps a dict object to make it behave basically the same as a standard javascript object
50
- and enables us to use vellum types here without a shared library since we don't actually
51
- typecheck things here.
52
- """
53
-
54
- def __getitem__(self, key):
55
- return self.__getattr__(key)
56
-
57
- def __getattr__(self, attr):
58
- if attr not in self:
59
- if attr == "value":
60
- # In order to be backwards compatible with legacy Workflows, which wrapped
61
- # several values as VellumValue objects, we use the "value" key to return itself
62
- return self
63
-
64
- raise AttributeError(f"dict has no key: '{attr}'")
65
-
66
- item = super().__getitem__(attr)
67
- if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
68
- self.__setattr__(attr, _clean_for_dict_wrapper(item))
69
-
70
- return super().__getitem__(attr)
71
-
72
- def __setattr__(self, name, value):
73
- self[name] = value
74
-
75
-
76
- def _clean_for_dict_wrapper(obj):
77
- if isinstance(obj, dict):
78
- wrapped = DictWrapper(obj)
79
- for key in wrapped:
80
- wrapped[key] = _clean_for_dict_wrapper(wrapped[key])
81
-
82
- return wrapped
83
-
84
- elif isinstance(obj, list):
85
- return ListWrapper(map(lambda item: _clean_for_dict_wrapper(item), obj))
86
-
87
- return obj
88
-
89
-
90
39
  def run_code_inline(
91
40
  code: str,
92
41
  inputs: EntityInputsInterface,
@@ -107,12 +56,12 @@ def run_code_inline(
107
56
  (
108
57
  item.model_dump()
109
58
  if isinstance(item, BaseModel)
110
- else _clean_for_dict_wrapper(item) if isinstance(item, (dict, list)) else item
59
+ else clean_for_dict_wrapper(item) if isinstance(item, (dict, list, str)) else item
111
60
  )
112
61
  for item in value
113
62
  ]
114
63
  )
115
- return _clean_for_dict_wrapper(value)
64
+ return clean_for_dict_wrapper(value)
116
65
 
117
66
  exec_globals = {
118
67
  "__arg__inputs": {name: wrap_value(value) for name, value in inputs.items()},
@@ -9,7 +9,6 @@ from vellum import (
9
9
  PromptOutput,
10
10
  StringVellumValue,
11
11
  )
12
- from vellum.workflows.constants import OMIT
13
12
  from vellum.workflows.inputs import BaseInputs
14
13
  from vellum.workflows.nodes import PromptDeploymentNode
15
14
  from vellum.workflows.state import BaseState
@@ -66,14 +65,14 @@ def test_text_prompt_deployment_node__basic(vellum_client):
66
65
 
67
66
  # AND we should have made the expected call to stream the prompt execution
68
67
  vellum_client.execute_prompt_stream.assert_called_once_with(
69
- expand_meta=OMIT,
70
- expand_raw=OMIT,
71
- external_id=OMIT,
68
+ expand_meta=None,
69
+ expand_raw=None,
70
+ external_id=None,
72
71
  inputs=[],
73
- metadata=OMIT,
72
+ metadata=None,
74
73
  prompt_deployment_id=None,
75
74
  prompt_deployment_name="my-deployment",
76
- raw_overrides=OMIT,
75
+ raw_overrides=None,
77
76
  release_tag="LATEST",
78
77
  request_options={
79
78
  "additional_body_parameters": {"execution_context": {"parent_context": None, "trace_id": mock.ANY}}
@@ -1,4 +1,5 @@
1
1
  from functools import cache
2
+ import inspect
2
3
  import json
3
4
  import sys
4
5
  from types import ModuleType
@@ -14,6 +15,7 @@ from vellum.workflows.nodes import BaseNode
14
15
  from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
15
16
  from vellum.workflows.ports.port import Port
16
17
  from vellum.workflows.state.base import BaseState
18
+ from vellum.workflows.types.code_execution_node_wrappers import StringValueWrapper
17
19
  from vellum.workflows.types.core import Json
18
20
  from vellum.workflows.types.generics import NodeType
19
21
 
@@ -176,6 +178,8 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
176
178
 
177
179
 
178
180
  def _get_type_name(obj: Any) -> str:
181
+ if inspect.isclass(obj) and issubclass(obj, StringValueWrapper):
182
+ return "str"
179
183
  if isinstance(obj, type):
180
184
  return obj.__name__
181
185
 
@@ -0,0 +1,64 @@
1
+ class StringValueWrapper(str):
2
+ def __getitem__(self, key):
3
+ if key == "value":
4
+ return self
5
+ raise KeyError(key)
6
+
7
+ def __getattr__(self, attr):
8
+ if attr == "value":
9
+ return self
10
+ raise AttributeError(f"'str' object has no attribute '{attr}'")
11
+
12
+
13
+ class ListWrapper(list):
14
+ def __getitem__(self, key):
15
+ item = super().__getitem__(key)
16
+ if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
17
+ self.__setitem__(key, clean_for_dict_wrapper(item))
18
+
19
+ return super().__getitem__(key)
20
+
21
+
22
+ class DictWrapper(dict):
23
+ """
24
+ This wraps a dict object to make it behave basically the same as a standard javascript object
25
+ and enables us to use vellum types here without a shared library since we don't actually
26
+ typecheck things here.
27
+ """
28
+
29
+ def __getitem__(self, key):
30
+ return self.__getattr__(key)
31
+
32
+ def __getattr__(self, attr):
33
+ if attr not in self:
34
+ if attr == "value":
35
+ # In order to be backwards compatible with legacy Workflows, which wrapped
36
+ # several values as VellumValue objects, we use the "value" key to return itself
37
+ return self
38
+
39
+ raise AttributeError(f"dict has no key: '{attr}'")
40
+
41
+ item = super().__getitem__(attr)
42
+ if not isinstance(item, DictWrapper) and not isinstance(item, ListWrapper):
43
+ self.__setattr__(attr, clean_for_dict_wrapper(item))
44
+
45
+ return super().__getitem__(attr)
46
+
47
+ def __setattr__(self, name, value):
48
+ self[name] = value
49
+
50
+
51
+ def clean_for_dict_wrapper(obj):
52
+ if isinstance(obj, dict):
53
+ wrapped = DictWrapper(obj)
54
+ for key in wrapped:
55
+ wrapped[key] = clean_for_dict_wrapper(wrapped[key])
56
+
57
+ return wrapped
58
+
59
+ elif isinstance(obj, list):
60
+ return ListWrapper(map(lambda item: clean_for_dict_wrapper(item), obj))
61
+ elif isinstance(obj, str):
62
+ return StringValueWrapper(obj)
63
+
64
+ return obj
@@ -1,22 +1,34 @@
1
1
  import os
2
- from typing import Optional
2
+ from typing import List, Optional
3
3
 
4
4
  from vellum import Vellum, VellumEnvironment
5
5
 
6
6
 
7
- def create_vellum_client(api_key: Optional[str] = None) -> Vellum:
7
+ def create_vellum_client(api_key: Optional[str] = None, api_url: Optional[str] = None) -> Vellum:
8
8
  if api_key is None:
9
9
  api_key = os.getenv("VELLUM_API_KEY", default="")
10
10
 
11
11
  return Vellum(
12
12
  api_key=api_key,
13
- environment=create_vellum_environment(),
13
+ environment=create_vellum_environment(api_url),
14
14
  )
15
15
 
16
16
 
17
- def create_vellum_environment() -> VellumEnvironment:
17
+ def create_vellum_environment(api_url: Optional[str] = None) -> VellumEnvironment:
18
18
  return VellumEnvironment(
19
- default=os.getenv("VELLUM_DEFAULT_API_URL", os.getenv("VELLUM_API_URL", "https://api.vellum.ai")),
20
- documents=os.getenv("VELLUM_DOCUMENTS_API_URL", os.getenv("VELLUM_API_URL", "https://documents.vellum.ai")),
21
- predict=os.getenv("VELLUM_PREDICT_API_URL", os.getenv("VELLUM_API_URL", "https://predict.vellum.ai")),
19
+ default=_resolve_env([api_url, "VELLUM_DEFAULT_API_URL", "VELLUM_API_URL"], "https://api.vellum.ai"),
20
+ documents=_resolve_env([api_url, "VELLUM_DOCUMENTS_API_URL", "VELLUM_API_URL"], "https://documents.vellum.ai"),
21
+ predict=_resolve_env([api_url, "VELLUM_PREDICT_API_URL", "VELLUM_API_URL"], "https://predict.vellum.ai"),
22
22
  )
23
+
24
+
25
+ def _resolve_env(names: List[Optional[str]], default: str = "") -> str:
26
+ for name in names:
27
+ if not name:
28
+ continue
29
+
30
+ value = os.getenv(name)
31
+ if value:
32
+ return value
33
+
34
+ return default
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.45
3
+ Version: 0.14.46
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -2,13 +2,13 @@ vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,29
2
2
  vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
3
3
  vellum_cli/__init__.py,sha256=7aO9XFnaEVRiVshn86cFudebFUccT-gV8xIARJWqKYo,12257
4
4
  vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
5
- vellum_cli/config.py,sha256=aKnhvM5B8QdPA4cQC5Sqg7ImP-vNcVdSkZmk_OBpQTw,9309
5
+ vellum_cli/config.py,sha256=v5BmZ-t_v4Jmqd7KVuQMZF2pRI-rbMspSkVYXIRoTmI,9448
6
6
  vellum_cli/image_push.py,sha256=8DDvRDJEZ-FukUCqGW1827bg1ybF4xBbx9WyqWYQE-g,6816
7
7
  vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
8
8
  vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
9
9
  vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
10
10
  vellum_cli/pull.py,sha256=2hSJGeqooevMb--mcvRLQ1GYT-9290cI7VdSRifzmTg,12561
11
- vellum_cli/push.py,sha256=0bHAhp6B67JUFuiL9sSekrLOw-x2Dp6VsrzhFbOy7e4,9508
11
+ vellum_cli/push.py,sha256=nWHLDi_w0LXycNkVv00CiNwY469BcTNBn7NphWpCA7E,9711
12
12
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
14
14
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
@@ -17,7 +17,7 @@ vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ
17
17
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
18
18
  vellum_cli/tests/test_ping.py,sha256=3ucVRThEmTadlV9LrJdCCrr1Ofj3rOjG6ue0BNR2UC0,2523
19
19
  vellum_cli/tests/test_pull.py,sha256=iTxVbJGuehvgNt8Vp9W3Y5Bvaocfws8bl8LMGEbc_qQ,47508
20
- vellum_cli/tests/test_push.py,sha256=j22l7p_cy1KXdcvQKhWiM2bpu-3WL1q5IJquRm84mxE,25580
20
+ vellum_cli/tests/test_push.py,sha256=uNMmPG9Z0uRN6xYYMzBGZaCXJZkAHWcbcSJRJ4eQk70,31182
21
21
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -45,10 +45,10 @@ vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=yZO9U
45
45
  vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=T3a6Lv51SC1506bTVd8T9y_KVmP2VP4IQarAO87xdmM,3755
46
46
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=yBWeN4T_lOsDVnNOKWRiT7JYKu0IR5Fx2z99iq6QKSA,3273
47
47
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=3E0UqmgVYdtbj4nyq8gKju8EpMsRHMCQ0KLrJhug3XU,1084
48
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=7wgyHaeolEKBKBZioqt1zc7cHxkuaW_2diL9XjIdFNE,3461
48
+ vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=NCh9T7M4V1w-5i_bfYdI6L4jj1C_JGBuVBb0nXXsN38,3239
49
49
  vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=X3xnlAU5JisL0jRvaG_V9RvTF7ZlGufTO8tXLLVhGIg,3280
50
50
  vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=7vU_4IxYe7iwn4p7J909cxF1TOR-tUlFXA7k3ySQPwM,9320
51
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=5NWneQCRtawbl_fI9cZXH5ssD2EheHAMhl2mnZOsJ0c,2814
51
+ vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=TIMpOL0dm__xvnDHotCEjBQWCp2hgRDJUnRuPMBrZcE,2592
52
52
  vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=Nk5pxly4d-kactJ3Z4SLErZCm8hicyj2rCArZw8pW9k,3283
53
53
  vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py,sha256=OoNO-BstB96F-VMK6FZ9aXyi-0wyNePo6HiaJC6SYRw,3880
@@ -91,17 +91,17 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_n
91
91
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=r748dpS13HtwY7t_KQFExFssxRy0xI2d-wxmhiUHRe0,3850
92
92
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=EL5kfakuoEcwD85dGjhMta-J-PpCHRSDoc80SdbBrQk,2769
93
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=RmFUDx8dYdfsOE2CGLvdXqNNRtLLpVzXDN8dqZyMcZ8,5822
94
- vellum_ee/workflows/display/types.py,sha256=NKtEGFhvgz1i_oAayXFIS06QZautixnS3id5DckCIjg,2637
94
+ vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHavATD88w,2821
95
95
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
96
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
97
- vellum_ee/workflows/display/utils/expressions.py,sha256=jtFyogkad5i8OKbaragW_C5-J2wXpMroRNqzkq5AJE0,10635
97
+ vellum_ee/workflows/display/utils/expressions.py,sha256=Q0Vf-88wtbOIM1ZRkAaF5tMZc2V-i8LeZ32wFmHFmZg,10642
98
98
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
99
- vellum_ee/workflows/display/utils/vellum.py,sha256=4gm4mPvM7srq5IGytPi41QvO6LPC5NwWPy_sRM1Iicg,5520
99
+ vellum_ee/workflows/display/utils/vellum.py,sha256=HDe1vtIaU35OEg4oC1KC6WNwhw5Laf_mNURorzbeutQ,5418
100
100
  vellum_ee/workflows/display/vellum.py,sha256=o7mq_vk2Yapu9DDKRz5l76h8EmCAypWGQYe6pryrbB8,3576
101
101
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
102
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=MifwBKquksk55O8qxYsxfPgFbiHUkgZ-tQNlO-a25uU,32896
103
- vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=s6yMO0RxoM8scmfT8TJ-9cwl-WHFe7JSyEJA0alCeEs,1913
104
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=ZmbW-_QgKE7Sx3H0KK4wvm48I5HvXs5ITJRsFiriM5w,13810
102
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=NuWlnGNe3Htcfh-l_8e37uitdUsy6WZNB7W7dYcCoUg,33355
103
+ vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
104
+ vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=U0LsUqjJ-5qrkqgFsvdWzqWX7OHEMzsDCWrXR1xyzp0,15599
105
105
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=aaKdmWrgEe5YyV4zuDY_4E3y-l59rIHQnNGiPj2OWxQ,359
106
106
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
107
  vellum_ee/workflows/server/virtual_file_loader.py,sha256=7JphJcSO3H85qiC2DpFfBWjC3JjrbRmoynBC6KKHVsA,2710
@@ -132,7 +132,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
132
132
  vellum/client/__init__.py,sha256=Z-JHK2jGxhtTtmkLeOaUGGJWIUNYGNVBLvUewC6lp6w,118148
133
133
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
134
134
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
135
- vellum/client/core/client_wrapper.py,sha256=2R_elVxC5Z8J7AjG_3HT1Vmw7m81NGxIOuC5HYXmvCI,1869
135
+ vellum/client/core/client_wrapper.py,sha256=Bo-z8mq3B7ugaNmdHcHDd0_iCRPsJMoD6YQfXPjlnC8,1869
136
136
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
137
137
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
138
138
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -1562,9 +1562,9 @@ vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id
1562
1562
  vellum/workflows/nodes/core/templating_node/node.py,sha256=iqBmr2i-f-BqhisNQJiDfewjol0ur7-XpupLStyMJsg,3731
1563
1563
  vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nXkgGDBg4wP36NwykdMEVWwx_xjv8oGT2rYkwuCB_VU,10075
1564
1564
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1565
- vellum/workflows/nodes/core/try_node/node.py,sha256=1DAgNXFTGMRikbsPKclb9S9ZCzNCfeQYYNjbi2UCwds,4685
1565
+ vellum/workflows/nodes/core/try_node/node.py,sha256=XdyOvlwQ3m4h0-_WNtaBl2t_CdlzPXclulkLOtUcX3E,4388
1566
1566
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1567
- vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=yoGPUdeO8Oa3N66XpwIhVVvGTBYSTQPPNTpwWefZIv4,5763
1567
+ vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=h6eUc3SggvhzBWlOD0PrPUlkoCSQHwjqYn81VkxSIxU,4948
1568
1568
  vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
1569
1569
  vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
1570
1570
  vellum/workflows/nodes/displayable/api_node/node.py,sha256=cp0nAukcOpM6TcNhbz12h08TMJxp_LM-MLDl1dAzYsk,2534
@@ -1580,7 +1580,7 @@ vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=
1580
1580
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=wqN1EjyjTL6McUmlkHWu3GXVzcNaqDjavvmKUHDaVqg,10623
1581
1581
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1582
1582
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=YPOFoaEBENfOzE_qWo3WdQ_E1dQk78aLCWk8gOMvTjg,16042
1583
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=lwH7mfiHcRKFxU1Y9IPQVgb3o5trssuhwlKnA30rTWk,9777
1583
+ vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=T99UWACTD9ytVDVHa6W2go00V7HNwDxOyBFyMM2GnhQ,9567
1584
1584
  vellum/workflows/nodes/displayable/bases/search_node.py,sha256=3UtbqY3QO4kzfJHbmUNZGnEEfJmaoiF892u8H6TGjp8,5381
1585
1585
  vellum/workflows/nodes/displayable/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1586
1586
  vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMDPevgwLg1i6YK0g4L4bCy-7xCBN5yYZI,3156
@@ -1591,8 +1591,8 @@ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=Ko_Dy17Ajf
1591
1591
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1592
1592
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1593
1593
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1594
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=AzgTK2YSvVj7zr6gWZfz0-YGf1cVQ9DiSx9fe5BR4uE,24690
1595
- vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=G-sc7yOL5g6rLk99X8HAbXNcLxRaqpju9IXq1iUwnQI,4470
1594
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=Km_BH9W61IJSRj5CdiSmEOINOIouG_a5Gey0fCDmShQ,26620
1595
+ vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=oCI8HS_Y-hsNhvGIC8HgbIzKIZeWOkXMLaknDRf-qus,2928
1596
1596
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1597
1597
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
1598
1598
  vellum/workflows/nodes/displayable/conftest.py,sha256=K2kLM2JGAfcrmmd92u8DXInUO5klFdggPWblg5RVcx4,5729
@@ -1628,7 +1628,7 @@ vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.p
1628
1628
  vellum/workflows/nodes/displayable/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1629
1629
  vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=LaxohBcKfSW2PSiBBlx67FdW_q4YC2BM2ouH-vuGPAA,4700
1630
1630
  vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
1631
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=sGlAzTgKz8OsqeT3e7FbQyPpvs_2Fk9_jfD6BRyc6M0,2628
1631
+ vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=dc3EEn1sOICpr3GdS8eyeFtExaGwWWcw9eHSdkRhQJU,2584
1632
1632
  vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrraiBHDLKTlnBa51ZiE,284
1633
1633
  vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1634
1634
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
@@ -1640,7 +1640,7 @@ vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWl
1640
1640
  vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1641
1641
  vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
1642
1642
  vellum/workflows/nodes/tests/test_utils.py,sha256=qNB6ApIsnFtE_9HDaEah9KD-zX8e10FhDixewS1uRcc,5199
1643
- vellum/workflows/nodes/utils.py,sha256=ziHJc5uU4foxIvacl0Vg7fEJJ0jiznqqhyvURVj0FsY,9005
1643
+ vellum/workflows/nodes/utils.py,sha256=j3I_qKlkU9WDplYbeija3-rC_KAKD4dyums1xROWFKQ,9193
1644
1644
  vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
1645
1645
  vellum/workflows/outputs/base.py,sha256=1OGHqBJVk7i8cW8uewFWOhIjuMlRRpzCDrGE30ZwDjw,8763
1646
1646
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
@@ -1676,6 +1676,7 @@ vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
1676
1676
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
1677
1677
  vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
1678
1678
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
1679
+ vellum/workflows/types/code_execution_node_wrappers.py,sha256=cQ1Se6fe1uqGUs8uVQxkk2OEVikcHbwTM0CyU2AsgqI,2010
1679
1680
  vellum/workflows/types/core.py,sha256=kMQremh_I8egXpiKmtMQbB6e3OczAWiRnnTq5V6xlD0,928
1680
1681
  vellum/workflows/types/definition.py,sha256=z81CL_u0FJol-9yUIqoXNTYAARtU8x__c6s-f4rb5c8,2335
1681
1682
  vellum/workflows/types/generics.py,sha256=tKXz0LwWJGKw1YGudyl9_yFDrRgU6yYV1yJV1Zv-LTw,1430
@@ -1693,15 +1694,15 @@ vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEW
1693
1694
  vellum/workflows/utils/tests/test_vellum_variables.py,sha256=maI5e7Od7UlpMwlrOrcdlXqnFhonkXGnWq8G2-YQLi8,1155
1694
1695
  vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrgTY,739
1695
1696
  vellum/workflows/utils/vellum_variables.py,sha256=UiGlUh0a8vel2FbW3w-xbHxSv_jNutkDdqMVtP_b42A,3385
1696
- vellum/workflows/vellum_client.py,sha256=GxOy3dX6A04xiY69vPv1S4YGuQ_TMxwHi6WRMimQBBE,762
1697
+ vellum/workflows/vellum_client.py,sha256=xkfoucodxNK5JR2-lbRqZx3xzDgExWkP6kySrpi_Ubc,1079
1697
1698
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1698
1699
  vellum/workflows/workflows/base.py,sha256=9dGZzoXbVVtw19pVhmawIA1wd1iLQttAKypuVFWb0fU,23793
1699
1700
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1700
1701
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1701
1702
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=8P5YIsNMO78_CR1NNK6wkEdkMB4b3Q_Ni1qxh78OnHo,20481
1702
1703
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1703
- vellum_ai-0.14.45.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1704
- vellum_ai-0.14.45.dist-info/METADATA,sha256=BfwTGG-Soly3Rh8Hqs0qan0I50KGBzN7o4hDZfMfdQo,5484
1705
- vellum_ai-0.14.45.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1706
- vellum_ai-0.14.45.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1707
- vellum_ai-0.14.45.dist-info/RECORD,,
1704
+ vellum_ai-0.14.46.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1705
+ vellum_ai-0.14.46.dist-info/METADATA,sha256=QVjFBBT5KT2PzAvAibMrRv8VD7GpQoUhjlXaR_H--Fw,5484
1706
+ vellum_ai-0.14.46.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1707
+ vellum_ai-0.14.46.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1708
+ vellum_ai-0.14.46.dist-info/RECORD,,
vellum_cli/config.py CHANGED
@@ -16,10 +16,15 @@ PYPROJECT_TOML_PATH = "pyproject.toml"
16
16
 
17
17
  class WorkspaceConfig(UniversalBaseModel):
18
18
  name: str
19
- api_key: str
19
+ api_key: str = "VELLUM_API_KEY"
20
+ api_url: Optional[str] = None
20
21
 
21
22
  def merge(self, other: "WorkspaceConfig") -> "WorkspaceConfig":
22
- return WorkspaceConfig(name=self.name or other.name, api_key=self.api_key or other.api_key)
23
+ return WorkspaceConfig(
24
+ name=self.name or other.name,
25
+ api_key=self.api_key or other.api_key,
26
+ api_url=self.api_url or other.api_url,
27
+ )
23
28
 
24
29
 
25
30
  DEFAULT_WORKSPACE_CONFIG = WorkspaceConfig(name="default", api_key="VELLUM_API_KEY")
vellum_cli/push.py CHANGED
@@ -97,6 +97,7 @@ def push_command(
97
97
 
98
98
  client = create_vellum_client(
99
99
  api_key=api_key,
100
+ api_url=workspace_config.api_url,
100
101
  )
101
102
  sys.path.insert(0, os.getcwd())
102
103
 
@@ -105,6 +106,7 @@ def push_command(
105
106
  workflow = BaseWorkflow.load_from_module(workflow_config.module)
106
107
  workflow_display = get_workflow_display(
107
108
  workflow_class=workflow,
109
+ client=client,
108
110
  dry_run=dry_run or False,
109
111
  )
110
112
  exec_config = workflow_display.serialize()
@@ -234,9 +236,11 @@ def push_command(
234
236
  """
235
237
  ) # type: ignore[attr-defined]
236
238
  else:
239
+ default_api_url = client._client_wrapper._environment.default
240
+ base_url = default_api_url.split("/v1")[0].replace("//api.", "//app.")
237
241
  logger.info(
238
242
  f"""Successfully pushed {workflow_config.module} to Vellum!
239
- Visit at: https://app.vellum.ai/workflow-sandboxes/{response.workflow_sandbox_id}"""
243
+ Visit at: {base_url}/workflow-sandboxes/{response.workflow_sandbox_id}"""
240
244
  )
241
245
 
242
246
  if not workflow_config.workflow_sandbox_id:
@@ -7,6 +7,7 @@ from unittest import mock
7
7
  from uuid import uuid4
8
8
 
9
9
  from click.testing import CliRunner
10
+ from httpx import Response
10
11
 
11
12
  from vellum.client.core.api_error import ApiError
12
13
  from vellum.client.types.workflow_push_response import WorkflowPushResponse
@@ -29,19 +30,25 @@ def _extract_tar_gz(tar_gz_bytes: bytes) -> dict[str, str]:
29
30
  return files
30
31
 
31
32
 
32
- def _ensure_workflow_py(temp_dir: str, module: str) -> str:
33
- base_dir = os.path.join(temp_dir, *module.split("."))
33
+ def _ensure_file(temp_dir: str, module: str, file_name: str, content: str) -> str:
34
+ file_path = os.path.join(temp_dir, *module.split("."), file_name)
35
+ base_dir = os.path.dirname(file_path)
34
36
  os.makedirs(base_dir, exist_ok=True)
37
+
38
+ with open(file_path, "w") as f:
39
+ f.write(content)
40
+
41
+ return content
42
+
43
+
44
+ def _ensure_workflow_py(temp_dir: str, module: str) -> str:
35
45
  workflow_py_file_content = """\
36
46
  from vellum.workflows import BaseWorkflow
37
47
 
38
48
  class ExampleWorkflow(BaseWorkflow):
39
49
  pass
40
50
  """
41
- with open(os.path.join(temp_dir, *module.split("."), "workflow.py"), "w") as f:
42
- f.write(workflow_py_file_content)
43
-
44
- return workflow_py_file_content
51
+ return _ensure_file(temp_dir, module, "workflow.py", workflow_py_file_content)
45
52
 
46
53
 
47
54
  def test_push__no_config(mock_module):
@@ -133,6 +140,38 @@ def test_push__happy_path(mock_module, vellum_client, base_command):
133
140
  assert extracted_files["workflow.py"] == workflow_py_file_content
134
141
 
135
142
 
143
+ def test_push__verify_default_url_in_raw_httpx_transport(mock_module, mock_httpx_transport):
144
+ # GIVEN a single workflow configured
145
+ module = mock_module.module
146
+ temp_dir = mock_module.temp_dir
147
+ _ensure_workflow_py(temp_dir, module)
148
+
149
+ # AND the push API call returns successfully
150
+ mock_httpx_transport.handle_request.return_value = Response(
151
+ status_code=200,
152
+ text=json.dumps(
153
+ {
154
+ "workflow_sandbox_id": str(uuid4()),
155
+ }
156
+ ),
157
+ )
158
+
159
+ # WHEN calling `vellum push`
160
+ runner = CliRunner()
161
+ result = runner.invoke(cli_main, ["workflows", "push", module])
162
+
163
+ # THEN it should succeed
164
+ assert result.exit_code == 0
165
+
166
+ # AND we should have called the push API with the correct args
167
+ mock_httpx_transport.handle_request.assert_called_once()
168
+ request = mock_httpx_transport.handle_request.call_args[0][0]
169
+ assert str(request.url) == "https://api.vellum.ai/v1/workflows/push"
170
+
171
+ # AND the new URL is in the message at the end
172
+ assert "Visit at: https://app.vellum.ai/workflow-sandboxes/" in result.output
173
+
174
+
136
175
  def test_push__no_config__module_found(mock_module, vellum_client):
137
176
  # GIVEN no config file set
138
177
  temp_dir = mock_module.temp_dir
@@ -486,7 +525,74 @@ Files that were different between the original project and the generated artifac
486
525
  )
487
526
 
488
527
 
489
- def test_push__workspace_option__uses_different_api_key(mock_module, vellum_client_class):
528
+ @pytest.mark.parametrize(
529
+ "file_data",
530
+ [
531
+ {
532
+ "workflow.py": """\
533
+ from vellum.workflows import BaseWorkflow
534
+
535
+ class ExampleWorkflow(BaseWorkflow):
536
+ pass
537
+ """
538
+ },
539
+ {
540
+ "nodes/start_node.py": """\
541
+ from vellum.workflows.nodes import CodeExecutionNode
542
+ from vellum.workflows.references import VellumSecretReference
543
+
544
+ class StartNode(CodeExecutionNode):
545
+ code_inputs = {
546
+ "foo": VellumSecretReference("MY_SECRET_KEY"),
547
+ }
548
+ """,
549
+ "workflow.py": """\
550
+ from vellum.workflows import BaseWorkflow
551
+ from .nodes.start_node import StartNode
552
+
553
+ class ExampleWorkflow(BaseWorkflow):
554
+ graph = StartNode
555
+ """,
556
+ },
557
+ {
558
+ "nodes/start_node.py": """\
559
+ from vellum.workflows.nodes import PromptDeploymentNode
560
+
561
+ class StartNode(PromptDeploymentNode):
562
+ deployment = "my-deployment"
563
+ """,
564
+ "workflow.py": """\
565
+ from vellum.workflows import BaseWorkflow
566
+ from .nodes.start_node import StartNode
567
+
568
+ class ExampleWorkflow(BaseWorkflow):
569
+ graph = StartNode
570
+ """,
571
+ },
572
+ {
573
+ "nodes/start_node.py": """\
574
+ from vellum.workflows.nodes import SubworkflowDeploymentNode
575
+
576
+ class StartNode(SubworkflowDeploymentNode):
577
+ deployment = "my-deployment"
578
+ """,
579
+ "workflow.py": """\
580
+ from vellum.workflows import BaseWorkflow
581
+ from .nodes.start_node import StartNode
582
+
583
+ class ExampleWorkflow(BaseWorkflow):
584
+ graph = StartNode
585
+ """,
586
+ },
587
+ ],
588
+ ids=[
589
+ "base_case",
590
+ "with_secret_reference",
591
+ "with_prompt_deployment",
592
+ "with_subworkflow_deployment",
593
+ ],
594
+ )
595
+ def test_push__workspace_option__uses_different_api_key(mock_module, vellum_client_class, file_data):
490
596
  # GIVEN a single workflow configured
491
597
  temp_dir = mock_module.temp_dir
492
598
  module = mock_module.module
@@ -521,7 +627,8 @@ MY_OTHER_VELLUM_API_KEY=aaabbbcccddd
521
627
  )
522
628
 
523
629
  # AND a workflow exists in the module successfully
524
- _ensure_workflow_py(temp_dir, module)
630
+ for file_name, content in file_data.items():
631
+ _ensure_file(temp_dir, module, file_name, content)
525
632
 
526
633
  # AND the push API call returns a new workflow sandbox id
527
634
  new_workflow_sandbox_id = str(uuid4())
@@ -564,6 +671,83 @@ MY_OTHER_VELLUM_API_KEY=aaabbbcccddd
564
671
  }
565
672
 
566
673
 
674
+ def test_push__workspace_option__uses_different_api_url_env(mock_module, mock_httpx_transport):
675
+ # GIVEN a single workflow configured
676
+ temp_dir = mock_module.temp_dir
677
+ module = mock_module.module
678
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
679
+ set_pyproject_toml = mock_module.set_pyproject_toml
680
+
681
+ # AND a different workspace is set in the pyproject.toml
682
+ set_pyproject_toml(
683
+ {
684
+ "workflows": [
685
+ {
686
+ "module": module,
687
+ "workflow_sandbox_id": workflow_sandbox_id,
688
+ }
689
+ ],
690
+ "workspaces": [
691
+ {
692
+ "name": "my_other_workspace",
693
+ "api_url": "MY_OTHER_VELLUM_API_URL",
694
+ }
695
+ ],
696
+ }
697
+ )
698
+
699
+ # AND the .env file has the other api key stored
700
+ with open(os.path.join(temp_dir, ".env"), "w") as f:
701
+ f.write(
702
+ """\
703
+ VELLUM_API_KEY=abcdef123456
704
+ MY_OTHER_VELLUM_API_URL=https://app.aws-vpc-staging.vellum.ai
705
+ """
706
+ )
707
+
708
+ # AND a workflow exists in the module successfully
709
+ _ensure_workflow_py(temp_dir, module)
710
+
711
+ # AND the push API call returns a new workflow sandbox id
712
+ new_workflow_sandbox_id = str(uuid4())
713
+ mock_httpx_transport.handle_request.return_value = Response(
714
+ status_code=200,
715
+ text=json.dumps(
716
+ {
717
+ "workflow_sandbox_id": new_workflow_sandbox_id,
718
+ }
719
+ ),
720
+ )
721
+
722
+ # WHEN calling `vellum push` on strict mode
723
+ runner = CliRunner()
724
+ result = runner.invoke(cli_main, ["push", module, "--workspace", "my_other_workspace"])
725
+
726
+ # THEN it should succeed
727
+ assert result.exit_code == 0, result.output
728
+
729
+ # AND we should have called the push API once with the correct api url
730
+ request = mock_httpx_transport.handle_request.call_args[0][0]
731
+ assert str(request.url) == "https://app.aws-vpc-staging.vellum.ai/v1/workflows/push"
732
+
733
+ # AND the vellum lock file should have been updated with the correct workspace
734
+ with open(os.path.join(temp_dir, "vellum.lock.json")) as f:
735
+ lock_file_content = json.load(f)
736
+ assert lock_file_content["workflows"][1] == {
737
+ "module": module,
738
+ "workflow_sandbox_id": new_workflow_sandbox_id,
739
+ "workspace": "my_other_workspace",
740
+ "container_image_name": None,
741
+ "container_image_tag": None,
742
+ "deployments": [],
743
+ "ignore": None,
744
+ "target_directory": None,
745
+ }
746
+
747
+ # AND the new URL is in the message at the end
748
+ assert "Visit at: https://app.aws-vpc-staging.vellum.ai/workflow-sandboxes/" in result.output
749
+
750
+
567
751
  def test_push__workspace_option__both_options_already_configured(mock_module, vellum_client_class):
568
752
  # GIVEN a single workflow configured
569
753
  temp_dir = mock_module.temp_dir
@@ -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
@@ -42,10 +41,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
42
41
  _, output_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.text)]
43
42
  _, array_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.results)]
44
43
 
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(
44
+ deployment = display_context.client.deployments.retrieve(
49
45
  id=str(raise_if_descriptor(node.deployment)),
50
46
  )
51
47
  ml_model_fallbacks = raise_if_descriptor(node.ml_model_fallbacks)
@@ -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
@@ -37,10 +36,7 @@ class BaseSubworkflowDeploymentNodeDisplay(
37
36
  for variable_name, variable_value in subworkflow_inputs.items()
38
37
  ]
39
38
 
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(
39
+ deployment = display_context.client.workflow_deployments.retrieve(
44
40
  id=str(raise_if_descriptor(node.deployment)),
45
41
  )
46
42
 
@@ -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
 
@@ -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
+ }