vellum-ai 0.12.16__py3-none-any.whl → 0.12.17__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.12.16",
21
+ "X-Fern-SDK-Version": "0.12.17",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -4,6 +4,7 @@ from typing import Callable, Dict, List, Optional, Set, Type
4
4
  from mypy.nodes import (
5
5
  AssignmentStmt,
6
6
  CallExpr,
7
+ ClassDef,
7
8
  Decorator,
8
9
  MemberExpr,
9
10
  NameExpr,
@@ -198,13 +199,27 @@ class VellumMypyPlugin(Plugin):
198
199
  return
199
200
 
200
201
  current_node_outputs = node_info.names.get("Outputs")
201
- if not current_node_outputs:
202
+ if not current_node_outputs and isinstance(base_node_outputs.node, TypeInfo):
202
203
  node_info.names["Outputs"] = base_node_outputs.copy()
203
- new_outputs_sym = node_info.names["Outputs"].node
204
- if isinstance(new_outputs_sym, TypeInfo):
205
- result_sym = new_outputs_sym.names[attribute_name].node
206
- if isinstance(result_sym, Var):
207
- result_sym.type = base_node_resolved_type
204
+
205
+ new_outputs_fullname = f"{node_info.fullname}.Outputs"
206
+ new_outputs_defn_raw = base_node_outputs.node.defn.serialize()
207
+ new_outputs_defn_raw["fullname"] = new_outputs_fullname
208
+ new_outputs_defn = ClassDef.deserialize(new_outputs_defn_raw)
209
+ new_outputs_sym = TypeInfo(
210
+ names=base_node_outputs.node.names.copy(),
211
+ defn=new_outputs_defn,
212
+ module_name=node_info.module_name,
213
+ )
214
+
215
+ base_result_sym = base_node_outputs.node.names[attribute_name].node
216
+ if isinstance(base_result_sym, Var):
217
+ new_result_sym = Var.deserialize(base_result_sym.serialize())
218
+ new_result_sym._fullname = f"{new_outputs_fullname}.{attribute_name}"
219
+ new_result_sym.type = base_node_resolved_type
220
+ new_outputs_sym.names[attribute_name].node = new_result_sym
221
+ new_outputs_sym.bases.append(Instance(base_node_outputs.node, []))
222
+ node_info.names["Outputs"].node = new_outputs_sym
208
223
 
209
224
  def _base_node_class_hook(self, ctx: ClassDefContext) -> None:
210
225
  """
@@ -472,10 +487,22 @@ class VellumMypyPlugin(Plugin):
472
487
 
473
488
  def _run_method_hook(self, ctx: MethodContext) -> MypyType:
474
489
  """
475
- We use this to target `Workflow.run()` so that the WorkflowExecutionFulfilledEvent is properly typed
476
- using the `Outputs` class defined on the user-defined subclass of `Workflow`.
490
+ We use this to target:
491
+ - `BaseWorkflow.run()`, so that the WorkflowExecutionFulfilledEvent is properly typed
492
+ using the `Outputs` class defined on the user-defined subclass of `Workflow`.
493
+ - `BaseNode.run()`, so that the `Outputs` class defined on the user-defined subclass of `Node`
494
+ is properly typed.
477
495
  """
478
496
 
497
+ if isinstance(ctx.default_return_type, TypeAliasType):
498
+ return self._workflow_run_method_hook(ctx)
499
+
500
+ if isinstance(ctx.default_return_type, Instance):
501
+ return self._node_run_method_hook(ctx)
502
+
503
+ return ctx.default_return_type
504
+
505
+ def _workflow_run_method_hook(self, ctx: MethodContext) -> MypyType:
479
506
  if not isinstance(ctx.default_return_type, TypeAliasType):
480
507
  return ctx.default_return_type
481
508
 
@@ -494,7 +521,7 @@ class VellumMypyPlugin(Plugin):
494
521
  if fulfilled_event.type.fullname != "vellum.workflows.events.workflow.WorkflowExecutionFulfilledEvent":
495
522
  return ctx.default_return_type
496
523
 
497
- outputs_node = self._get_outputs_node(ctx)
524
+ outputs_node = self._get_workflow_outputs_type_info(ctx)
498
525
  if not outputs_node:
499
526
  return ctx.default_return_type
500
527
 
@@ -513,6 +540,19 @@ class VellumMypyPlugin(Plugin):
513
540
  column=ctx.default_return_type.column,
514
541
  )
515
542
 
543
+ def _node_run_method_hook(self, ctx: MethodContext) -> MypyType:
544
+ if not isinstance(ctx.default_return_type, Instance):
545
+ return ctx.default_return_type
546
+
547
+ if not _is_subclass(ctx.default_return_type.type, "vellum.workflows.nodes.bases.base.BaseNode.Outputs"):
548
+ return ctx.default_return_type
549
+
550
+ outputs_node = self._get_node_outputs_type_info(ctx)
551
+ if not outputs_node:
552
+ return ctx.default_return_type
553
+
554
+ return Instance(outputs_node, [])
555
+
516
556
  def _stream_method_hook(self, ctx: MethodContext) -> MypyType:
517
557
  """
518
558
  We use this to target `Workflow.stream()` so that the WorkflowExecutionFulfilledEvent is properly typed
@@ -557,7 +597,7 @@ class VellumMypyPlugin(Plugin):
557
597
  if fulfilled_event_index == -1 or not fulfilled_event:
558
598
  return ctx.default_return_type
559
599
 
560
- outputs_node = self._get_outputs_node(ctx)
600
+ outputs_node = self._get_workflow_outputs_type_info(ctx)
561
601
  if not outputs_node:
562
602
  return ctx.default_return_type
563
603
 
@@ -594,7 +634,7 @@ class VellumMypyPlugin(Plugin):
594
634
  column=ctx.default_return_type.column,
595
635
  )
596
636
 
597
- def _get_outputs_node(self, ctx: MethodContext) -> Optional[TypeInfo]:
637
+ def _get_workflow_outputs_type_info(self, ctx: MethodContext) -> Optional[TypeInfo]:
598
638
  if not isinstance(ctx.context, CallExpr):
599
639
  return None
600
640
 
@@ -624,6 +664,35 @@ class VellumMypyPlugin(Plugin):
624
664
 
625
665
  return resolved_outputs_node.node
626
666
 
667
+ def _get_node_outputs_type_info(self, ctx: MethodContext) -> Optional[TypeInfo]:
668
+ if not isinstance(ctx.context, CallExpr):
669
+ return None
670
+
671
+ if not isinstance(ctx.context.callee, MemberExpr):
672
+ return None
673
+
674
+ expr = ctx.context.callee.expr
675
+ instance = ctx.api.get_expression_type(expr)
676
+ if not isinstance(instance, Instance) or not _is_subclass(
677
+ instance.type, "vellum.workflows.nodes.bases.base.BaseNode"
678
+ ):
679
+ return None
680
+
681
+ outputs_node = instance.type.names.get("Outputs")
682
+
683
+ if (
684
+ not outputs_node
685
+ or not isinstance(outputs_node.node, TypeInfo)
686
+ or not _is_subclass(outputs_node.node, "vellum.workflows.outputs.base.BaseOutputs")
687
+ ):
688
+ return None
689
+
690
+ # TODO: For some reason, returning the correct Outputs type info is causing `result` to not
691
+ # be found. `test_templating_node.py` is the best place to test this.
692
+ # https://app.shortcut.com/vellum/story/6132
693
+ # return outputs_node.node
694
+ return None
695
+
627
696
  def _resolve_descriptors_in_outputs(self, type_info: SymbolTableNode) -> SymbolTableNode:
628
697
  new_type_info = type_info.copy()
629
698
  if not isinstance(new_type_info.node, TypeInfo):
@@ -28,6 +28,9 @@ def render_sandboxed_jinja_template(
28
28
  keep_trailing_newline=True,
29
29
  finalize=finalize,
30
30
  )
31
+ environment.policies["json.dumps_kwargs"] = {
32
+ "cls": DefaultStateEncoder,
33
+ }
31
34
 
32
35
  if jinja_custom_filters:
33
36
  environment.filters.update(jinja_custom_filters)
@@ -324,6 +324,10 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
324
324
  if not descriptor.instance:
325
325
  continue
326
326
 
327
+ if any(isinstance(t, type) and issubclass(t, BaseDescriptor) for t in descriptor.types):
328
+ # We don't want to resolve attributes that are _meant_ to be descriptors
329
+ continue
330
+
327
331
  resolved_value = resolve_value(descriptor.instance, self.state, path=descriptor.name, memo=inputs)
328
332
  setattr(self, descriptor.name, resolved_value)
329
333
 
@@ -1,5 +1,7 @@
1
1
  from typing import Callable, Generic, Optional, Type
2
2
 
3
+ from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.descriptors.utils import resolve_value
3
5
  from vellum.workflows.errors.types import WorkflowErrorCode
4
6
  from vellum.workflows.exceptions import NodeException
5
7
  from vellum.workflows.inputs.base import BaseInputs
@@ -21,6 +23,7 @@ class RetryNode(BaseAdornmentNode[StateType], Generic[StateType]):
21
23
 
22
24
  max_attempts: int
23
25
  retry_on_error_code: Optional[WorkflowErrorCode] = None
26
+ retry_on_condition: Optional[BaseDescriptor] = None
24
27
 
25
28
  class SubworkflowInputs(BaseInputs):
26
29
  attempt_number: int
@@ -55,18 +58,36 @@ class RetryNode(BaseAdornmentNode[StateType], Generic[StateType]):
55
58
  last_exception = NodeException(
56
59
  code=WorkflowErrorCode.INVALID_OUTPUTS,
57
60
  message=f"""Unexpected rejection on attempt {attempt_number}: {terminal_event.error.code.value}.
61
+ Message: {terminal_event.error.message}""",
62
+ )
63
+ break
64
+ elif self.retry_on_condition and not resolve_value(self.retry_on_condition, self.state):
65
+ last_exception = NodeException(
66
+ code=WorkflowErrorCode.INVALID_OUTPUTS,
67
+ message=f"""Rejection failed on attempt {attempt_number}: {terminal_event.error.code.value}.
58
68
  Message: {terminal_event.error.message}""",
59
69
  )
60
70
  break
61
71
  else:
62
- last_exception = Exception(terminal_event.error.message)
72
+ last_exception = NodeException(
73
+ terminal_event.error.message,
74
+ code=terminal_event.error.code,
75
+ )
63
76
 
64
77
  raise last_exception
65
78
 
66
79
  @classmethod
67
80
  def wrap(
68
- cls, max_attempts: int, retry_on_error_code: Optional[WorkflowErrorCode] = None
81
+ cls,
82
+ max_attempts: int,
83
+ retry_on_error_code: Optional[WorkflowErrorCode] = None,
84
+ retry_on_condition: Optional[BaseDescriptor] = None,
69
85
  ) -> Callable[..., Type["RetryNode"]]:
70
86
  return create_adornment(
71
- cls, attributes={"max_attempts": max_attempts, "retry_on_error_code": retry_on_error_code}
87
+ cls,
88
+ attributes={
89
+ "max_attempts": max_attempts,
90
+ "retry_on_error_code": retry_on_error_code,
91
+ "retry_on_condition": retry_on_condition,
92
+ },
72
93
  )
@@ -6,6 +6,7 @@ from vellum.workflows.inputs.base import BaseInputs
6
6
  from vellum.workflows.nodes.bases import BaseNode
7
7
  from vellum.workflows.nodes.core.retry_node.node import RetryNode
8
8
  from vellum.workflows.outputs import BaseOutputs
9
+ from vellum.workflows.references.lazy import LazyReference
9
10
  from vellum.workflows.state.base import BaseState, StateMeta
10
11
 
11
12
 
@@ -91,3 +92,42 @@ def test_retry_node__use_parent_inputs_and_state():
91
92
 
92
93
  # THEN the data is used successfully
93
94
  assert outputs.value == "foo bar"
95
+
96
+
97
+ def test_retry_node__condition_arg_successfully_retries():
98
+ # GIVEN workflow Inputs and State
99
+ class State(BaseState):
100
+ count = 0
101
+
102
+ # AND a retry node that retries on a condition
103
+ @RetryNode.wrap(
104
+ max_attempts=5,
105
+ retry_on_condition=LazyReference(lambda: State.count.less_than(3)),
106
+ )
107
+ class TestNode(BaseNode[State]):
108
+ attempt_number = RetryNode.SubworkflowInputs.attempt_number
109
+
110
+ class Outputs(BaseOutputs):
111
+ value: str
112
+
113
+ def run(self) -> Outputs:
114
+ if not isinstance(self.state.meta.parent, State):
115
+ raise NodeException(message="Failed to resolve parent state")
116
+
117
+ self.state.meta.parent.count += 1
118
+ raise NodeException(message=f"This is failure attempt {self.attempt_number}")
119
+
120
+ # WHEN the node is run
121
+ node = TestNode(state=State())
122
+ with pytest.raises(NodeException) as exc_info:
123
+ node.run()
124
+
125
+ # THEN the exception raised is the last one
126
+ assert (
127
+ exc_info.value.message
128
+ == """Rejection failed on attempt 3: INTERNAL_ERROR.
129
+ Message: This is failure attempt 3"""
130
+ )
131
+
132
+ # AND the state was updated each time
133
+ assert node.state.count == 3
@@ -1,5 +1,6 @@
1
1
  import json
2
2
 
3
+ from vellum.client.types.function_call import FunctionCall
3
4
  from vellum.workflows.nodes.bases.base import BaseNode
4
5
  from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
5
6
  from vellum.workflows.state import BaseState
@@ -21,7 +22,9 @@ def test_templating_node__dict_output():
21
22
  outputs = node.run()
22
23
 
23
24
  # THEN the output is json serializable
24
- assert json.loads(outputs.result) == {"key": "value"}
25
+ # https://app.shortcut.com/vellum/story/6132
26
+ dump: str = outputs.result # type: ignore[assignment]
27
+ assert json.loads(dump) == {"key": "value"}
25
28
 
26
29
 
27
30
  def test_templating_node__int_output():
@@ -106,3 +109,19 @@ def test_templating_node__execution_count_reference():
106
109
 
107
110
  # THEN the output is just the total
108
111
  assert outputs.result == "0"
112
+
113
+
114
+ def test_templating_node__pydantic_to_json():
115
+ # GIVEN a templating node that uses tojson on a pydantic model
116
+ class JSONTemplateNode(TemplatingNode[BaseState, Json]):
117
+ template = "{{ function_call | tojson }}"
118
+ inputs = {
119
+ "function_call": FunctionCall(name="test", arguments={"key": "value"}),
120
+ }
121
+
122
+ # WHEN the node is run
123
+ node = JSONTemplateNode()
124
+ outputs = node.run()
125
+
126
+ # THEN the output is the expected JSON
127
+ assert outputs.result == {"name": "test", "arguments": {"key": "value"}, "id": None}
@@ -2,7 +2,7 @@ from typing import Optional, Union
2
2
 
3
3
  from vellum.workflows.constants import AuthorizationType
4
4
  from vellum.workflows.nodes.displayable.bases.api_node import BaseAPINode
5
- from vellum.workflows.references.vellum_secret import VellumSecretReference
5
+ from vellum.workflows.types.core import VellumSecret
6
6
 
7
7
 
8
8
  class APINode(BaseAPINode):
@@ -24,8 +24,8 @@ class APINode(BaseAPINode):
24
24
 
25
25
  authorization_type: Optional[AuthorizationType] = None
26
26
  api_key_header_key: Optional[str] = None
27
- api_key_header_value: Optional[Union[str, VellumSecretReference]] = None
28
- bearer_token_value: Optional[Union[str, VellumSecretReference]] = None
27
+ api_key_header_value: Optional[Union[str, VellumSecret]] = None
28
+ bearer_token_value: Optional[Union[str, VellumSecret]] = None
29
29
 
30
30
  def run(self) -> BaseAPINode.Outputs:
31
31
  headers = self.headers or {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.12.16
3
+ Version: 0.12.17
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -5,14 +5,14 @@ vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3
5
5
  vellum_cli/config.py,sha256=998IZbvkrw2avjbvs8bw6NrbEgGz5UBKRbvKAcastJg,5493
6
6
  vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
7
7
  vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
8
- vellum_cli/pull.py,sha256=hYQBe2_-Y4Ieh_Jo54EwBUpqPYq6UYitUBPgqyOVba0,7499
8
+ vellum_cli/pull.py,sha256=zf0y22XptUYI_hMP_4Q1CEo9s2wALsTJcCXNd-_ibd8,7551
9
9
  vellum_cli/push.py,sha256=iQ2H-vY8njqiYgIifGooqopqkb1BUUA3I7IN7VIHKv8,6149
10
10
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- vellum_cli/tests/conftest.py,sha256=eFGwBxib3Nki830lIFintB0b6r4x8T_KMnmzhlTY5x0,1337
11
+ vellum_cli/tests/conftest.py,sha256=Jv-QUjXoCDhsvVvXEjOenNqRArO_xXhtNuCYt4wiOyE,1421
12
12
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
13
13
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
14
14
  vellum_cli/tests/test_pull.py,sha256=6gbASF6ASy5YcdWjOCt6b5K0u2VWsFegdrTWu6sEVKs,19613
15
- vellum_cli/tests/test_push.py,sha256=sqPy1N9TOa0Wwk4tfEZm18BnfcwRSoQboTWW_PHQA-E,8145
15
+ vellum_cli/tests/test_push.py,sha256=8o8DFW9HCakhfZMS1Iql13RC90hF9pM630Y-rQbo234,8593
16
16
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -84,7 +84,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
84
84
  vellum/client/__init__.py,sha256=7yb5YnhvHQUJusa1WyUZcAKGol3z-Lfu07EfD03eEW4,111291
85
85
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
86
86
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
87
- vellum/client/core/client_wrapper.py,sha256=2N1VoBiQAederYuBKuAwXoDAkbTGPnkciOAPvOv1Xyk,1869
87
+ vellum/client/core/client_wrapper.py,sha256=Ip_JnKYTedcuVV4puh0iLeYLzPtHAxgnmwqZyCKXSCY,1869
88
88
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
89
89
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
90
90
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -668,7 +668,7 @@ vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQ
668
668
  vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
669
669
  vellum/plugins/pydantic.py,sha256=dNtZWHo-IdseG52C2RoTanxyTJg0AhPZrH-9lbNqwYg,2604
670
670
  vellum/plugins/utils.py,sha256=U9ZY9KdE3RRvbcG01hXxu9CvfJD6Fo7nJDgcHjQn0FI,606
671
- vellum/plugins/vellum_mypy.py,sha256=VC15EzjTsXOb9uF1bky4rcxePP-0epMVmCsLB2z4Dh8,24816
671
+ vellum/plugins/vellum_mypy.py,sha256=7JmyuTX723-XWnWoE6afiUNOkHyAufCUZwxck9BP2aI,27784
672
672
  vellum/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
673
673
  vellum/prompts/blocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
674
674
  vellum/prompts/blocks/compilation.py,sha256=ISuvDHaeVCPb1L7l4umORCECkDn0-rvE49hopz6c2gM,9222
@@ -1221,7 +1221,7 @@ vellum/utils/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
1221
1221
  vellum/utils/templating/constants.py,sha256=XTNmDsKa7byjw4GMZmzx2dUeYUTeMLZrPgRHcc80Kvc,613
1222
1222
  vellum/utils/templating/custom_filters.py,sha256=Q0DahYRHP4KfaUXDt9XxN-DFLBrAxlv90yaVqxScoUw,264
1223
1223
  vellum/utils/templating/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
1224
- vellum/utils/templating/render.py,sha256=0vgkwhu2A6o64aT4fUdTSLFCEMbeRjAKAuvv2k2LYGY,1772
1224
+ vellum/utils/templating/render.py,sha256=OwdZTJlQv_qsygMX3LOFr4d1_2oz3r9vGKyj7cX05VE,1876
1225
1225
  vellum/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
1226
1226
  vellum/utils/uuid.py,sha256=Ch6wWRgwICxLxJCTl5iE3EdRlZj2zADR-zUMUtjcMWM,214
1227
1227
  vellum/version.py,sha256=jq-1PlAYxN9AXuaZqbYk9ak27SgE2lw9Ia5gx1b1gVI,76
@@ -1286,7 +1286,7 @@ vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOr
1286
1286
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1287
1287
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1288
1288
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1289
- vellum/workflows/nodes/bases/base.py,sha256=RREFzYPxemKUvQc0NfnwQmby-p_BE3O-TbVWKbQFdfs,14271
1289
+ vellum/workflows/nodes/bases/base.py,sha256=CcWQBMR3cx5vftqQp_oJ_GncULJIbOLMyNP4KZNgU10,14487
1290
1290
  vellum/workflows/nodes/bases/base_adornment_node.py,sha256=eFTgsPCYb3eyGS0-kw7C6crFnwFx437R5wh9-8bWYts,2905
1291
1291
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1292
1292
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=51CueFVty9XYASC0rKr1cXWejho5WElmhfhp6cCONy0,3811
@@ -1302,19 +1302,19 @@ vellum/workflows/nodes/core/map_node/node.py,sha256=DTMoGqtR8MyfZ8jy8apNoN-4KFFF
1302
1302
  vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1303
1303
  vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=RHSZs7t6mW3UWvRrXnHZqaXVdRT2ZquOK_YHJ-gzXsU,1871
1304
1304
  vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
1305
- vellum/workflows/nodes/core/retry_node/node.py,sha256=lAABgo2E_pWkzOYUBGzC1SnywgwtGuJojwT602fKCUc,3153
1305
+ vellum/workflows/nodes/core/retry_node/node.py,sha256=QEpxhKOyxDkRoAn2b0PToZWtAGQetSQYVTpb9yCOLlw,4028
1306
1306
  vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1307
- vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=QXTnHwmJHISxXjvZMeuuEo0iVugVMJyaJoggI8yKXfI,3132
1307
+ vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
1308
1308
  vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
1309
1309
  vellum/workflows/nodes/core/templating_node/node.py,sha256=N-NOBd-UY91qO9orCcW4KEbhNvDQivZPA-PCxs-M0RM,4204
1310
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nW_kyJ9RAqz45_kJE_rlhOOvbV4OO3hecP-P-ydQpkw,2845
1310
+ vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=3rN5Ncwq87Y2YKyobNXALCih6OnUTJf55otnR4V20C8,3557
1311
1311
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1312
1312
  vellum/workflows/nodes/core/try_node/node.py,sha256=_lTmSYCiz7lktaxpNWUCglNi8_5Sfy8Rpiov5SeKVMw,3920
1313
1313
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1314
1314
  vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=Wc2kLl-MkffsBxl3IiFaqLd16e2Iosxhk7qBnojPvQg,4092
1315
1315
  vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
1316
1316
  vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
1317
- vellum/workflows/nodes/displayable/api_node/node.py,sha256=ehvJNkI-L_WLn8pszCaRkQuhRDgioCLaX6TT72KM_9E,2111
1317
+ vellum/workflows/nodes/displayable/api_node/node.py,sha256=ocXjg0Sp52V6DllTDZXUgOp9qBw6l_KbtWcaxowsDM4,2070
1318
1318
  vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
1319
1319
  vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
1320
1320
  vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=ywPwCxDesgnE86Wjyf2ZyG2Dr2O7xW8D4tPHZB5Se_s,2477
@@ -1415,8 +1415,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
1415
1415
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1416
1416
  vellum/workflows/workflows/base.py,sha256=qdZYQq-jjdr0fYT0FCfmFuI5ypE3pANupgYcOqqML0o,18884
1417
1417
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1418
- vellum_ai-0.12.16.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1419
- vellum_ai-0.12.16.dist-info/METADATA,sha256=dSVFM4nOL_e1hiNJX-uVrRBdO4-1wDtqhA3MZKwMLTM,5328
1420
- vellum_ai-0.12.16.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1421
- vellum_ai-0.12.16.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1422
- vellum_ai-0.12.16.dist-info/RECORD,,
1418
+ vellum_ai-0.12.17.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1419
+ vellum_ai-0.12.17.dist-info/METADATA,sha256=01ujt39Or2zqLaKV_VsarQ9H_hkgSc1YfrsEOMokUUk,5328
1420
+ vellum_ai-0.12.17.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1421
+ vellum_ai-0.12.17.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1422
+ vellum_ai-0.12.17.dist-info/RECORD,,
vellum_cli/pull.py CHANGED
@@ -173,10 +173,13 @@ def pull_command(
173
173
  target.write(content)
174
174
 
175
175
  if metadata_json:
176
- workflow_config.container_image_name = metadata_json.get("runner_config", {}).get("container_image_name")
177
- workflow_config.container_image_tag = metadata_json.get("runner_config", {}).get("container_image_tag")
178
- if workflow_config.container_image_name and not workflow_config.container_image_tag:
179
- workflow_config.container_image_tag = "latest"
176
+ runner_config = metadata_json.get("runner_config")
177
+
178
+ if runner_config:
179
+ workflow_config.container_image_name = runner_config.get("container_image_name")
180
+ workflow_config.container_image_tag = runner_config.get("container_image_tag")
181
+ if workflow_config.container_image_name and not workflow_config.container_image_tag:
182
+ workflow_config.container_image_tag = "latest"
180
183
 
181
184
  if include_json:
182
185
  logger.warning(
@@ -18,11 +18,13 @@ class MockModuleResult:
18
18
 
19
19
 
20
20
  @pytest.fixture
21
- def mock_module() -> Generator[MockModuleResult, None, None]:
21
+ def mock_module(request) -> Generator[MockModuleResult, None, None]:
22
22
  current_dir = os.getcwd()
23
23
  temp_dir = tempfile.mkdtemp()
24
24
  os.chdir(temp_dir)
25
- module = "examples.mock"
25
+
26
+ # Use the test name to create a unique module path
27
+ module = f"examples.mock.{request.node.name}"
26
28
  workflow_sandbox_id = str(uuid4())
27
29
 
28
30
  def set_pyproject_toml(vellum_config: Dict[str, Any]) -> None:
@@ -110,13 +110,17 @@ class ExampleWorkflow(BaseWorkflow):
110
110
  # THEN it should succeed
111
111
  assert result.exit_code == 0
112
112
 
113
+ # Get the last part of the module path and format it
114
+ expected_label = mock_module.module.split(".")[-1].replace("_", " ").title()
115
+ expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
116
+
113
117
  # AND we should have called the push API with the correct args
114
118
  vellum_client.workflows.push.assert_called_once()
115
119
  call_args = vellum_client.workflows.push.call_args.kwargs
116
120
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
117
- assert call_args["label"] == "Mock"
121
+ assert call_args["label"] == expected_label
118
122
  assert is_valid_uuid(call_args["workflow_sandbox_id"])
119
- assert call_args["artifact"].name == "examples__mock.tar.gz"
123
+ assert call_args["artifact"].name == expected_artifact_name
120
124
  assert "deplyment_config" not in call_args
121
125
 
122
126
  extracted_files = _extract_tar_gz(call_args["artifact"].read())
@@ -160,13 +164,17 @@ class ExampleWorkflow(BaseWorkflow):
160
164
  # THEN it should succeed
161
165
  assert result.exit_code == 0
162
166
 
167
+ # Get the last part of the module path and format it
168
+ expected_label = mock_module.module.split(".")[-1].replace("_", " ").title()
169
+ expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
170
+
163
171
  # AND we should have called the push API with the correct args
164
172
  vellum_client.workflows.push.assert_called_once()
165
173
  call_args = vellum_client.workflows.push.call_args.kwargs
166
174
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
167
- assert call_args["label"] == "Mock"
175
+ assert call_args["label"] == expected_label
168
176
  assert is_valid_uuid(call_args["workflow_sandbox_id"])
169
- assert call_args["artifact"].name == "examples__mock.tar.gz"
177
+ assert call_args["artifact"].name == expected_artifact_name
170
178
  assert call_args["deployment_config"] == "{}"
171
179
 
172
180
  extracted_files = _extract_tar_gz(call_args["artifact"].read())
@@ -231,6 +239,6 @@ class ExampleWorkflow(BaseWorkflow):
231
239
 
232
240
  # AND the report should be in the output
233
241
  assert "## Errors" in result.output
234
- # assert "Serialization is not supported." in result.output
242
+ assert "Serialization is not supported." in result.output
235
243
  assert "## Proposed Diffs" in result.output
236
244
  assert "iterable_item_added" in result.output