vellum-ai 0.12.16__py3-none-any.whl → 0.12.17__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.
@@ -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