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 +2 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/types/__init__.py +2 -0
- vellum/client/types/test_suite_run_progress.py +20 -0
- vellum/client/types/test_suite_run_read.py +3 -0
- vellum/client/types/vellum_sdk_error_code_enum.py +1 -0
- vellum/client/types/workflow_execution_event_error_code.py +1 -0
- vellum/types/test_suite_run_progress.py +3 -0
- vellum/workflows/errors/types.py +1 -0
- vellum/workflows/events/tests/test_event.py +1 -0
- vellum/workflows/events/workflow.py +13 -3
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +7 -1
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +6 -1
- vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py +26 -0
- vellum/workflows/runner/runner.py +37 -17
- vellum/workflows/state/base.py +28 -5
- vellum/workflows/state/tests/test_state.py +26 -20
- vellum/workflows/workflows/base.py +8 -18
- {vellum_ai-0.14.38.dist-info → vellum_ai-0.14.39.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.38.dist-info → vellum_ai-0.14.39.dist-info}/RECORD +25 -23
- vellum_cli/push.py +2 -3
- vellum_cli/tests/test_push.py +52 -0
- {vellum_ai-0.14.38.dist-info → vellum_ai-0.14.39.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.38.dist-info → vellum_ai-0.14.39.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.38.dist-info → vellum_ai-0.14.39.dist-info}/entry_points.txt +0 -0
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.
|
21
|
+
"X-Fern-SDK-Version": "0.14.39",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
vellum/client/types/__init__.py
CHANGED
@@ -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:
|
vellum/workflows/errors/types.py
CHANGED
@@ -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"
|
@@ -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
|
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
|
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.
|
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
|
-
|
301
|
-
|
302
|
-
if
|
303
|
-
|
304
|
-
|
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
|
-
|
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(
|
369
|
-
|
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
|
372
|
-
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
|
-
|
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={
|
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
|
)
|
vellum/workflows/state/base.py
CHANGED
@@ -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
|
-
|
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.
|
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
|
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.
|
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("_")
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
130
|
+
assert state.__snapshot_count__ == 2
|
125
131
|
|
126
132
|
# AND the copied state has had the correct number of snapshots
|
127
|
-
assert
|
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
|
167
|
+
assert state.__snapshot_count__ == 2
|
162
168
|
|
163
169
|
# AND the copied state has had the correct number of snapshots
|
164
|
-
assert
|
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=
|
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=
|
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=
|
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
|
-
|
489
|
-
|
490
|
-
|
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
|
|
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
1695
|
-
vellum_ai-0.14.
|
1696
|
-
vellum_ai-0.14.
|
1697
|
-
vellum_ai-0.14.
|
1698
|
-
vellum_ai-0.14.
|
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
|
|
vellum_cli/tests/test_push.py
CHANGED
@@ -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
|
[
|
File without changes
|
File without changes
|
File without changes
|