vellum-ai 0.14.38__py3-none-any.whl → 0.14.39__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.
vellum/__init__.py CHANGED
@@ -443,6 +443,7 @@ from .types import (
443
443
  TestSuiteRunMetricNumberOutput,
444
444
  TestSuiteRunMetricOutput,
445
445
  TestSuiteRunMetricStringOutput,
446
+ TestSuiteRunProgress,
446
447
  TestSuiteRunPromptSandboxExecConfigDataRequest,
447
448
  TestSuiteRunPromptSandboxExecConfigRequest,
448
449
  TestSuiteRunPromptSandboxHistoryItemExecConfig,
@@ -1072,6 +1073,7 @@ __all__ = [
1072
1073
  "TestSuiteRunMetricNumberOutput",
1073
1074
  "TestSuiteRunMetricOutput",
1074
1075
  "TestSuiteRunMetricStringOutput",
1076
+ "TestSuiteRunProgress",
1075
1077
  "TestSuiteRunPromptSandboxExecConfigDataRequest",
1076
1078
  "TestSuiteRunPromptSandboxExecConfigRequest",
1077
1079
  "TestSuiteRunPromptSandboxHistoryItemExecConfig",
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.38",
21
+ "X-Fern-SDK-Version": "0.14.39",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -453,6 +453,7 @@ from .test_suite_run_metric_json_output import TestSuiteRunMetricJsonOutput
453
453
  from .test_suite_run_metric_number_output import TestSuiteRunMetricNumberOutput
454
454
  from .test_suite_run_metric_output import TestSuiteRunMetricOutput
455
455
  from .test_suite_run_metric_string_output import TestSuiteRunMetricStringOutput
456
+ from .test_suite_run_progress import TestSuiteRunProgress
456
457
  from .test_suite_run_prompt_sandbox_exec_config_data_request import TestSuiteRunPromptSandboxExecConfigDataRequest
457
458
  from .test_suite_run_prompt_sandbox_exec_config_request import TestSuiteRunPromptSandboxExecConfigRequest
458
459
  from .test_suite_run_prompt_sandbox_history_item_exec_config import TestSuiteRunPromptSandboxHistoryItemExecConfig
@@ -1052,6 +1053,7 @@ __all__ = [
1052
1053
  "TestSuiteRunMetricNumberOutput",
1053
1054
  "TestSuiteRunMetricOutput",
1054
1055
  "TestSuiteRunMetricStringOutput",
1056
+ "TestSuiteRunProgress",
1055
1057
  "TestSuiteRunPromptSandboxExecConfigDataRequest",
1056
1058
  "TestSuiteRunPromptSandboxExecConfigRequest",
1057
1059
  "TestSuiteRunPromptSandboxHistoryItemExecConfig",
@@ -0,0 +1,20 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
5
+ import typing
6
+ import pydantic
7
+
8
+
9
+ class TestSuiteRunProgress(UniversalBaseModel):
10
+ number_of_requested_test_cases: int
11
+ number_of_completed_test_cases: int
12
+
13
+ if IS_PYDANTIC_V2:
14
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
15
+ else:
16
+
17
+ class Config:
18
+ frozen = True
19
+ smart_union = True
20
+ extra = pydantic.Extra.allow
@@ -9,6 +9,7 @@ from .test_suite_run_state import TestSuiteRunState
9
9
  import pydantic
10
10
  import typing
11
11
  from .test_suite_run_exec_config import TestSuiteRunExecConfig
12
+ from .test_suite_run_progress import TestSuiteRunProgress
12
13
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
13
14
  from ..core.pydantic_utilities import update_forward_refs
14
15
 
@@ -33,6 +34,8 @@ class TestSuiteRunRead(UniversalBaseModel):
33
34
  Configuration that defines how the Test Suite should be run
34
35
  """
35
36
 
37
+ progress: typing.Optional[TestSuiteRunProgress] = None
38
+
36
39
  if IS_PYDANTIC_V2:
37
40
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
38
41
  else:
@@ -11,6 +11,7 @@ VellumSdkErrorCodeEnum = typing.Union[
11
11
  "INVALID_CODE",
12
12
  "INVALID_TEMPLATE",
13
13
  "INTERNAL_ERROR",
14
+ "PROVIDER_CREDENTIALS_UNAVAILABLE",
14
15
  "PROVIDER_ERROR",
15
16
  "USER_DEFINED_ERROR",
16
17
  "WORKFLOW_CANCELLED",
@@ -6,6 +6,7 @@ WorkflowExecutionEventErrorCode = typing.Union[
6
6
  typing.Literal[
7
7
  "WORKFLOW_INITIALIZATION",
8
8
  "WORKFLOW_CANCELLED",
9
+ "PROVIDER_CREDENTIALS_UNAVAILABLE",
9
10
  "NODE_EXECUTION_COUNT_LIMIT_REACHED",
10
11
  "INTERNAL_SERVER_ERROR",
11
12
  "NODE_EXECUTION",
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.test_suite_run_progress import *
@@ -17,6 +17,7 @@ class WorkflowErrorCode(Enum):
17
17
  INVALID_TEMPLATE = "INVALID_TEMPLATE"
18
18
  INTERNAL_ERROR = "INTERNAL_ERROR"
19
19
  NODE_EXECUTION = "NODE_EXECUTION"
20
+ PROVIDER_CREDENTIALS_UNAVAILABLE = "PROVIDER_CREDENTIALS_UNAVAILABLE"
20
21
  PROVIDER_ERROR = "PROVIDER_ERROR"
21
22
  USER_DEFINED_ERROR = "USER_DEFINED_ERROR"
22
23
  WORKFLOW_CANCELLED = "WORKFLOW_CANCELLED"
@@ -89,6 +89,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
89
89
  "foo": "bar",
90
90
  },
91
91
  "display_context": None,
92
+ "initial_state": None,
92
93
  },
93
94
  "parent": None,
94
95
  },
@@ -54,8 +54,10 @@ class WorkflowEventDisplayContext(UniversalBaseModel):
54
54
  workflow_outputs: Dict[str, UUID]
55
55
 
56
56
 
57
- class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsType]):
57
+ class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsType, StateType]):
58
58
  inputs: InputsType
59
+ initial_state: Optional[StateType] = None
60
+
59
61
  # It is still the responsibility of the workflow server to populate this context. The SDK's
60
62
  # Workflow Runner will always leave this field None.
61
63
  #
@@ -67,15 +69,23 @@ class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsT
67
69
  def serialize_inputs(self, inputs: InputsType, _info: Any) -> Dict[str, Any]:
68
70
  return default_serializer(inputs)
69
71
 
72
+ @field_serializer("initial_state")
73
+ def serialize_initial_state(self, initial_state: Optional[StateType], _info: Any) -> Optional[Dict[str, Any]]:
74
+ return default_serializer(initial_state)
75
+
70
76
 
71
- class WorkflowExecutionInitiatedEvent(_BaseWorkflowEvent, Generic[InputsType]):
77
+ class WorkflowExecutionInitiatedEvent(_BaseWorkflowEvent, Generic[InputsType, StateType]):
72
78
  name: Literal["workflow.execution.initiated"] = "workflow.execution.initiated"
73
- body: WorkflowExecutionInitiatedBody[InputsType]
79
+ body: WorkflowExecutionInitiatedBody[InputsType, StateType]
74
80
 
75
81
  @property
76
82
  def inputs(self) -> InputsType:
77
83
  return self.body.inputs
78
84
 
85
+ @property
86
+ def initial_state(self) -> Optional[StateType]:
87
+ return self.body.initial_state
88
+
79
89
 
80
90
  class WorkflowExecutionStreamingBody(_BaseWorkflowExecutionBody):
81
91
  output: BaseOutput
@@ -69,7 +69,13 @@ class BasePromptNode(BaseNode, Generic[StateType]):
69
69
  return outputs
70
70
 
71
71
  def _handle_api_error(self, e: ApiError):
72
- if e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
72
+ if e.status_code and e.status_code == 403 and isinstance(e.body, dict):
73
+ raise NodeException(
74
+ message=e.body.get("detail", "Provider credentials is missing or unavailable"),
75
+ code=WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
76
+ )
77
+
78
+ elif e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
73
79
  raise NodeException(
74
80
  message=e.body.get("detail", "Failed to execute Prompt"),
75
81
  code=WorkflowErrorCode.INVALID_INPUTS,
@@ -170,8 +170,13 @@ def test_inline_prompt_node__function_definitions(vellum_adhoc_prompt_client):
170
170
  WorkflowErrorCode.INTERNAL_ERROR,
171
171
  "Failed to execute Prompt",
172
172
  ),
173
+ (
174
+ ApiError(status_code=403, body={"detail": "Provider credentials is missing or unavailable"}),
175
+ WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
176
+ "Provider credentials is missing or unavailable",
177
+ ),
173
178
  ],
174
- ids=["404", "invalid_dict", "invalid_body", "no_status_code", "500"],
179
+ ids=["404", "invalid_dict", "invalid_body", "no_status_code", "500", "403"],
175
180
  )
176
181
  def test_inline_prompt_node__api_error__invalid_inputs_node_exception(
177
182
  vellum_adhoc_prompt_client, exception, expected_code, expected_message
@@ -491,3 +491,29 @@ def test_prompt_deployment_node__no_fallbacks(vellum_client):
491
491
 
492
492
  # AND the client should have been called only once (for the primary model)
493
493
  assert vellum_client.execute_prompt_stream.call_count == 1
494
+
495
+
496
+ def test_prompt_deployment_node__provider_credentials_missing(vellum_client):
497
+ # GIVEN a Prompt Deployment Node
498
+ class TestPromptDeploymentNode(PromptDeploymentNode):
499
+ deployment = "test_deployment"
500
+ prompt_inputs = {}
501
+
502
+ # AND the client responds with a 403 error of provider credentials missing
503
+ primary_error = ApiError(
504
+ body={"detail": "Provider credentials is missing or unavailable"},
505
+ status_code=403,
506
+ )
507
+
508
+ vellum_client.execute_prompt_stream.side_effect = primary_error
509
+
510
+ # WHEN we run the node
511
+ node = TestPromptDeploymentNode()
512
+
513
+ # THEN the node should raise an exception
514
+ with pytest.raises(NodeException) as exc_info:
515
+ list(node.run())
516
+
517
+ # AND the exception should contain the original error message
518
+ assert exc_info.value.message == "Provider credentials is missing or unavailable"
519
+ assert exc_info.value.code == WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE
@@ -4,11 +4,11 @@ from dataclasses import dataclass
4
4
  import logging
5
5
  from queue import Empty, Queue
6
6
  from threading import Event as ThreadingEvent, Thread
7
- from uuid import UUID
7
+ from uuid import UUID, uuid4
8
8
  from typing import TYPE_CHECKING, Any, Dict, Generic, Iterable, Iterator, Optional, Sequence, Set, Tuple, Type, Union
9
9
 
10
10
  from vellum.workflows.constants import undefined
11
- from vellum.workflows.context import ExecutionContext, execution_context, get_execution_context, get_parent_context
11
+ from vellum.workflows.context import ExecutionContext, execution_context, get_execution_context
12
12
  from vellum.workflows.descriptors.base import BaseDescriptor
13
13
  from vellum.workflows.edges.edge import Edge
14
14
  from vellum.workflows.errors import WorkflowError, WorkflowErrorCode
@@ -30,7 +30,7 @@ from vellum.workflows.events.node import (
30
30
  NodeExecutionRejectedBody,
31
31
  NodeExecutionStreamingBody,
32
32
  )
33
- from vellum.workflows.events.types import BaseEvent, NodeParentContext, WorkflowParentContext
33
+ from vellum.workflows.events.types import BaseEvent, NodeParentContext, ParentContext, WorkflowParentContext
34
34
  from vellum.workflows.events.workflow import (
35
35
  WorkflowExecutionFulfilledBody,
36
36
  WorkflowExecutionInitiatedBody,
@@ -90,6 +90,7 @@ class WorkflowRunner(Generic[StateType]):
90
90
 
91
91
  self.workflow = workflow
92
92
  self._is_resuming = False
93
+ self._should_emit_initial_state = True
93
94
  if entrypoint_nodes:
94
95
  if len(list(entrypoint_nodes)) > 1:
95
96
  raise ValueError("Cannot resume from multiple nodes")
@@ -98,7 +99,8 @@ class WorkflowRunner(Generic[StateType]):
98
99
  # https://app.shortcut.com/vellum/story/4408
99
100
  node = next(iter(entrypoint_nodes))
100
101
  if state:
101
- self._initial_state = state
102
+ self._initial_state = deepcopy(state)
103
+ self._initial_state.meta.span_id = uuid4()
102
104
  else:
103
105
  self._initial_state = self.workflow.get_state_at_node(node)
104
106
  self._entrypoints = entrypoint_nodes
@@ -123,8 +125,13 @@ class WorkflowRunner(Generic[StateType]):
123
125
  if state:
124
126
  self._initial_state = deepcopy(state)
125
127
  self._initial_state.meta.workflow_inputs = normalized_inputs
128
+ self._initial_state.meta.span_id = uuid4()
126
129
  else:
127
130
  self._initial_state = self.workflow.get_default_state(normalized_inputs)
131
+ # We don't want to emit the initial state on the base case of Workflow Runs, since
132
+ # all of that data is redundant and is derivable. It also clearly communicates that
133
+ # there was no initial state provided by the user to invoke the workflow.
134
+ self._should_emit_initial_state = False
128
135
  self._entrypoints = self.workflow.get_entrypoints()
129
136
 
130
137
  # This queue is responsible for sending events from WorkflowRunner to the outside world
@@ -239,7 +246,8 @@ class WorkflowRunner(Generic[StateType]):
239
246
  instance=None,
240
247
  outputs_class=node.Outputs,
241
248
  )
242
- node.state.meta.node_outputs[output_descriptor] = streaming_output_queues[output.name]
249
+ with node.state.__quiet__():
250
+ node.state.meta.node_outputs[output_descriptor] = streaming_output_queues[output.name]
243
251
  initiated_output: BaseOutput = BaseOutput(name=output.name)
244
252
  initiated_ports = initiated_output > ports
245
253
  self._workflow_event_inner_queue.put(
@@ -297,13 +305,14 @@ class WorkflowRunner(Generic[StateType]):
297
305
 
298
306
  node.state.meta.node_execution_cache.fulfill_node_execution(node.__class__, span_id)
299
307
 
300
- for descriptor, output_value in outputs:
301
- if output_value is undefined:
302
- if descriptor in node.state.meta.node_outputs:
303
- del node.state.meta.node_outputs[descriptor]
304
- continue
308
+ with node.state.__atomic__():
309
+ for descriptor, output_value in outputs:
310
+ if output_value is undefined:
311
+ if descriptor in node.state.meta.node_outputs:
312
+ del node.state.meta.node_outputs[descriptor]
313
+ continue
305
314
 
306
- node.state.meta.node_outputs[descriptor] = output_value
315
+ node.state.meta.node_outputs[descriptor] = output_value
307
316
 
308
317
  invoked_ports = ports(outputs, node.state)
309
318
  self._workflow_event_inner_queue.put(
@@ -365,11 +374,16 @@ class WorkflowRunner(Generic[StateType]):
365
374
 
366
375
  logger.debug(f"Finished running node: {node.__class__.__name__}")
367
376
 
368
- def _context_run_work_item(self, node: BaseNode[StateType], span_id: UUID, parent_context=None) -> None:
369
- execution = get_execution_context()
377
+ def _context_run_work_item(
378
+ self,
379
+ node: BaseNode[StateType],
380
+ span_id: UUID,
381
+ parent_context: ParentContext,
382
+ trace_id: UUID,
383
+ ) -> None:
370
384
  with execution_context(
371
- parent_context=parent_context or execution.parent_context,
372
- trace_id=execution.trace_id,
385
+ parent_context=parent_context,
386
+ trace_id=trace_id,
373
387
  ):
374
388
  self._run_work_item(node, span_id)
375
389
 
@@ -419,14 +433,19 @@ class WorkflowRunner(Generic[StateType]):
419
433
  if not node_class.Trigger.should_initiate(state, all_deps, node_span_id):
420
434
  return
421
435
 
422
- current_parent = get_parent_context()
436
+ execution = get_execution_context()
423
437
  node = node_class(state=state, context=self.workflow.context)
424
438
  state.meta.node_execution_cache.initiate_node_execution(node_class, node_span_id)
425
439
  self._active_nodes_by_execution_id[node_span_id] = ActiveNode(node=node)
426
440
 
427
441
  worker_thread = Thread(
428
442
  target=self._context_run_work_item,
429
- kwargs={"node": node, "span_id": node_span_id, "parent_context": current_parent},
443
+ kwargs={
444
+ "node": node,
445
+ "span_id": node_span_id,
446
+ "parent_context": execution.parent_context,
447
+ "trace_id": execution.trace_id,
448
+ },
430
449
  )
431
450
  worker_thread.start()
432
451
 
@@ -500,6 +519,7 @@ class WorkflowRunner(Generic[StateType]):
500
519
  body=WorkflowExecutionInitiatedBody(
501
520
  workflow_definition=self.workflow.__class__,
502
521
  inputs=self._initial_state.meta.workflow_inputs,
522
+ initial_state=deepcopy(self._initial_state) if self._should_emit_initial_state else None,
503
523
  ),
504
524
  parent=self._execution_context.parent_context,
505
525
  )
@@ -1,4 +1,5 @@
1
1
  from collections import defaultdict
2
+ from contextlib import contextmanager
2
3
  from copy import deepcopy
3
4
  from dataclasses import field
4
5
  from datetime import datetime
@@ -404,11 +405,11 @@ class BaseState(metaclass=_BaseStateMeta):
404
405
  meta: StateMeta = field(init=False)
405
406
 
406
407
  __lock__: Lock = field(init=False)
407
- __is_initializing__: bool = field(init=False)
408
+ __is_quiet__: bool = field(init=False)
408
409
  __snapshot_callback__: Callable[["BaseState"], None] = field(init=False)
409
410
 
410
411
  def __init__(self, meta: Optional[StateMeta] = None, **kwargs: Any) -> None:
411
- self.__is_initializing__ = True
412
+ self.__is_quiet__ = True
412
413
  self.__snapshot_callback__ = lambda state: None
413
414
  self.__lock__ = Lock()
414
415
 
@@ -418,14 +419,14 @@ class BaseState(metaclass=_BaseStateMeta):
418
419
  # Make all class attribute values snapshottable
419
420
  for name, value in self.__class__.__dict__.items():
420
421
  if not name.startswith("_") and name != "meta":
421
- # Bypass __is_initializing__ instead of `setattr`
422
+ # Bypass __is_quiet__ instead of `setattr`
422
423
  snapshottable_value = _make_snapshottable(value, self.__snapshot__)
423
424
  super().__setattr__(name, snapshottable_value)
424
425
 
425
426
  for name, value in kwargs.items():
426
427
  setattr(self, name, value)
427
428
 
428
- self.__is_initializing__ = False
429
+ self.__is_quiet__ = False
429
430
 
430
431
  def __deepcopy__(self, memo: Any) -> "BaseState":
431
432
  new_state = deepcopy_with_exclusions(
@@ -472,7 +473,7 @@ class BaseState(metaclass=_BaseStateMeta):
472
473
  return self.__dict__[key]
473
474
 
474
475
  def __setattr__(self, name: str, value: Any) -> None:
475
- if name.startswith("_") or self.__is_initializing__:
476
+ if name.startswith("_"):
476
477
  super().__setattr__(name, value)
477
478
  return
478
479
 
@@ -513,11 +514,33 @@ class BaseState(metaclass=_BaseStateMeta):
513
514
  Snapshots the current state to the workflow emitter. The invoked callback is overridden by the
514
515
  workflow runner.
515
516
  """
517
+ if self.__is_quiet__:
518
+ return
519
+
516
520
  try:
517
521
  self.__snapshot_callback__(deepcopy(self))
518
522
  except Exception:
519
523
  logger.exception("Failed to snapshot Workflow state.")
520
524
 
525
+ @contextmanager
526
+ def __quiet__(self):
527
+ prev = self.__is_quiet__
528
+ self.__is_quiet__ = True
529
+ try:
530
+ yield
531
+ finally:
532
+ self.__is_quiet__ = prev
533
+
534
+ @contextmanager
535
+ def __atomic__(self):
536
+ prev = self.__is_quiet__
537
+ self.__is_quiet__ = True
538
+ try:
539
+ yield
540
+ finally:
541
+ self.__is_quiet__ = prev
542
+ self.__snapshot__()
543
+
521
544
  @classmethod
522
545
  def __get_pydantic_core_schema__(
523
546
  cls, source_type: Type[Any], handler: GetCoreSchemaHandler
@@ -1,17 +1,14 @@
1
1
  import pytest
2
- from collections import defaultdict
3
2
  from copy import deepcopy
4
3
  import json
5
4
  from queue import Queue
6
- from typing import Dict
5
+ from typing import Dict, cast
7
6
 
8
7
  from vellum.workflows.nodes.bases import BaseNode
9
8
  from vellum.workflows.outputs.base import BaseOutputs
10
9
  from vellum.workflows.state.base import BaseState
11
10
  from vellum.workflows.state.encoder import DefaultStateEncoder
12
11
 
13
- snapshot_count: Dict[int, int] = defaultdict(int)
14
-
15
12
 
16
13
  @pytest.fixture()
17
14
  def mock_deepcopy(mocker):
@@ -27,9 +24,19 @@ class MockState(BaseState):
27
24
  foo: str
28
25
  nested_dict: Dict[str, int] = {}
29
26
 
30
- def __snapshot__(self) -> None:
31
- global snapshot_count
32
- snapshot_count[id(self)] += 1
27
+ __snapshot_count__: int = 0
28
+
29
+ def __init__(self, *args, **kwargs) -> None:
30
+ super().__init__(*args, **kwargs)
31
+ self.__snapshot_callback__ = lambda _: self.__mock_snapshot__()
32
+
33
+ def __mock_snapshot__(self) -> None:
34
+ self.__snapshot_count__ += 1
35
+
36
+ def __deepcopy__(self, memo: dict) -> "MockState":
37
+ new_state = cast(MockState, super().__deepcopy__(memo))
38
+ new_state.__snapshot_count__ = 0
39
+ return new_state
33
40
 
34
41
 
35
42
  class MockNode(BaseNode):
@@ -43,50 +50,50 @@ class MockNode(BaseNode):
43
50
  def test_state_snapshot__node_attribute_edit():
44
51
  # GIVEN an initial state instance
45
52
  state = MockState(foo="bar")
46
- assert snapshot_count[id(state)] == 0
53
+ assert state.__snapshot_count__ == 0
47
54
 
48
55
  # WHEN we edit an attribute
49
56
  state.foo = "baz"
50
57
 
51
58
  # THEN the snapshot is emitted
52
- assert snapshot_count[id(state)] == 1
59
+ assert state.__snapshot_count__ == 1
53
60
 
54
61
 
55
62
  def test_state_snapshot__node_output_edit():
56
63
  # GIVEN an initial state instance
57
64
  state = MockState(foo="bar")
58
- assert snapshot_count[id(state)] == 0
65
+ assert state.__snapshot_count__ == 0
59
66
 
60
67
  # WHEN we add a Node Output to state
61
68
  for output in MockNode.Outputs:
62
69
  state.meta.node_outputs[output] = "hello"
63
70
 
64
71
  # THEN the snapshot is emitted
65
- assert snapshot_count[id(state)] == 1
72
+ assert state.__snapshot_count__ == 1
66
73
 
67
74
 
68
75
  def test_state_snapshot__nested_dictionary_edit():
69
76
  # GIVEN an initial state instance
70
77
  state = MockState(foo="bar")
71
- assert snapshot_count[id(state)] == 0
78
+ assert state.__snapshot_count__ == 0
72
79
 
73
80
  # WHEN we edit a nested dictionary
74
81
  state.nested_dict["hello"] = 1
75
82
 
76
83
  # THEN the snapshot is emitted
77
- assert snapshot_count[id(state)] == 1
84
+ assert state.__snapshot_count__ == 1
78
85
 
79
86
 
80
87
  def test_state_snapshot__external_input_edit():
81
88
  # GIVEN an initial state instance
82
89
  state = MockState(foo="bar")
83
- assert snapshot_count[id(state)] == 0
90
+ assert state.__snapshot_count__ == 0
84
91
 
85
92
  # WHEN we add an external input to state
86
93
  state.meta.external_inputs[MockNode.ExternalInputs.message] = "hello"
87
94
 
88
95
  # THEN the snapshot is emitted
89
- assert snapshot_count[id(state)] == 1
96
+ assert state.__snapshot_count__ == 1
90
97
 
91
98
 
92
99
  def test_state_deepcopy():
@@ -103,7 +110,6 @@ def test_state_deepcopy():
103
110
  assert deepcopied_state.meta.node_outputs == state.meta.node_outputs
104
111
 
105
112
 
106
- @pytest.mark.skip(reason="https://app.shortcut.com/vellum/story/5654")
107
113
  def test_state_deepcopy__with_node_output_updates():
108
114
  # GIVEN an initial state instance
109
115
  state = MockState(foo="bar")
@@ -121,10 +127,10 @@ def test_state_deepcopy__with_node_output_updates():
121
127
  assert deepcopied_state.meta.node_outputs[MockNode.Outputs.baz] == "hello"
122
128
 
123
129
  # AND the original state has had the correct number of snapshots
124
- assert snapshot_count[id(state)] == 2
130
+ assert state.__snapshot_count__ == 2
125
131
 
126
132
  # AND the copied state has had the correct number of snapshots
127
- assert snapshot_count[id(deepcopied_state)] == 0
133
+ assert deepcopied_state.__snapshot_count__ == 0
128
134
 
129
135
 
130
136
  def test_state_json_serialization__with_node_output_updates():
@@ -158,10 +164,10 @@ def test_state_deepcopy__with_external_input_updates():
158
164
  assert deepcopied_state.meta.external_inputs[MockNode.ExternalInputs.message] == "hello"
159
165
 
160
166
  # AND the original state has had the correct number of snapshots
161
- assert snapshot_count[id(state)] == 2
167
+ assert state.__snapshot_count__ == 2
162
168
 
163
169
  # AND the copied state has had the correct number of snapshots
164
- assert snapshot_count[id(deepcopied_state)] == 0
170
+ assert deepcopied_state.__snapshot_count__ == 0
165
171
 
166
172
 
167
173
  def test_state_json_serialization__with_queue():
@@ -146,7 +146,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
146
146
 
147
147
  WorkflowEvent = Union[ # type: ignore
148
148
  GenericWorkflowEvent,
149
- WorkflowExecutionInitiatedEvent[InputsType], # type: ignore[valid-type]
149
+ WorkflowExecutionInitiatedEvent[InputsType, StateType], # type: ignore[valid-type]
150
150
  WorkflowExecutionFulfilledEvent[Outputs],
151
151
  WorkflowExecutionSnapshottedEvent[StateType], # type: ignore[valid-type]
152
152
  ]
@@ -335,7 +335,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
335
335
 
336
336
  if not last_event:
337
337
  return WorkflowExecutionRejectedEvent(
338
- trace_id=uuid4(),
338
+ trace_id=self._execution_context.trace_id,
339
339
  span_id=uuid4(),
340
340
  body=WorkflowExecutionRejectedBody(
341
341
  error=WorkflowError(
@@ -348,7 +348,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
348
348
 
349
349
  if not first_event:
350
350
  return WorkflowExecutionRejectedEvent(
351
- trace_id=uuid4(),
351
+ trace_id=self._execution_context.trace_id,
352
352
  span_id=uuid4(),
353
353
  body=WorkflowExecutionRejectedBody(
354
354
  error=WorkflowError(
@@ -367,7 +367,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
367
367
  return last_event
368
368
 
369
369
  return WorkflowExecutionRejectedEvent(
370
- trace_id=first_event.trace_id,
370
+ trace_id=self._execution_context.trace_id,
371
371
  span_id=first_event.span_id,
372
372
  body=WorkflowExecutionRejectedBody(
373
373
  workflow_definition=self.__class__,
@@ -482,21 +482,11 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
482
482
  return self.get_inputs_class()()
483
483
 
484
484
  def get_default_state(self, workflow_inputs: Optional[InputsType] = None) -> StateType:
485
- execution_context = self._execution_context
486
485
  return self.get_state_class()(
487
- meta=(
488
- StateMeta(
489
- parent=self._parent_state,
490
- workflow_inputs=workflow_inputs or self.get_default_inputs(),
491
- trace_id=execution_context.trace_id,
492
- workflow_definition=self.__class__,
493
- )
494
- if execution_context and int(execution_context.trace_id)
495
- else StateMeta(
496
- parent=self._parent_state,
497
- workflow_inputs=workflow_inputs or self.get_default_inputs(),
498
- workflow_definition=self.__class__,
499
- )
486
+ meta=StateMeta(
487
+ parent=self._parent_state,
488
+ workflow_inputs=workflow_inputs or self.get_default_inputs(),
489
+ workflow_definition=self.__class__,
500
490
  )
501
491
  )
502
492
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.38
3
+ Version: 0.14.39
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -8,7 +8,7 @@ vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
8
8
  vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
9
9
  vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
10
10
  vellum_cli/pull.py,sha256=27Mh10aQ8H1OkTmQOJcOuJ5cQcYbNjkkuQrBmtkRe0o,12262
11
- vellum_cli/push.py,sha256=xjTNbLwOVFNU3kpBrm56Bk5QkSRrJ9z86qceghCzfIA,9655
11
+ vellum_cli/push.py,sha256=e2zo4PsQgEhXR3Eeg7sVJ_pdV6Rv-MswQEdCMIEdIqY,9654
12
12
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
14
14
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
@@ -17,7 +17,7 @@ vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ
17
17
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
18
18
  vellum_cli/tests/test_ping.py,sha256=3ucVRThEmTadlV9LrJdCCrr1Ofj3rOjG6ue0BNR2UC0,2523
19
19
  vellum_cli/tests/test_pull.py,sha256=5nIDX5yXLyi6p-siLvrzfaepI4RWbaoMwgFoQFUEhI0,46692
20
- vellum_cli/tests/test_push.py,sha256=zDv_Q1hbXtLwmTJDPRAvwDjbuHC09uNRYOy4FQujUow,23476
20
+ vellum_cli/tests/test_push.py,sha256=j22l7p_cy1KXdcvQKhWiM2bpu-3WL1q5IJquRm84mxE,25580
21
21
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -124,12 +124,12 @@ vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIP
124
124
  vellum_ee/workflows/tests/test_display_meta.py,sha256=C25dErwghPNXio49pvSRxyOuc96srH6eYEwTAWdE2zY,2258
125
125
  vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
126
126
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
127
- vellum/__init__.py,sha256=430zRtvdyz4CCMrfuVGV376v3E2yT_mIjZun7qe5qek,41724
127
+ vellum/__init__.py,sha256=Zq_5b5TBLmrMg94dokW3dU3ER_nhjPZQ5gP3HI6X9bE,41778
128
128
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
129
129
  vellum/client/__init__.py,sha256=Z-JHK2jGxhtTtmkLeOaUGGJWIUNYGNVBLvUewC6lp6w,118148
130
130
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
131
131
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
132
- vellum/client/core/client_wrapper.py,sha256=AgCSD62C0H6UZT5KMnqofnXDVF4tqxpuR550TMlFN0Q,1869
132
+ vellum/client/core/client_wrapper.py,sha256=KIJivfBpItYrCWMBe54yxwrVv0jkHzTimMkbw06mEAg,1869
133
133
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
134
134
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
135
135
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -197,7 +197,7 @@ vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCat
197
197
  vellum/client/resources/workspace_secrets/client.py,sha256=h7UzXLyTttPq1t-JZGMg1BWxypxJvBGUdqg7KGT7MK4,8027
198
198
  vellum/client/resources/workspaces/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
199
199
  vellum/client/resources/workspaces/client.py,sha256=RthwzN1o-Jxwg5yyNNodavFyNUSxfLoTv26w3mRR5g8,3595
200
- vellum/client/types/__init__.py,sha256=IIfGVNuXgBGmwm0-_BIpFzo9VQAGABBLytDlZwKkNyA,63167
200
+ vellum/client/types/__init__.py,sha256=jsEadfdJtvzb8rRVGPamSTE4n2-S3vA598lR3nw6mfE,63253
201
201
  vellum/client/types/ad_hoc_execute_prompt_event.py,sha256=bCjujA2XsOgyF3bRZbcEqV2rOIymRgsLoIRtZpB14xg,607
202
202
  vellum/client/types/ad_hoc_expand_meta.py,sha256=1gv-NCsy_6xBYupLvZH979yf2VMdxAU-l0y0ynMKZaw,1331
203
203
  vellum/client/types/ad_hoc_fulfilled_prompt_execution_meta.py,sha256=oDG60TpwK1YNSKhRsBbiP2O3ZF9PKR-M9chGIfKw4R4,1004
@@ -637,13 +637,14 @@ vellum/client/types/test_suite_run_metric_json_output.py,sha256=DI3mJR5kpi8Hm2n6
637
637
  vellum/client/types/test_suite_run_metric_number_output.py,sha256=8pddeSds6Rrn0xGqyvgPsG1hr1tu6eOiQAp8kkM_aBk,739
638
638
  vellum/client/types/test_suite_run_metric_output.py,sha256=z9A4_ZT9_NIHWHkFZakaf_SMn3Y13WRJ_lWzTCzXZ_U,691
639
639
  vellum/client/types/test_suite_run_metric_string_output.py,sha256=YXerGfpvJdBtKrzgutSqEfG-N6cZoeOL59qZ5k6DwQA,737
640
+ vellum/client/types/test_suite_run_progress.py,sha256=A_zazZ2jBOuM-kwfxbgp1obizRTydfNXBebXF0Pz9xo,629
640
641
  vellum/client/types/test_suite_run_prompt_sandbox_exec_config_data_request.py,sha256=FrUaFEfdZq4lyGaC_ZQpq0EyXB7_nRF865h13F-_ihs,870
641
642
  vellum/client/types/test_suite_run_prompt_sandbox_exec_config_request.py,sha256=E2W0Ec6Yy6NGHzG-sPe8bYw09g3L_H7TQhh_2I9UNLc,1135
642
643
  vellum/client/types/test_suite_run_prompt_sandbox_history_item_exec_config.py,sha256=NUJXSSLxOxVAGOoQj74m5UqjCTCCeEIgbWGP6kM2Fck,1200
643
644
  vellum/client/types/test_suite_run_prompt_sandbox_history_item_exec_config_data.py,sha256=IdlTWDda1061PwsHaoGDyB7-2lBVSus7Z8agcdmSOYE,905
644
645
  vellum/client/types/test_suite_run_prompt_sandbox_history_item_exec_config_data_request.py,sha256=0XxaQKR-pb__We2EDSoiKTcz3v-nKodIYtnwFXFQ_GM,912
645
646
  vellum/client/types/test_suite_run_prompt_sandbox_history_item_exec_config_request.py,sha256=psLq8aL-ygkW-HZZFiYerDrCrTf556RuXbOOAUuMvr8,1229
646
- vellum/client/types/test_suite_run_read.py,sha256=g6-ViS7YexJCVaZ6qNZhVZiJ8EUCRFbQXFlPaAhF23U,1439
647
+ vellum/client/types/test_suite_run_read.py,sha256=ExvHTjVxHoVehQq1lKeIkGVB7ROh90M5-dlP53IkdE0,1557
647
648
  vellum/client/types/test_suite_run_state.py,sha256=E4f_AfzXBnxhObLLZ12dBzdoYlRm-gaTqkzrZQ_KfCo,197
648
649
  vellum/client/types/test_suite_run_test_suite.py,sha256=Wcmbk1XglVFKiDcqxsW7-c7QtOrIqJBK-vWXKXvORXY,602
649
650
  vellum/client/types/test_suite_run_workflow_release_tag_exec_config.py,sha256=0ANnBKsPqBNdEoZGEfwRzZKbXbzT24T2YNC7c-3Qy1M,1144
@@ -691,7 +692,7 @@ vellum/client/types/vellum_image.py,sha256=wkFRgxOkxFPrmRdWTO58_41_vk0HYn5k4xsc-
691
692
  vellum/client/types/vellum_image_request.py,sha256=_Gr4L7PSY8PNQINyTy04hPdwLc8_bR1RTUWZ73RQRYM,644
692
693
  vellum/client/types/vellum_node_execution_event.py,sha256=64a6Gw9pGD5andcz1VOXI3fRjpoA_xiwv2T9GK6HLpA,735
693
694
  vellum/client/types/vellum_sdk_error.py,sha256=1AyCu_sUa8gx5wUJg5EtDYbvs9uQPhZfHjns1X3n8gI,656
694
- vellum/client/types/vellum_sdk_error_code_enum.py,sha256=9lqUnPRvUW9tk5GASIE57FO3bc1RJoBTGcuFk5mGMA8,460
695
+ vellum/client/types/vellum_sdk_error_code_enum.py,sha256=a2K6XJvl72tNn82zyx5QpzYGgLfYWVBKY-o8jeDqKoM,504
695
696
  vellum/client/types/vellum_secret.py,sha256=jru3nBCquZHZ3wEdpgKZNmy2WFh26PVKrTNaXeVSsZQ,555
696
697
  vellum/client/types/vellum_span.py,sha256=V5P1z-OFqgQQAZjek2toYudlh-fCu0NTt5uKuKm9hd0,258
697
698
  vellum/client/types/vellum_value.py,sha256=Un9AeNlx0QZA9-eIGgA5nEdZU0_0vHfibiafJR0q8V4,1049
@@ -718,7 +719,7 @@ vellum/client/types/workflow_execution_actual.py,sha256=8-eGI91UTAw2g3EpSC-axH_O
718
719
  vellum/client/types/workflow_execution_actual_chat_history_request.py,sha256=L6U8tgM7SiU4qGJMZChFzj6HfHgO-YAlTXfbT7ZIaE4,1993
719
720
  vellum/client/types/workflow_execution_actual_json_request.py,sha256=5QYaPCSOwFnjH_kTrB2bTznTMFExSZdBhTkmelf1h4Q,1931
720
721
  vellum/client/types/workflow_execution_actual_string_request.py,sha256=1optEDv090iVev1l0Z9cgZ1NfNrHp2VRiNjmS7f7jtc,1895
721
- vellum/client/types/workflow_execution_event_error_code.py,sha256=UQihvr26uUXt1UU6ngIOmYAk3QepeUVTymla5ulCNn8,451
722
+ vellum/client/types/workflow_execution_event_error_code.py,sha256=QzRLZ0dteptDmt3n5_vw26zGgDrpvcR4vRa_mtl2sMI,495
722
723
  vellum/client/types/workflow_execution_event_type.py,sha256=ESKqV3ItoAlqBooruf-i0AnmEh_GvCySZ0Co3r9Bvt0,170
723
724
  vellum/client/types/workflow_execution_fulfilled_body.py,sha256=4M9fQdJy7nH5Y1HLq9VVlfTNW4F9jlj4k5SWty9fFqQ,746
724
725
  vellum/client/types/workflow_execution_fulfilled_event.py,sha256=ud3jxSpoo3D85vyEewsf4Eqv1AdBfwclOGmJu9lfOv8,2412
@@ -1310,6 +1311,7 @@ vellum/types/test_suite_run_metric_json_output.py,sha256=mZIXhFS0IoauRhNhwtO_nBe
1310
1311
  vellum/types/test_suite_run_metric_number_output.py,sha256=FRat-EjmPl4eyK307tPnQ8U3prAPsqgXVLdRmYXxaGw,173
1311
1312
  vellum/types/test_suite_run_metric_output.py,sha256=QvexO_ontwejyAQBipmrvTMxJRZrFu5ja_g8HBjDpiI,166
1312
1313
  vellum/types/test_suite_run_metric_string_output.py,sha256=_az-yxsYHgariEfKeFN1UtCyW1rXxCiOsVZj-INNqa8,173
1314
+ vellum/types/test_suite_run_progress.py,sha256=ZVzd4oCv7K2UC5HJ0q2hXzIEAwtDoZK7BLP7TDreO4k,161
1313
1315
  vellum/types/test_suite_run_prompt_sandbox_exec_config_data_request.py,sha256=_AVqGprc3VUZwDrnieSETimiJ3lEHxuuQlQEb29yfV4,192
1314
1316
  vellum/types/test_suite_run_prompt_sandbox_exec_config_request.py,sha256=wDlHjrkMPdexhv5lN4hr0BMprSLKohXjnALJx44N7CE,187
1315
1317
  vellum/types/test_suite_run_prompt_sandbox_history_item_exec_config.py,sha256=aShS-YpR-mxoj59s1pF1qSx7NWSrUSuDC59kXesT33Y,192
@@ -1481,13 +1483,13 @@ vellum/workflows/emitters/base.py,sha256=D5SADKIvnbgKwIBgYm77jaqvpo1o0rz4MmuX_mu
1481
1483
  vellum/workflows/environment/__init__.py,sha256=wGHslgSEZ7Octe4C-hNtl84EFelNimgmWQoi7px4-uw,71
1482
1484
  vellum/workflows/environment/environment.py,sha256=0XhJPBs8YASWmvPx8bkSdCvcbDmzpe9stfs2kgtNDRU,296
1483
1485
  vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
1484
- vellum/workflows/errors/types.py,sha256=tVW7Il9zalnwWzdoDLqYPIvRTOhXIv6FPORZAbU7n5Q,3640
1486
+ vellum/workflows/errors/types.py,sha256=_A0J3ACjg5dAR2TLI1KHSNPI-oDGkt_9nDk5a0NAjTY,3714
1485
1487
  vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
1486
1488
  vellum/workflows/events/node.py,sha256=TaawUxKaZG8cv_GkiKnJmjOmC4Ic0wMlsxUduF2Rbpw,5446
1487
1489
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1488
- vellum/workflows/events/tests/test_event.py,sha256=WRxjOO1470rFH40O56RWjhonIdupW782h_FRAhIQZCQ,17823
1490
+ vellum/workflows/events/tests/test_event.py,sha256=elg5j3MOdWQe-JjshopqVwd716Jrvtr9P_V2JUGZC60,17866
1489
1491
  vellum/workflows/events/types.py,sha256=LwgFlMRbptJvdPtPO1POUtGtbhGw7BSuvgHxNSgS7N8,4652
1490
- vellum/workflows/events/workflow.py,sha256=WL6RDtlPnTHNGvR6Vk7KQG1AvgnOaBnnmSG8VZnYkKI,7338
1492
+ vellum/workflows/events/workflow.py,sha256=i9JSCANjAhf5uc57gYspdII2V2OLItbc0BfT8yB9mF0,7728
1491
1493
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1492
1494
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1493
1495
  vellum/workflows/expressions/accessor.py,sha256=ItZF7fMLzVTqsdAiaXb5SiDupXmX0X9xbIus1W6hRds,1870
@@ -1568,7 +1570,7 @@ vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN
1568
1570
  vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
1569
1571
  vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=70pLGU0UzWvSbKwNkx3YlUYrDSkl7MmhVHoI8bzN79c,4343
1570
1572
  vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
1571
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=HGNoGLJ9lbqflGdYFDIiuHFyi0iJ-agJu4kkJ7D3dGs,3212
1573
+ vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=amBXi7Tv50AbGLhfWbwX83PlOdV1XyYRyQmpa6_afE4,3511
1572
1574
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1573
1575
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
1574
1576
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=rga24gkK9_STRhFwhBwGL7oHhTTZvLWS_rXHHrp85p4,8386
@@ -1602,7 +1604,7 @@ vellum/workflows/nodes/displayable/guardrail_node/tests/test_node.py,sha256=X2pd
1602
1604
  vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py,sha256=gSUOoEZLlrx35-tQhSAd3An8WDwBqyiQh-sIebLU9wU,74
1603
1605
  vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=8RXZqWMzViUjFfbpmcy1gkSsKnEpci8BGwsuPYv4xMQ,3380
1604
1606
  vellum/workflows/nodes/displayable/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1605
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=2Xg37rrzWA5-LrLjO3yQQN0hMn6gDQWyPv6Lye64ujQ,10663
1607
+ vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=bBHs90mV5SZ3rJPAL0wx4WWyawUA406LgMPOdvpZC_A,10923
1606
1608
  vellum/workflows/nodes/displayable/merge_node/__init__.py,sha256=J8IC08dSH7P76wKlNuxe1sn7toNGtSQdFirUbtPDEs0,60
1607
1609
  vellum/workflows/nodes/displayable/merge_node/node.py,sha256=nZtGGVAvY4fvGg8vwV6sTQ8_QLRnigeXt0vf2FL272A,450
1608
1610
  vellum/workflows/nodes/displayable/note_node/__init__.py,sha256=KWA3P4fyYJ-fOTky8qNGlcOotQ-HeHJ9AjZt6mRQmCE,58
@@ -1610,7 +1612,7 @@ vellum/workflows/nodes/displayable/note_node/node.py,sha256=sIN1VBQ7zeT3GhN0kupX
1610
1612
  vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py,sha256=krX1Hds-TSVYZsx0wJFX4wsAKkEFYOX1ifwRGiIM-EA,82
1611
1613
  vellum/workflows/nodes/displayable/prompt_deployment_node/node.py,sha256=eUiQYdqJTrWhVcUgGAPJYEnRk6S71Yrzu5-c-XcVFs4,3243
1612
1614
  vellum/workflows/nodes/displayable/prompt_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1613
- vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py,sha256=AM-W95ck2GWmG2skC3Lkx1Ng-RUJXg0185J1mdt9sDs,18772
1615
+ vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py,sha256=c_nuuqrwiIjgj4qIbVypfDuOc-3TlgO6CbXFqQl2Nqw,19725
1614
1616
  vellum/workflows/nodes/displayable/search_node/__init__.py,sha256=hpBpvbrDYf43DElRZFLzieSn8weXiwNiiNOJurERQbs,62
1615
1617
  vellum/workflows/nodes/displayable/search_node/node.py,sha256=_VHHuTNN4icZBgc7O5U9SVKrv1zgKipU72fOtxTyrQU,1453
1616
1618
  vellum/workflows/nodes/displayable/search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1654,15 +1656,15 @@ vellum/workflows/references/workflow_input.py,sha256=lq7BiiLBHQNP-vP2p1TN2QBq0_L
1654
1656
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1655
1657
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1656
1658
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1657
- vellum/workflows/runner/runner.py,sha256=94A--MUhXNct3kNMsdZrEKsjNsFo7mBQvx-Ddz3qddI,31949
1659
+ vellum/workflows/runner/runner.py,sha256=1ozidWAFecR7nfpj_Uw33SBGJVZY54JyU0AA3wySqaA,32826
1658
1660
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1659
1661
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1660
- vellum/workflows/state/base.py,sha256=nLXsa85JSVb-4lFN0i2I1tzJq51zbcPDD12ps-SwRGQ,20894
1662
+ vellum/workflows/state/base.py,sha256=Rj4UWh_YfPNZ4ISGv5ltVf8s0zdu4yGXhwYwEhO1Wf4,21353
1661
1663
  vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
1662
1664
  vellum/workflows/state/encoder.py,sha256=TnOQojc5lTQ83g9QbpA4UCqShJvutmTMxbpKt-9gNe4,1911
1663
1665
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
1664
1666
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1665
- vellum/workflows/state/tests/test_state.py,sha256=m8f0jPTnK3i0W3wj5GTTRcetRrIBqBMklSA7Vev2nsg,6491
1667
+ vellum/workflows/state/tests/test_state.py,sha256=hkxaGBfsbWyzSfMM-hfFktMQEFI3kIdfm5a4XQwk-Z8,6684
1666
1668
  vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1667
1669
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
1668
1670
  vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
@@ -1686,13 +1688,13 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
1686
1688
  vellum/workflows/utils/vellum_variables.py,sha256=UiGlUh0a8vel2FbW3w-xbHxSv_jNutkDdqMVtP_b42A,3385
1687
1689
  vellum/workflows/vellum_client.py,sha256=GxOy3dX6A04xiY69vPv1S4YGuQ_TMxwHi6WRMimQBBE,762
1688
1690
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1689
- vellum/workflows/workflows/base.py,sha256=zdBOFZaCs5jNYrsYsnPtyD58qTne5oOwDLxw42zKwkY,23784
1691
+ vellum/workflows/workflows/base.py,sha256=X2X2-v_kN9dqkkhHfkPFpZ3k-ldURBF3CBKmi8js7A8,23392
1690
1692
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1691
1693
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1692
1694
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=rBLsUsoLdRRQ-JBEfUivV9qVCjdqaiDsCPNyHvqz9fc,17361
1693
1695
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1694
- vellum_ai-0.14.38.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1695
- vellum_ai-0.14.38.dist-info/METADATA,sha256=4W7jvgNhHoXv4MdRLeJNEKfyvBGhEkxXrKTVcx-RA9E,5484
1696
- vellum_ai-0.14.38.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1697
- vellum_ai-0.14.38.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1698
- vellum_ai-0.14.38.dist-info/RECORD,,
1696
+ vellum_ai-0.14.39.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1697
+ vellum_ai-0.14.39.dist-info/METADATA,sha256=j--ftKNWYCJ3_QoC58vOMR75cDgcc0XqjmkFgo0Sp90,5484
1698
+ vellum_ai-0.14.39.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1699
+ vellum_ai-0.14.39.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1700
+ vellum_ai-0.14.39.dist-info/RECORD,,
vellum_cli/push.py CHANGED
@@ -36,9 +36,6 @@ def push_command(
36
36
  logger = load_cli_logger()
37
37
  config = load_vellum_cli_config()
38
38
 
39
- if not config.workflows:
40
- raise ValueError("No Workflows found in project to push.")
41
-
42
39
  workflow_configs = (
43
40
  [
44
41
  w
@@ -61,6 +58,8 @@ def push_command(
61
58
  )
62
59
  config.workflows.append(new_config)
63
60
  workflow_configs = [new_config]
61
+ elif not module:
62
+ raise ValueError("No Workflows found in project to push.")
64
63
  else:
65
64
  raise ValueError(f"No workflow config for '{module}' found in project to push.")
66
65
 
@@ -133,6 +133,58 @@ def test_push__happy_path(mock_module, vellum_client, base_command):
133
133
  assert extracted_files["workflow.py"] == workflow_py_file_content
134
134
 
135
135
 
136
+ def test_push__no_config__module_found(mock_module, vellum_client):
137
+ # GIVEN no config file set
138
+ temp_dir = mock_module.temp_dir
139
+ module = mock_module.module
140
+ mock_module.set_pyproject_toml({"workflows": []})
141
+
142
+ # AND a workflow exists in the module successfully
143
+ workflow_py_file_content = _ensure_workflow_py(temp_dir, module)
144
+
145
+ # AND the push API call returns successfully
146
+ new_workflow_sandbox_id = str(uuid4())
147
+ vellum_client.workflows.push.return_value = WorkflowPushResponse(
148
+ workflow_sandbox_id=new_workflow_sandbox_id,
149
+ )
150
+
151
+ # WHEN calling `vellum push`
152
+ runner = CliRunner()
153
+ result = runner.invoke(cli_main, ["push", module])
154
+
155
+ # THEN it should successfully push the workflow
156
+ assert result.exit_code == 0, result.stdout
157
+
158
+ # Get the last part of the module path and format it
159
+ expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
160
+
161
+ # AND we should have called the push API with the correct args
162
+ vellum_client.workflows.push.assert_called_once()
163
+ call_args = vellum_client.workflows.push.call_args.kwargs
164
+ assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
165
+ assert call_args["workflow_sandbox_id"] is None
166
+ assert "deplyment_config" not in call_args
167
+
168
+ # AND we should have pushed the correct artifact
169
+ assert call_args["artifact"].name == expected_artifact_name
170
+ extracted_files = _extract_tar_gz(call_args["artifact"].read())
171
+ assert extracted_files["workflow.py"] == workflow_py_file_content
172
+
173
+ # AND there should be a new entry in the lock file
174
+ with open(os.path.join(temp_dir, "vellum.lock.json")) as f:
175
+ lock_file_content = json.load(f)
176
+ assert lock_file_content["workflows"][0] == {
177
+ "container_image_name": None,
178
+ "container_image_tag": None,
179
+ "deployments": [],
180
+ "ignore": None,
181
+ "module": module,
182
+ "target_directory": None,
183
+ "workflow_sandbox_id": new_workflow_sandbox_id,
184
+ "workspace": "default",
185
+ }
186
+
187
+
136
188
  @pytest.mark.parametrize(
137
189
  "base_command",
138
190
  [