vellum-ai 0.13.27__py3-none-any.whl → 0.13.28__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 (31) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/descriptors/exceptions.py +2 -0
  3. vellum/workflows/expressions/accessor.py +4 -3
  4. vellum/workflows/expressions/begins_with.py +3 -2
  5. vellum/workflows/expressions/between.py +4 -3
  6. vellum/workflows/expressions/contains.py +8 -1
  7. vellum/workflows/expressions/does_not_begin_with.py +3 -2
  8. vellum/workflows/expressions/does_not_contain.py +4 -1
  9. vellum/workflows/expressions/does_not_end_with.py +3 -2
  10. vellum/workflows/expressions/ends_with.py +3 -2
  11. vellum/workflows/expressions/greater_than.py +3 -2
  12. vellum/workflows/expressions/greater_than_or_equal_to.py +3 -2
  13. vellum/workflows/expressions/in_.py +2 -1
  14. vellum/workflows/expressions/is_blank.py +2 -1
  15. vellum/workflows/expressions/is_not_blank.py +2 -1
  16. vellum/workflows/expressions/less_than.py +3 -2
  17. vellum/workflows/expressions/less_than_or_equal_to.py +3 -2
  18. vellum/workflows/expressions/not_between.py +4 -3
  19. vellum/workflows/expressions/not_in.py +2 -1
  20. vellum/workflows/nodes/bases/base.py +2 -4
  21. vellum/workflows/nodes/displayable/bases/api_node/node.py +8 -3
  22. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +19 -9
  23. vellum/workflows/ports/port.py +14 -5
  24. vellum/workflows/utils/vellum_variables.py +3 -0
  25. {vellum_ai-0.13.27.dist-info → vellum_ai-0.13.28.dist-info}/METADATA +2 -1
  26. {vellum_ai-0.13.27.dist-info → vellum_ai-0.13.28.dist-info}/RECORD +31 -30
  27. vellum_cli/push.py +15 -1
  28. vellum_cli/tests/test_push.py +44 -0
  29. {vellum_ai-0.13.27.dist-info → vellum_ai-0.13.28.dist-info}/LICENSE +0 -0
  30. {vellum_ai-0.13.27.dist-info → vellum_ai-0.13.28.dist-info}/WHEEL +0 -0
  31. {vellum_ai-0.13.27.dist-info → vellum_ai-0.13.28.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.13.27",
21
+ "X-Fern-SDK-Version": "0.13.28",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -0,0 +1,2 @@
1
+ class InvalidExpressionException(Exception):
2
+ pass
@@ -6,6 +6,7 @@ from pydantic import BaseModel, GetCoreSchemaHandler
6
6
  from pydantic_core import core_schema
7
7
 
8
8
  from vellum.workflows.descriptors.base import BaseDescriptor
9
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
9
10
  from vellum.workflows.descriptors.utils import resolve_value
10
11
  from vellum.workflows.state.base import BaseState
11
12
 
@@ -32,13 +33,13 @@ class AccessorExpression(BaseDescriptor[Any]):
32
33
 
33
34
  if dataclasses.is_dataclass(base):
34
35
  if isinstance(self._field, int):
35
- raise ValueError("Cannot access field by index on a dataclass")
36
+ raise InvalidExpressionException("Cannot access field by index on a dataclass")
36
37
 
37
38
  return getattr(base, self._field)
38
39
 
39
40
  if isinstance(base, BaseModel):
40
41
  if isinstance(self._field, int):
41
- raise ValueError("Cannot access field by index on a BaseModel")
42
+ raise InvalidExpressionException("Cannot access field by index on a BaseModel")
42
43
 
43
44
  return getattr(base, self._field)
44
45
 
@@ -49,7 +50,7 @@ class AccessorExpression(BaseDescriptor[Any]):
49
50
  index = int(self._field)
50
51
  return base[index]
51
52
 
52
- raise ValueError(f"Cannot get field {self._field} from {base}")
53
+ raise InvalidExpressionException(f"Cannot get field {self._field} from {base}")
53
54
 
54
55
  @classmethod
55
56
  def __get_pydantic_core_schema__(
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class BeginsWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return lhs.startswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -25,14 +26,14 @@ class BetweenExpression(BaseDescriptor[bool], Generic[_V, _S, _E]):
25
26
  def resolve(self, state: "BaseState") -> bool:
26
27
  value = resolve_value(self._value, state)
27
28
  if not isinstance(value, (int, float)):
28
- raise ValueError(f"Expected a numeric value, got: {value.__class__.__name__}")
29
+ raise InvalidExpressionException(f"Expected a numeric value, got: {value.__class__.__name__}")
29
30
 
30
31
  start = resolve_value(self._start, state)
31
32
  if not isinstance(start, (int, float)):
32
- raise ValueError(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
+ raise InvalidExpressionException(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
34
 
34
35
  end = resolve_value(self._end, state)
35
36
  if not isinstance(end, (int, float)):
36
- raise ValueError(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
+ raise InvalidExpressionException(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
38
 
38
39
  return start <= value <= end
@@ -1,6 +1,8 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
+ from vellum.workflows.constants import UNDEF
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
6
  from vellum.workflows.descriptors.utils import resolve_value
5
7
  from vellum.workflows.state.base import BaseState
6
8
 
@@ -23,8 +25,13 @@ class ContainsExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
25
  # Support any type that implements the in operator
24
26
  # https://app.shortcut.com/vellum/story/4658
25
27
  lhs = resolve_value(self._lhs, state)
28
+ # assumes that lack of is also false
29
+ if lhs is UNDEF:
30
+ return False
26
31
  if not isinstance(lhs, (list, tuple, set, dict, str)):
27
- raise ValueError(f"Expected a LHS that supported contains, got: {lhs.__class__.__name__}")
32
+ raise InvalidExpressionException(
33
+ f"Expected a LHS that supported `contains`, got `{lhs.__class__.__name__}`"
34
+ )
28
35
 
29
36
  rhs = resolve_value(self._rhs, state)
30
37
  return rhs in lhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class DoesNotBeginWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return not lhs.startswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,7 +25,9 @@ class DoesNotContainExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (list, tuple, set, dict, str)):
27
- raise ValueError(f"Expected a LHS that supported contains, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(
29
+ f"Expected a LHS that supported `contains`, got `{lhs.__class__.__name__}`"
30
+ )
28
31
 
29
32
  rhs = resolve_value(self._rhs, state)
30
33
  return rhs not in lhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class DoesNotEndWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return not lhs.endswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -23,9 +24,9 @@ class EndsWithExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
23
24
  lhs = resolve_value(self._lhs, state)
24
25
  rhs = resolve_value(self._rhs, state)
25
26
  if not isinstance(lhs, str):
26
- raise ValueError(f"Expected LHS to be a string, got {type(lhs)}")
27
+ raise InvalidExpressionException(f"Expected LHS to be a string, got {type(lhs)}")
27
28
 
28
29
  if not isinstance(rhs, str):
29
- raise ValueError(f"Expected RHS to be a string, got {type(rhs)}")
30
+ raise InvalidExpressionException(f"Expected RHS to be a string, got {type(rhs)}")
30
31
 
31
32
  return lhs.endswith(rhs)
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class GreaterThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs > rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs >= rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -26,6 +27,6 @@ class InExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
26
27
 
27
28
  rhs = resolve_value(self._rhs, state)
28
29
  if not isinstance(rhs, (list, tuple, set, dict, str)):
29
- raise ValueError(f"Expected a RHS that supported in, got: {rhs.__class__.__name__}")
30
+ raise InvalidExpressionException(f"Expected a RHS that supported in, got: {rhs.__class__.__name__}")
30
31
 
31
32
  return lhs in rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -19,6 +20,6 @@ class IsBlankExpression(BaseDescriptor[bool], Generic[_T]):
19
20
  def resolve(self, state: "BaseState") -> bool:
20
21
  expression = resolve_value(self._expression, state)
21
22
  if not isinstance(expression, str):
22
- raise ValueError(f"Expected a string expression, got: {expression.__class__.__name__}")
23
+ raise InvalidExpressionException(f"Expected a string expression, got: {expression.__class__.__name__}")
23
24
 
24
25
  return len(expression) == 0
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -19,6 +20,6 @@ class IsNotBlankExpression(BaseDescriptor[bool], Generic[_T]):
19
20
  def resolve(self, state: "BaseState") -> bool:
20
21
  expression = resolve_value(self._expression, state)
21
22
  if not isinstance(expression, str):
22
- raise ValueError(f"Expected a string expression, got: {expression.__class__.__name__}")
23
+ raise InvalidExpressionException(f"Expected a string expression, got: {expression.__class__.__name__}")
23
24
 
24
25
  return len(expression) != 0
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs < rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -24,10 +25,10 @@ class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
24
25
  # https://app.shortcut.com/vellum/story/4658
25
26
  lhs = resolve_value(self._lhs, state)
26
27
  if not isinstance(lhs, (int, float)):
27
- raise ValueError(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
+ raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
28
29
 
29
30
  rhs = resolve_value(self._rhs, state)
30
31
  if not isinstance(rhs, (int, float)):
31
- raise ValueError(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
+ raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
32
33
 
33
34
  return lhs <= rhs
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -25,14 +26,14 @@ class NotBetweenExpression(BaseDescriptor[bool], Generic[_V, _S, _E]):
25
26
  def resolve(self, state: "BaseState") -> bool:
26
27
  value = resolve_value(self._value, state)
27
28
  if not isinstance(value, (int, float)):
28
- raise ValueError(f"Expected a numeric value, got: {value.__class__.__name__}")
29
+ raise InvalidExpressionException(f"Expected a numeric value, got: {value.__class__.__name__}")
29
30
 
30
31
  start = resolve_value(self._start, state)
31
32
  if not isinstance(start, (int, float)):
32
- raise ValueError(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
+ raise InvalidExpressionException(f"Expected a numeric start value, got: {start.__class__.__name__}")
33
34
 
34
35
  end = resolve_value(self._end, state)
35
36
  if not isinstance(end, (int, float)):
36
- raise ValueError(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
+ raise InvalidExpressionException(f"Expected a numeric end value, got: {end.__class__.__name__}")
37
38
 
38
39
  return value < start or value > end
@@ -1,6 +1,7 @@
1
1
  from typing import Generic, TypeVar, Union
2
2
 
3
3
  from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
4
5
  from vellum.workflows.descriptors.utils import resolve_value
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
@@ -26,6 +27,6 @@ class NotInExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
26
27
 
27
28
  rhs = resolve_value(self._rhs, state)
28
29
  if not isinstance(rhs, (list, tuple, set, dict, str)):
29
- raise ValueError(f"Expected a RHS that supported contains, got: {rhs.__class__.__name__}")
30
+ raise InvalidExpressionException(f"Expected a RHS that supported contains, got: {rhs.__class__.__name__}")
30
31
 
31
32
  return lhs not in rhs
@@ -255,17 +255,15 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
255
255
  return False
256
256
 
257
257
  if cls.merge_behavior == MergeBehavior.AWAIT_ATTRIBUTES:
258
- is_ready = True
259
258
  for descriptor in cls.node_class:
260
259
  if not descriptor.instance:
261
260
  continue
262
261
 
263
262
  resolved_value = resolve_value(descriptor.instance, state, path=descriptor.name)
264
263
  if is_unresolved(resolved_value):
265
- is_ready = False
266
- break
264
+ return False
267
265
 
268
- return is_ready
266
+ return True
269
267
 
270
268
  if cls.merge_behavior == MergeBehavior.AWAIT_ANY:
271
269
  return True
@@ -3,6 +3,7 @@ from typing import Any, Dict, Generic, Optional, Union
3
3
  from requests import Request, RequestException, Session
4
4
  from requests.exceptions import JSONDecodeError
5
5
 
6
+ from vellum.client import ApiError
6
7
  from vellum.client.types.vellum_secret import VellumSecret as ClientVellumSecret
7
8
  from vellum.workflows.constants import APIRequestMethod
8
9
  from vellum.workflows.errors.types import WorkflowErrorCode
@@ -83,9 +84,13 @@ class BaseAPINode(BaseNode, Generic[StateType]):
83
84
 
84
85
  def _vellum_execute_api(self, bearer_token, data, headers, method, url):
85
86
  client_vellum_secret = ClientVellumSecret(name=bearer_token.name) if bearer_token else None
86
- vellum_response = self._context.vellum_client.execute_api(
87
- url=url, method=method.value, body=data, headers=headers, bearer_token=client_vellum_secret
88
- )
87
+ try:
88
+ vellum_response = self._context.vellum_client.execute_api(
89
+ url=url, method=method.value, body=data, headers=headers, bearer_token=client_vellum_secret
90
+ )
91
+ except ApiError as e:
92
+ NodeException(f"Failed to prepare HTTP request: {e}", code=WorkflowErrorCode.NODE_EXECUTION)
93
+
89
94
  return self.Outputs(
90
95
  json=vellum_response.json_,
91
96
  headers={header: value for header, value in vellum_response.headers.items()},
@@ -40,16 +40,14 @@ class BasePromptNode(BaseNode, Generic[StateType]):
40
40
  try:
41
41
  prompt_event_stream = self._get_prompt_event_stream()
42
42
  except ApiError as e:
43
- if e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
44
- raise NodeException(
45
- message=e.body.get("detail", "Failed to execute prompt"),
46
- code=WorkflowErrorCode.INVALID_INPUTS,
47
- ) from e
43
+ self._handle_api_error(e)
48
44
 
49
- raise NodeException(
50
- message="Failed to execute prompt",
51
- code=WorkflowErrorCode.INTERNAL_ERROR,
52
- ) from e
45
+ # We don't use the INITIATED event anyway, so we can just skip it
46
+ # and use the exception handling to catch other api level errors
47
+ try:
48
+ next(prompt_event_stream)
49
+ except ApiError as e:
50
+ self._handle_api_error(e)
53
51
 
54
52
  outputs: Optional[List[PromptOutput]] = None
55
53
  for event in prompt_event_stream:
@@ -65,3 +63,15 @@ class BasePromptNode(BaseNode, Generic[StateType]):
65
63
  raise NodeException.of(workflow_error)
66
64
 
67
65
  return outputs
66
+
67
+ def _handle_api_error(self, e: ApiError):
68
+ if e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
69
+ raise NodeException(
70
+ message=e.body.get("detail", "Failed to execute prompt"),
71
+ code=WorkflowErrorCode.INVALID_INPUTS,
72
+ ) from e
73
+
74
+ raise NodeException(
75
+ message="Failed to execute prompt",
76
+ code=WorkflowErrorCode.INTERNAL_ERROR,
77
+ ) from e
@@ -4,7 +4,10 @@ from pydantic import GetCoreSchemaHandler
4
4
  from pydantic_core import core_schema
5
5
 
6
6
  from vellum.workflows.descriptors.base import BaseDescriptor
7
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
7
8
  from vellum.workflows.edges.edge import Edge
9
+ from vellum.workflows.errors.types import WorkflowErrorCode
10
+ from vellum.workflows.exceptions import NodeException
8
11
  from vellum.workflows.graph import Graph, GraphTarget
9
12
  from vellum.workflows.state.base import BaseState
10
13
  from vellum.workflows.types.core import ConditionType
@@ -82,11 +85,17 @@ class Port:
82
85
  return Port(condition_type=ConditionType.ELSE, fork_state=fork_state)
83
86
 
84
87
  def resolve_condition(self, state: BaseState) -> bool:
85
- if self._condition is None:
86
- return False
87
-
88
- value = self._condition.resolve(state)
89
- return bool(value)
88
+ try:
89
+ if self._condition is None:
90
+ return False
91
+
92
+ value = self._condition.resolve(state)
93
+ return bool(value)
94
+ except InvalidExpressionException as e:
95
+ raise NodeException(
96
+ message=f"Failed to resolve condition for port `{self.name}`: {e}",
97
+ code=WorkflowErrorCode.INVALID_INPUTS,
98
+ ) from e
90
99
 
91
100
  def serialize(self) -> dict:
92
101
  return {
@@ -45,6 +45,9 @@ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -
45
45
  ]
46
46
  if types == actual_types_with_explicit_ref:
47
47
  return "JSON"
48
+ # Number now supports float and int
49
+ elif types == [float, int]:
50
+ return "NUMBER"
48
51
  raise ValueError(f"Expected Descriptor to only have one type, got {types}")
49
52
 
50
53
  type_ = type_.types[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.13.27
3
+ Version: 0.13.28
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -37,6 +37,7 @@ Requires-Dist: pyyaml (==6.0.1)
37
37
  Requires-Dist: requests (==2.32.3)
38
38
  Requires-Dist: tomli (==2.0.2)
39
39
  Requires-Dist: typing_extensions (>=4.0.0)
40
+ Project-URL: Repository, https://github.com/vellum-ai/vellum-python-sdks
40
41
  Description-Content-Type: text/markdown
41
42
 
42
43
  <p align="center">
@@ -7,14 +7,14 @@ vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
7
7
  vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
8
8
  vellum_cli/ping.py,sha256=lWyJw6sziXjyTopTYRdFF5hV-sYPVDdX0yVbG5fzcY4,585
9
9
  vellum_cli/pull.py,sha256=7yvg4oBOgsbBEsgXtCpYlNR4AOR8hPICamY-4HI-3kM,9031
10
- vellum_cli/push.py,sha256=j4XLSircGX3aHz35i3HyJ4jynWiEH6xVm1uspavPeLM,9101
10
+ vellum_cli/push.py,sha256=xjTNbLwOVFNU3kpBrm56Bk5QkSRrJ9z86qceghCzfIA,9655
11
11
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
13
13
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
14
14
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
15
15
  vellum_cli/tests/test_ping.py,sha256=QtbhYKMYn1DFnDyBij2mkQO32j9KOpZ5Pf0yek7k_Ao,1284
16
16
  vellum_cli/tests/test_pull.py,sha256=JURmgGs5lSnpzefSx4K13eF2swv7O8OF86-4df81Zjo,25241
17
- vellum_cli/tests/test_push.py,sha256=e0nJlyRQjfRPjXlHm9uh5Z5eGZMzu1lnpEhO35nGlsc,21330
17
+ vellum_cli/tests/test_push.py,sha256=zDZfSQCHCdKqSfGVHGRgX9VPm-H7EW5gwMf55dm_PFg,23438
18
18
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -116,7 +116,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
116
116
  vellum/client/__init__.py,sha256=j6zi0NZ4BMC6JrwckvzMWuG5x8KoOvO4KqsLhvVCa68,117624
117
117
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
118
118
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
119
- vellum/client/core/client_wrapper.py,sha256=ov-BIscDOkAsVlrwx5oFTC_b2ka0l1D__GsoKPKFG5w,1869
119
+ vellum/client/core/client_wrapper.py,sha256=K05OPis1DlTJSg2W24og49fZhiG0aW5aExROjEOtIAA,1869
120
120
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
121
121
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
122
122
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -1269,6 +1269,7 @@ vellum/workflows/constants.py,sha256=Z0W4YlqfSlSgWC11PrVUPs6ZOBeIaQ78E_90J1hohiw
1269
1269
  vellum/workflows/context.py,sha256=R8qdsFbD_0p7B6PWnyvSrZ_aOgMtGw-_uk0P0UAmwLA,1230
1270
1270
  vellum/workflows/descriptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1271
1271
  vellum/workflows/descriptors/base.py,sha256=gSib3vJpcI_UC8y8jhdp-hOK3_TGTF-SuwdhxF6x5iQ,14332
1272
+ vellum/workflows/descriptors/exceptions.py,sha256=gUy4UD9JFUKSeQnQpeuDSLiRqWjWiIsxLahB7p_q3JY,54
1272
1273
  vellum/workflows/descriptors/tests/test_utils.py,sha256=63w1P6PELNhWzpg1BqL8y2XQZ8XRt0RUkHgLcsXyOQE,4592
1273
1274
  vellum/workflows/descriptors/utils.py,sha256=nE51MR0KMy3SVHPBg0wjJ3CSJBTLM3tZnVUC_enQEIw,3826
1274
1275
  vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR_pUus,50
@@ -1287,33 +1288,33 @@ vellum/workflows/events/types.py,sha256=cjRE8WL8tYCFradd9NOGl_H0mN3LiWWnA1uHmyT2
1287
1288
  vellum/workflows/events/workflow.py,sha256=lveoWXtVRLjdO_nu0z6VlKeTqlGimogamiFR-jYihlE,5184
1288
1289
  vellum/workflows/exceptions.py,sha256=l-FLGvXywxg6ivolCts71b8pcsYAWoB1cmUR4Jx7N8g,614
1289
1290
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1290
- vellum/workflows/expressions/accessor.py,sha256=daIkEudAdyiqCOzO9x_3cy-Y9ilYgNWSEeqcFelkjI0,1743
1291
+ vellum/workflows/expressions/accessor.py,sha256=ItZF7fMLzVTqsdAiaXb5SiDupXmX0X9xbIus1W6hRds,1870
1291
1292
  vellum/workflows/expressions/and_.py,sha256=I7lNqrUM3-m_5hmjjiMhaHhJtKcLj39kEFVWPDOqwfo,916
1292
- vellum/workflows/expressions/begins_with.py,sha256=Ae-RBz_0Xr1WiX1EP0mSgCOOUo-ji1A85aAGd-4BscY,1012
1293
- vellum/workflows/expressions/between.py,sha256=xjc_-TLDN-25tVQ9k9aZciH0wOfqrXV8qNmyGOhimd8,1365
1293
+ vellum/workflows/expressions/begins_with.py,sha256=FnWsQXbENm0ZwkfEP7dR8Qx4_MMrzj6C1yqAV2KaNHw,1123
1294
+ vellum/workflows/expressions/between.py,sha256=dVeddT6YA91eOAlE1Utg7C7gnCiYE7WP-dg17yXUeAY,1492
1294
1295
  vellum/workflows/expressions/coalesce_expression.py,sha256=Fyx2cvi715hPhi2OEfH3D0gD_smQIa2aDwnjr7q2Ngs,1186
1295
- vellum/workflows/expressions/contains.py,sha256=j8iKPxytSC0Ig0e-Ie_1vTZ9kFG-4mS1SZ4L0fUP3K4,1045
1296
- vellum/workflows/expressions/does_not_begin_with.py,sha256=CERIru9CGJWGXWmT5QyVC7wbZBJiZu3qNR-cyrJzzec,1026
1297
- vellum/workflows/expressions/does_not_contain.py,sha256=Z7_v_j9VlUFiZo8nFfqaSj7eAYy7EHiIILceS8F2ioE,1067
1298
- vellum/workflows/expressions/does_not_end_with.py,sha256=WB34F0mEtCtKBQ0hliNVj56vdD_ucWxgjyJSH-pKMkI,1020
1296
+ vellum/workflows/expressions/contains.py,sha256=gYpQQlsY8YxBWfisfCI4I_mc-IW7f6U4ZTpuN3WZtMA,1313
1297
+ vellum/workflows/expressions/does_not_begin_with.py,sha256=qcnIJsxg4Jt82i2L-PW6ZhKP3C-OlEiXbiIgwHQc5RE,1137
1298
+ vellum/workflows/expressions/does_not_contain.py,sha256=B4m23jJiKBM6ANVtA4WfdUnT-MdQGxVVgR14ypgYBIs,1195
1299
+ vellum/workflows/expressions/does_not_end_with.py,sha256=idw2OSuIk02XwBM5CXYOESf655mRrdBREjeinZ5tmVQ,1131
1299
1300
  vellum/workflows/expressions/does_not_equal.py,sha256=LNiCibRZZIxaIrwotjW3SIsKYHWR7BpOItFI-x5KuKU,764
1300
- vellum/workflows/expressions/ends_with.py,sha256=qrnCOfBl-66Hgdz0d_EEej_Md9EMzIYP9NeZBpIP3y0,1006
1301
+ vellum/workflows/expressions/ends_with.py,sha256=FkWZjAudc_DFM-fK-A3_tr6WXavfs_Qi7F6JtVgMglw,1117
1301
1302
  vellum/workflows/expressions/equals.py,sha256=gbI6BKQR7cLQih226-ge_BFSLprgEjqbdiIxo7WFg_E,758
1302
- vellum/workflows/expressions/greater_than.py,sha256=nMi6iAo0vi2hM3JeKfQSCy7DnXbdoyueGEZpGCRImH8,1152
1303
- vellum/workflows/expressions/greater_than_or_equal_to.py,sha256=xUDjfsOrJdoWm9K7OafGDGsuyvYS3xbMjyV4umvA50Q,1164
1304
- vellum/workflows/expressions/in_.py,sha256=tdDTvePdG4WyRkwmSaA4hsUdFJNiIMlrEG6oRrUskjI,1028
1305
- vellum/workflows/expressions/is_blank.py,sha256=u8mGreoZb5t_q2mhhmpD7ytAfFCFUAW9APsDapqUsDY,809
1303
+ vellum/workflows/expressions/greater_than.py,sha256=fTM2YF3FY_eOkRtGpgVWcv8qNt4K3lazONUIqcD8oe8,1263
1304
+ vellum/workflows/expressions/greater_than_or_equal_to.py,sha256=sazL_-Jsgjo4jcQeE-6EC0BFHZACriaYMQH4rn0dqEo,1275
1305
+ vellum/workflows/expressions/in_.py,sha256=RgiAIFntXGN4eWoOVBj1gqLymnBxSiw5hYD3TngF3dk,1123
1306
+ vellum/workflows/expressions/is_blank.py,sha256=vOOmK5poXmiNRVH7MR0feIFnL4rwKn7vmmTkJ9TcfVU,904
1306
1307
  vellum/workflows/expressions/is_nil.py,sha256=dtgY9Czm3slk45weARspwtfhQmVh0BIUsPOECrATLrA,740
1307
- vellum/workflows/expressions/is_not_blank.py,sha256=npXK9KSUm0nWeT6WJ5LLKB3owgBXHr7SpH8j4W0WapI,816
1308
+ vellum/workflows/expressions/is_not_blank.py,sha256=GJNTe8TKIbh4RwWPFuPwEQw0hbxg2MobHg8bcal4xWU,911
1308
1309
  vellum/workflows/expressions/is_not_nil.py,sha256=M5Qhtp_H07PORjFN2WapwA1Njp_KaANmLWbfckSSscM,761
1309
1310
  vellum/workflows/expressions/is_not_null.py,sha256=EoHXFgZScKP_BM2a5Z7YFQN6l7RMEtzs5x5nlvaSST8,671
1310
1311
  vellum/workflows/expressions/is_not_undefined.py,sha256=8NGwA0wZP_aHRy5qJOtaNhRCJyXKekwBNJalSk6Rdmo,727
1311
1312
  vellum/workflows/expressions/is_null.py,sha256=C75ALGlG_sTGcxI46tm9HtgPVfJ7DwTIyKzX8qtEiDU,660
1312
1313
  vellum/workflows/expressions/is_undefined.py,sha256=c9Oc1fdp911fQQ8WMG2L-TeUSqz1wF8cOTuLutJKbe8,716
1313
- vellum/workflows/expressions/less_than.py,sha256=DTWjbhaegnsCWnFSLNldTQr94Dqg3rqWOgMWI7IVsII,1149
1314
- vellum/workflows/expressions/less_than_or_equal_to.py,sha256=IxP1VeXAzXrccdIWT2hm6Fj3Sr-7rPW-DKOHl6MrHjo,1161
1315
- vellum/workflows/expressions/not_between.py,sha256=H2huRR95D9Qb7lCHmK7BcK-Ug-E1gA3sMWz9PGcAVZs,1379
1316
- vellum/workflows/expressions/not_in.py,sha256=OQkN5G1E6VoTDpoLvx7X3GbohLlqEAYHV0rVVUV7ow4,1049
1314
+ vellum/workflows/expressions/less_than.py,sha256=BcfkUH6Bb2inwR8jILn1hebUiyC74foVijBA-JymwT0,1260
1315
+ vellum/workflows/expressions/less_than_or_equal_to.py,sha256=4i1FR6FzlKam29cZPPnXUqACslO242Ww-wZZY4CEK6A,1272
1316
+ vellum/workflows/expressions/not_between.py,sha256=ZtRJeJDSSlOvajL8YoBoh5o_khjIn9xSSeQCnXYbHFE,1506
1317
+ vellum/workflows/expressions/not_in.py,sha256=pFvwkFPsn3WJw61ssFgM2U1dqWEeglfz4FVT4xwm5Mc,1144
1317
1318
  vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
1318
1319
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
1319
1320
  vellum/workflows/graph/graph.py,sha256=GGR8cGpSuNWPIiTWNWsj6l70upb5nIxAyFcn7VdaJIs,5506
@@ -1324,7 +1325,7 @@ vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOr
1324
1325
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1325
1326
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1326
1327
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1327
- vellum/workflows/nodes/bases/base.py,sha256=v6Y38xRTTKQxfvDpnx7X37EQN6Mrxu2vKtMDQiR2g8A,14186
1328
+ vellum/workflows/nodes/bases/base.py,sha256=jKgneygDa47BkuApl8VgvYItT3DMFuaqO3RetJwqPdg,14116
1328
1329
  vellum/workflows/nodes/bases/base_adornment_node.py,sha256=eFTgsPCYb3eyGS0-kw7C6crFnwFx437R5wh9-8bWYts,2905
1329
1330
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1330
1331
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=51CueFVty9XYASC0rKr1cXWejho5WElmhfhp6cCONy0,3811
@@ -1357,9 +1358,9 @@ vellum/workflows/nodes/displayable/api_node/tests/__init__.py,sha256=47DEQpj8HBS
1357
1358
  vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=yo3zTMRxgpSdWmJ68X610A5rrtCchyfqhcDd2X-GJiU,1249
1358
1359
  vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
1359
1360
  vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
1360
- vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=nIFkP7eeXXSzOVUoQgjPPCzy3_P083X_F5fCm2OAjdU,3798
1361
+ vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=TeVsAhUPEx_lbiyhGcWarqBKZeJJZAJx8mwym9qhwVs,3994
1361
1362
  vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
1362
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=r_gOmeGFQQfzhAc1_bmzcwUvH-Xllc93gE7miTV4rQE,2824
1363
+ vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=cKsNziwWJ9jUjS578I5dIka-zmsXz94hYZR3Cofm8aE,3140
1363
1364
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1364
1365
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
1365
1366
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=UrmI8NkNJFGq9f59gD12S1e8D_R3ROHc934JmtfQk9I,6841
@@ -1416,7 +1417,7 @@ vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfv
1416
1417
  vellum/workflows/outputs/base.py,sha256=Wu48tqyQoxpflBUcwzKeZjSVp1LPKrBwuIvnx__9H90,8459
1417
1418
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
1418
1419
  vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4rP2Kq4Aw,2743
1419
- vellum/workflows/ports/port.py,sha256=rc3GB7dDQCUs0IbY08a92-31YzJHQgBeww13brSJ2Js,3172
1420
+ vellum/workflows/ports/port.py,sha256=eI2SOZPZ5rsC3jMsxW6Rbn0NpaYQsrR7AapiIbbiy8Q,3635
1420
1421
  vellum/workflows/ports/utils.py,sha256=pEjVNJKw9LhD_cFN-o0MWBOW2ejno7jv26qqzjLxwS4,1662
1421
1422
  vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
1422
1423
  vellum/workflows/references/constant.py,sha256=6yUT4q1sMj1hkI_tzzQ9AYcmeeDYFUNCqUq_W2DN0S8,540
@@ -1460,13 +1461,13 @@ vellum/workflows/utils/tests/test_names.py,sha256=aOqpyvMsOEK_9mg_-yaNxQDW7QQfwq
1460
1461
  vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEWCMBn1HJ3DObrE,437
1461
1462
  vellum/workflows/utils/tests/test_vellum_variables.py,sha256=6H-BpmbIEmVRO75QQ3Rfy4bEUMMP2qwGzx2Gp1uXbfw,879
1462
1463
  vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrgTY,739
1463
- vellum/workflows/utils/vellum_variables.py,sha256=g5xHYB8etfHE32ek19nP6Anf8NyjhmUtOwO2KmQ5xZU,3111
1464
+ vellum/workflows/utils/vellum_variables.py,sha256=fC2aSLvlS31D15dOWu43LBRR0QsgUKNXBiCUvvaLXSs,3231
1464
1465
  vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528t75s,683
1465
1466
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1466
1467
  vellum/workflows/workflows/base.py,sha256=uYT0TQnEDtVaH3pErq785FhxxEEmk7C5ZGfuSO3QK8c,18537
1467
1468
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1468
- vellum_ai-0.13.27.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1469
- vellum_ai-0.13.27.dist-info/METADATA,sha256=vsSAQXzNF0pgVlHW2s8G6S4tfzSjLEOplZ9JR0BIR9I,5335
1470
- vellum_ai-0.13.27.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1471
- vellum_ai-0.13.27.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1472
- vellum_ai-0.13.27.dist-info/RECORD,,
1469
+ vellum_ai-0.13.28.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1470
+ vellum_ai-0.13.28.dist-info/METADATA,sha256=wrTghMo3kpO_xtsK1SbioWmx3p_ZdG0vifuj57JrZvs,5408
1471
+ vellum_ai-0.13.28.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1472
+ vellum_ai-0.13.28.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1473
+ vellum_ai-0.13.28.dist-info/RECORD,,
vellum_cli/push.py CHANGED
@@ -53,7 +53,16 @@ def push_command(
53
53
  workflow_configs = [w for w in workflow_configs if w.workspace == workspace]
54
54
 
55
55
  if len(workflow_configs) == 0:
56
- raise ValueError(f"No workflow config for '{module}' found in project to push.")
56
+ if module and module_exists(module):
57
+ new_config = WorkflowConfig(
58
+ module=module,
59
+ workflow_sandbox_id=workflow_sandbox_id,
60
+ workspace=workspace or DEFAULT_WORKSPACE_CONFIG.name,
61
+ )
62
+ config.workflows.append(new_config)
63
+ workflow_configs = [new_config]
64
+ else:
65
+ raise ValueError(f"No workflow config for '{module}' found in project to push.")
57
66
 
58
67
  if len(workflow_configs) > 1:
59
68
  raise ValueError("Multiple workflows found in project to push. Pushing only a single workflow is supported.")
@@ -241,3 +250,8 @@ Visit at: https://app.vellum.ai/workflow-sandboxes/{response.workflow_sandbox_id
241
250
 
242
251
  config.save()
243
252
  logger.info("Updated vellum.lock.json file.")
253
+
254
+
255
+ def module_exists(module_name: str) -> bool:
256
+ module_path = os.path.join(os.getcwd(), *module_name.split("."))
257
+ return os.path.exists(module_path) and os.path.isdir(module_path)
@@ -589,3 +589,47 @@ MY_OTHER_VELLUM_API_KEY=aaabbbcccddd
589
589
  assert lock_file_content["workflows"][1]["module"] == module
590
590
  assert lock_file_content["workflows"][1]["workflow_sandbox_id"] == second_workflow_sandbox_id
591
591
  assert lock_file_content["workflows"][1]["workspace"] == "my_other_workspace"
592
+
593
+
594
+ def test_push__create_new_config_for_existing_module(mock_module, vellum_client):
595
+ # GIVEN an empty config (no workflows configured)
596
+ temp_dir = mock_module.temp_dir
597
+ module = mock_module.module
598
+ # GIVEN multiple workflows configured
599
+ mock_module.set_pyproject_toml({"workflows": [{"module": "examples.mock"}, {"module": "examples.mock2"}]})
600
+
601
+ # AND a workflow exists in the module successfully
602
+ workflow_py_file_content = _ensure_workflow_py(temp_dir, module)
603
+
604
+ # AND the push API call returns successfully
605
+ new_workflow_sandbox_id = str(uuid4())
606
+ vellum_client.workflows.push.return_value = WorkflowPushResponse(
607
+ workflow_sandbox_id=new_workflow_sandbox_id,
608
+ )
609
+
610
+ # WHEN calling `vellum push` with a module that exists but isn't in config
611
+ runner = CliRunner()
612
+ result = runner.invoke(cli_main, ["workflows", "push", module])
613
+
614
+ # THEN it should succeed
615
+ assert result.exit_code == 0, result.output
616
+
617
+ # AND we should have called the push API with the correct args
618
+ vellum_client.workflows.push.assert_called_once()
619
+ call_args = vellum_client.workflows.push.call_args.kwargs
620
+ assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
621
+ assert call_args["workflow_sandbox_id"] is None # Should be None since it's a new config
622
+ assert call_args["artifact"].name == f"{module.replace('.', '__')}.tar.gz"
623
+
624
+ # AND the files in the artifact should be correct
625
+ extracted_files = _extract_tar_gz(call_args["artifact"].read())
626
+ assert extracted_files["workflow.py"] == workflow_py_file_content
627
+
628
+ # AND check that lockfile was updated with new config
629
+ with open(os.path.join(temp_dir, "vellum.lock.json")) as f:
630
+ lock_file_content = json.load(f)
631
+ new_configs = [w for w in lock_file_content["workflows"] if w["module"] == module]
632
+ assert len(new_configs) == 1 # Should only create one config
633
+ new_config = new_configs[0]
634
+ assert new_config["workflow_sandbox_id"] == new_workflow_sandbox_id
635
+ assert new_config["workspace"] == "default"