vellum-ai 1.2.4__py3-none-any.whl → 1.3.0__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 +56 -0
- vellum/client/README.md +1 -1
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/reference.md +0 -9
- vellum/client/resources/workflow_sandboxes/client.py +0 -12
- vellum/client/resources/workflow_sandboxes/raw_client.py +2 -10
- vellum/client/resources/workflows/client.py +20 -0
- vellum/client/resources/workflows/raw_client.py +20 -0
- vellum/client/types/__init__.py +56 -0
- vellum/client/types/audio_input.py +30 -0
- vellum/client/types/code_executor_input.py +8 -0
- vellum/client/types/deployment_read.py +5 -5
- vellum/client/types/document_input.py +30 -0
- vellum/client/types/image_input.py +30 -0
- vellum/client/types/named_scenario_input_audio_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_document_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_image_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_request.py +8 -0
- vellum/client/types/named_scenario_input_video_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_audio_variable_value.py +26 -0
- vellum/client/types/named_test_case_audio_variable_value_request.py +26 -0
- vellum/client/types/named_test_case_document_variable_value.py +22 -0
- vellum/client/types/named_test_case_document_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_image_variable_value.py +22 -0
- vellum/client/types/named_test_case_image_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_variable_value.py +8 -0
- vellum/client/types/named_test_case_variable_value_request.py +8 -0
- vellum/client/types/named_test_case_video_variable_value.py +22 -0
- vellum/client/types/named_test_case_video_variable_value_request.py +22 -0
- vellum/client/types/node_execution_span_attributes.py +1 -0
- vellum/client/types/scenario_input.py +11 -1
- vellum/client/types/scenario_input_audio_variable_value.py +22 -0
- vellum/client/types/scenario_input_document_variable_value.py +22 -0
- vellum/client/types/scenario_input_image_variable_value.py +22 -0
- vellum/client/types/scenario_input_video_variable_value.py +22 -0
- vellum/client/types/slim_deployment_read.py +5 -5
- vellum/client/types/slim_workflow_deployment.py +5 -5
- vellum/client/types/span_link.py +1 -1
- vellum/client/types/span_link_type_enum.py +1 -1
- vellum/client/types/test_case_audio_variable_value.py +27 -0
- vellum/client/types/test_case_document_variable_value.py +27 -0
- vellum/client/types/test_case_image_variable_value.py +27 -0
- vellum/client/types/test_case_variable_value.py +8 -0
- vellum/client/types/test_case_video_variable_value.py +27 -0
- vellum/client/types/video_input.py +30 -0
- vellum/client/types/workflow_deployment_read.py +5 -5
- vellum/client/types/workflow_push_deployment_config_request.py +1 -0
- vellum/client/types/workflow_request_audio_input_request.py +30 -0
- vellum/client/types/workflow_request_document_input_request.py +30 -0
- vellum/client/types/workflow_request_image_input_request.py +30 -0
- vellum/client/types/workflow_request_input_request.py +8 -0
- vellum/client/types/workflow_request_video_input_request.py +30 -0
- vellum/types/audio_input.py +3 -0
- vellum/types/document_input.py +3 -0
- vellum/types/image_input.py +3 -0
- vellum/types/named_scenario_input_audio_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_document_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_image_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_video_variable_value_request.py +3 -0
- vellum/types/named_test_case_audio_variable_value.py +3 -0
- vellum/types/named_test_case_audio_variable_value_request.py +3 -0
- vellum/types/named_test_case_document_variable_value.py +3 -0
- vellum/types/named_test_case_document_variable_value_request.py +3 -0
- vellum/types/named_test_case_image_variable_value.py +3 -0
- vellum/types/named_test_case_image_variable_value_request.py +3 -0
- vellum/types/named_test_case_video_variable_value.py +3 -0
- vellum/types/named_test_case_video_variable_value_request.py +3 -0
- vellum/types/scenario_input_audio_variable_value.py +3 -0
- vellum/types/scenario_input_document_variable_value.py +3 -0
- vellum/types/scenario_input_image_variable_value.py +3 -0
- vellum/types/scenario_input_video_variable_value.py +3 -0
- vellum/types/test_case_audio_variable_value.py +3 -0
- vellum/types/test_case_document_variable_value.py +3 -0
- vellum/types/test_case_image_variable_value.py +3 -0
- vellum/types/test_case_video_variable_value.py +3 -0
- vellum/types/video_input.py +3 -0
- vellum/types/workflow_request_audio_input_request.py +3 -0
- vellum/types/workflow_request_document_input_request.py +3 -0
- vellum/types/workflow_request_image_input_request.py +3 -0
- vellum/types/workflow_request_video_input_request.py +3 -0
- vellum/workflows/events/types.py +6 -1
- vellum/workflows/integrations/tests/test_mcp_service.py +106 -1
- vellum/workflows/nodes/__init__.py +2 -0
- vellum/workflows/nodes/displayable/__init__.py +2 -0
- vellum/workflows/nodes/displayable/web_search_node/__init__.py +3 -0
- vellum/workflows/nodes/displayable/web_search_node/node.py +133 -0
- vellum/workflows/resolvers/base.py +19 -1
- vellum/workflows/resolvers/resolver.py +97 -0
- vellum/workflows/resolvers/tests/test_resolver.py +131 -0
- vellum/workflows/resolvers/types.py +11 -0
- vellum/workflows/runner/runner.py +49 -1
- vellum/workflows/state/context.py +41 -7
- vellum/workflows/utils/zip.py +46 -0
- vellum/workflows/workflows/base.py +13 -0
- {vellum_ai-1.2.4.dist-info → vellum_ai-1.3.0.dist-info}/METADATA +1 -1
- {vellum_ai-1.2.4.dist-info → vellum_ai-1.3.0.dist-info}/RECORD +105 -43
- vellum_cli/tests/test_init.py +7 -24
- vellum_cli/tests/test_pull.py +27 -52
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +7 -33
- vellum_ee/workflows/display/utils/events.py +19 -1
- vellum_ee/workflows/display/utils/tests/test_events.py +42 -0
- vellum_ee/workflows/tests/test_server.py +115 -0
- {vellum_ai-1.2.4.dist-info → vellum_ai-1.3.0.dist-info}/LICENSE +0 -0
- {vellum_ai-1.2.4.dist-info → vellum_ai-1.3.0.dist-info}/WHEEL +0 -0
- {vellum_ai-1.2.4.dist-info → vellum_ai-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from unittest.mock import Mock
|
3
|
+
from uuid import uuid4
|
4
|
+
|
5
|
+
from vellum.client.types.span_link import SpanLink
|
6
|
+
from vellum.client.types.vellum_code_resource_definition import VellumCodeResourceDefinition
|
7
|
+
from vellum.client.types.workflow_execution_detail import WorkflowExecutionDetail
|
8
|
+
from vellum.client.types.workflow_execution_initiated_body import WorkflowExecutionInitiatedBody
|
9
|
+
from vellum.client.types.workflow_execution_initiated_event import WorkflowExecutionInitiatedEvent
|
10
|
+
from vellum.client.types.workflow_execution_span import WorkflowExecutionSpan
|
11
|
+
from vellum.client.types.workflow_execution_span_attributes import WorkflowExecutionSpanAttributes
|
12
|
+
from vellum.client.types.workflow_parent_context import WorkflowParentContext
|
13
|
+
from vellum.workflows import BaseWorkflow
|
14
|
+
from vellum.workflows.inputs.base import BaseInputs
|
15
|
+
from vellum.workflows.resolvers.resolver import VellumResolver
|
16
|
+
from vellum.workflows.resolvers.types import LoadStateResult
|
17
|
+
from vellum.workflows.state.base import BaseState, NodeExecutionCache
|
18
|
+
from vellum.workflows.state.context import WorkflowContext
|
19
|
+
|
20
|
+
|
21
|
+
def test_load_state_with_context_success():
|
22
|
+
"""Test load_state successfully loads state when context and client are available."""
|
23
|
+
resolver = VellumResolver()
|
24
|
+
execution_id = uuid4()
|
25
|
+
root_execution_id = uuid4()
|
26
|
+
|
27
|
+
class TestState(BaseState):
|
28
|
+
test_key: str = "test_value"
|
29
|
+
|
30
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, TestState]):
|
31
|
+
pass
|
32
|
+
|
33
|
+
# GIVEN a state dictionary that matches what the resolver expects
|
34
|
+
prev_id = str(uuid4())
|
35
|
+
prev_span_id = str(uuid4())
|
36
|
+
state_dict = {
|
37
|
+
"test_key": "test_value",
|
38
|
+
"meta": {
|
39
|
+
"workflow_definition": "MockWorkflow",
|
40
|
+
"id": prev_id,
|
41
|
+
"span_id": prev_span_id,
|
42
|
+
"updated_ts": datetime.now().isoformat(),
|
43
|
+
"workflow_inputs": BaseInputs(),
|
44
|
+
"external_inputs": {},
|
45
|
+
"node_outputs": {},
|
46
|
+
"node_execution_cache": NodeExecutionCache(),
|
47
|
+
"parent": None,
|
48
|
+
},
|
49
|
+
}
|
50
|
+
|
51
|
+
mock_workflow_definition = VellumCodeResourceDefinition(
|
52
|
+
name="TestWorkflow", module=["test", "module"], id=str(uuid4())
|
53
|
+
)
|
54
|
+
|
55
|
+
mock_body = WorkflowExecutionInitiatedBody(workflow_definition=mock_workflow_definition, inputs={})
|
56
|
+
|
57
|
+
previous_trace_id = str(uuid4())
|
58
|
+
root_trace_id = str(uuid4())
|
59
|
+
|
60
|
+
previous_invocation = WorkflowExecutionInitiatedEvent(
|
61
|
+
id=str(uuid4()),
|
62
|
+
timestamp=datetime.now(),
|
63
|
+
trace_id=previous_trace_id,
|
64
|
+
span_id=str(execution_id),
|
65
|
+
body=mock_body,
|
66
|
+
links=[
|
67
|
+
SpanLink(
|
68
|
+
trace_id=previous_trace_id,
|
69
|
+
type="PREVIOUS_SPAN",
|
70
|
+
span_context=WorkflowParentContext(workflow_definition=mock_workflow_definition, span_id=str(uuid4())),
|
71
|
+
),
|
72
|
+
SpanLink(
|
73
|
+
trace_id=root_trace_id,
|
74
|
+
type="ROOT_SPAN",
|
75
|
+
span_context=WorkflowParentContext(
|
76
|
+
workflow_definition=mock_workflow_definition, span_id=str(root_execution_id)
|
77
|
+
),
|
78
|
+
),
|
79
|
+
],
|
80
|
+
)
|
81
|
+
|
82
|
+
root_invocation = WorkflowExecutionInitiatedEvent(
|
83
|
+
id=str(uuid4()),
|
84
|
+
timestamp=datetime.now(),
|
85
|
+
trace_id=root_trace_id,
|
86
|
+
span_id=str(root_execution_id),
|
87
|
+
body=mock_body,
|
88
|
+
links=None, # Root invocation has no links
|
89
|
+
)
|
90
|
+
|
91
|
+
mock_span = WorkflowExecutionSpan(
|
92
|
+
span_id=str(execution_id), # Use the actual execution_id
|
93
|
+
start_ts=datetime.now(),
|
94
|
+
end_ts=datetime.now(),
|
95
|
+
attributes=WorkflowExecutionSpanAttributes(label="Test Workflow", workflow_id=str(uuid4())),
|
96
|
+
events=[previous_invocation, root_invocation],
|
97
|
+
)
|
98
|
+
|
99
|
+
mock_response = WorkflowExecutionDetail(
|
100
|
+
span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[mock_span], state=state_dict
|
101
|
+
)
|
102
|
+
|
103
|
+
mock_client = Mock()
|
104
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.return_value = mock_response
|
105
|
+
|
106
|
+
# AND context with the test workflow class is set up
|
107
|
+
context = WorkflowContext(vellum_client=mock_client)
|
108
|
+
TestWorkflow(context=context, resolvers=[resolver])
|
109
|
+
|
110
|
+
# WHEN load_state is called
|
111
|
+
result = resolver.load_state(previous_execution_id=execution_id)
|
112
|
+
|
113
|
+
# THEN should return LoadStateResult with state and span link info
|
114
|
+
assert isinstance(result, LoadStateResult)
|
115
|
+
assert result.state is not None
|
116
|
+
assert isinstance(result.state, TestState)
|
117
|
+
assert result.state.test_key == "test_value"
|
118
|
+
|
119
|
+
# AND the new state should have different meta IDs than those provided in the loaded state_dict
|
120
|
+
assert str(result.state.meta.id) != prev_id
|
121
|
+
assert str(result.state.meta.span_id) != prev_span_id
|
122
|
+
|
123
|
+
# AND should have span link info
|
124
|
+
assert result.previous_trace_id == previous_invocation.trace_id
|
125
|
+
assert result.previous_span_id == previous_invocation.span_id
|
126
|
+
assert result.root_trace_id == root_invocation.trace_id
|
127
|
+
assert result.root_span_id == root_invocation.span_id
|
128
|
+
|
129
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
|
130
|
+
execution_id=str(execution_id)
|
131
|
+
)
|
@@ -46,7 +46,7 @@ from vellum.workflows.events.node import (
|
|
46
46
|
NodeExecutionRejectedBody,
|
47
47
|
NodeExecutionStreamingBody,
|
48
48
|
)
|
49
|
-
from vellum.workflows.events.types import BaseEvent, NodeParentContext, ParentContext, WorkflowParentContext
|
49
|
+
from vellum.workflows.events.types import BaseEvent, NodeParentContext, ParentContext, SpanLink, WorkflowParentContext
|
50
50
|
from vellum.workflows.events.workflow import (
|
51
51
|
WorkflowEventStream,
|
52
52
|
WorkflowExecutionFulfilledBody,
|
@@ -99,6 +99,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
99
99
|
state: Optional[StateType] = None,
|
100
100
|
entrypoint_nodes: Optional[RunFromNodeArg] = None,
|
101
101
|
external_inputs: Optional[ExternalInputsArg] = None,
|
102
|
+
previous_execution_id: Optional[Union[str, UUID]] = None,
|
102
103
|
cancel_signal: Optional[ThreadingEvent] = None,
|
103
104
|
node_output_mocks: Optional[MockNodeExecutionArg] = None,
|
104
105
|
max_concurrency: Optional[int] = None,
|
@@ -110,6 +111,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
110
111
|
self.workflow = workflow
|
111
112
|
self._is_resuming = False
|
112
113
|
self._should_emit_initial_state = True
|
114
|
+
self._span_link_info: Optional[Tuple[str, str, str, str]] = None
|
113
115
|
if entrypoint_nodes:
|
114
116
|
if len(list(entrypoint_nodes)) > 1:
|
115
117
|
raise ValueError("Cannot resume from multiple nodes")
|
@@ -140,6 +142,28 @@ class WorkflowRunner(Generic[StateType]):
|
|
140
142
|
if issubclass(ei.inputs_class.__parent_class__, BaseNode)
|
141
143
|
]
|
142
144
|
self._is_resuming = True
|
145
|
+
elif previous_execution_id:
|
146
|
+
for resolver in self.workflow.resolvers:
|
147
|
+
try:
|
148
|
+
load_state_result = resolver.load_state(previous_execution_id)
|
149
|
+
if load_state_result is not None:
|
150
|
+
state_class = self.workflow.get_state_class()
|
151
|
+
if isinstance(load_state_result.state, state_class):
|
152
|
+
self._initial_state = load_state_result.state
|
153
|
+
normalized_inputs = deepcopy(inputs) if inputs else self.workflow.get_default_inputs()
|
154
|
+
self._initial_state.meta.workflow_inputs = normalized_inputs
|
155
|
+
self._initial_state.meta.workflow_definition = self.workflow.__class__
|
156
|
+
self._span_link_info = (
|
157
|
+
load_state_result.previous_trace_id,
|
158
|
+
load_state_result.previous_span_id,
|
159
|
+
load_state_result.root_trace_id,
|
160
|
+
load_state_result.root_span_id,
|
161
|
+
)
|
162
|
+
break
|
163
|
+
except Exception as e:
|
164
|
+
logger.warning(f"Failed to load state from resolver {type(resolver).__name__}: {e}")
|
165
|
+
continue
|
166
|
+
self._entrypoints = self.workflow.get_entrypoints()
|
143
167
|
else:
|
144
168
|
normalized_inputs = deepcopy(inputs) if inputs else self.workflow.get_default_inputs()
|
145
169
|
if state:
|
@@ -627,6 +651,29 @@ class WorkflowRunner(Generic[StateType]):
|
|
627
651
|
return None
|
628
652
|
|
629
653
|
def _initiate_workflow_event(self) -> WorkflowExecutionInitiatedEvent:
|
654
|
+
links: Optional[List[SpanLink]] = None
|
655
|
+
|
656
|
+
if self._span_link_info:
|
657
|
+
previous_trace_id, previous_span_id, root_trace_id, root_span_id = self._span_link_info
|
658
|
+
links = [
|
659
|
+
SpanLink(
|
660
|
+
trace_id=previous_trace_id,
|
661
|
+
type="PREVIOUS_SPAN",
|
662
|
+
span_context=WorkflowParentContext(
|
663
|
+
workflow_definition=self.workflow.__class__,
|
664
|
+
span_id=previous_span_id,
|
665
|
+
),
|
666
|
+
),
|
667
|
+
SpanLink(
|
668
|
+
trace_id=root_trace_id,
|
669
|
+
type="ROOT_SPAN",
|
670
|
+
span_context=WorkflowParentContext(
|
671
|
+
workflow_definition=self.workflow.__class__,
|
672
|
+
span_id=root_span_id,
|
673
|
+
),
|
674
|
+
),
|
675
|
+
]
|
676
|
+
|
630
677
|
return WorkflowExecutionInitiatedEvent(
|
631
678
|
trace_id=self._execution_context.trace_id,
|
632
679
|
span_id=self._initial_state.meta.span_id,
|
@@ -636,6 +683,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
636
683
|
initial_state=deepcopy(self._initial_state) if self._should_emit_initial_state else None,
|
637
684
|
),
|
638
685
|
parent=self._execution_context.parent_context,
|
686
|
+
links=links,
|
639
687
|
)
|
640
688
|
|
641
689
|
def _stream_workflow_event(self, output: BaseOutput) -> WorkflowExecutionStreamingEvent:
|
@@ -1,15 +1,16 @@
|
|
1
1
|
from functools import cached_property
|
2
2
|
from queue import Queue
|
3
|
-
from uuid import uuid4
|
3
|
+
from uuid import UUID, uuid4
|
4
4
|
from typing import TYPE_CHECKING, Dict, List, Optional, Type
|
5
5
|
|
6
|
-
from vellum import Vellum
|
6
|
+
from vellum import Vellum, __version__
|
7
7
|
from vellum.workflows.context import ExecutionContext, get_execution_context, set_execution_context
|
8
8
|
from vellum.workflows.events.types import ExternalParentContext
|
9
9
|
from vellum.workflows.nodes.mocks import MockNodeExecution, MockNodeExecutionArg
|
10
10
|
from vellum.workflows.outputs.base import BaseOutputs
|
11
11
|
from vellum.workflows.references.constant import ConstantValueReference
|
12
12
|
from vellum.workflows.utils.uuids import generate_workflow_deployment_prefix
|
13
|
+
from vellum.workflows.utils.zip import extract_zip_files
|
13
14
|
from vellum.workflows.vellum_client import create_vellum_client
|
14
15
|
|
15
16
|
if TYPE_CHECKING:
|
@@ -40,6 +41,9 @@ class WorkflowContext:
|
|
40
41
|
|
41
42
|
if self._execution_context.parent_context is None:
|
42
43
|
self._execution_context.parent_context = ExternalParentContext(span_id=uuid4())
|
44
|
+
# Only generate a new trace_id if one wasn't explicitly provided (i.e., if it's the default zero UUID)
|
45
|
+
if self._execution_context.trace_id == UUID("00000000-0000-0000-0000-000000000000"):
|
46
|
+
self._execution_context.trace_id = uuid4()
|
43
47
|
# Propagate the updated context back to the global execution context
|
44
48
|
set_execution_context(self._execution_context)
|
45
49
|
|
@@ -156,23 +160,53 @@ class WorkflowContext:
|
|
156
160
|
Returns:
|
157
161
|
BaseWorkflow instance if found, None otherwise
|
158
162
|
"""
|
159
|
-
if not self.
|
163
|
+
if not self._generated_files or not self._namespace:
|
160
164
|
return None
|
161
165
|
|
162
166
|
expected_prefix = generate_workflow_deployment_prefix(deployment_name, release_tag)
|
163
167
|
|
164
|
-
|
165
|
-
|
166
|
-
|
168
|
+
try:
|
169
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
170
|
+
|
171
|
+
WorkflowClass = BaseWorkflow.load_from_module(f"{self.namespace}.{expected_prefix}")
|
172
|
+
WorkflowClass.is_dynamic = True
|
173
|
+
workflow_instance = WorkflowClass(context=WorkflowContext.create_from(self), parent_state=state)
|
174
|
+
return workflow_instance
|
175
|
+
except Exception:
|
176
|
+
pass
|
167
177
|
|
168
178
|
try:
|
179
|
+
major_version = __version__.split(".")[0]
|
180
|
+
version_range = f">={major_version}.0.0,<={__version__}"
|
181
|
+
|
182
|
+
response = self.vellum_client.workflows.pull(
|
183
|
+
deployment_name,
|
184
|
+
release_tag=release_tag,
|
185
|
+
version=version_range,
|
186
|
+
request_options={"additional_headers": {"X-Vellum-Always-Success": "true"}},
|
187
|
+
)
|
188
|
+
|
189
|
+
if isinstance(response, dict) and response.get("success") is False:
|
190
|
+
return None
|
191
|
+
|
192
|
+
zip_bytes = b"".join(response)
|
193
|
+
pulled_files = extract_zip_files(zip_bytes)
|
194
|
+
|
195
|
+
for file_name, content in pulled_files.items():
|
196
|
+
prefixed_file_name = f"{expected_prefix}/{file_name}"
|
197
|
+
self._generated_files[prefixed_file_name] = content
|
198
|
+
|
169
199
|
from vellum.workflows.workflows.base import BaseWorkflow
|
170
200
|
|
171
201
|
WorkflowClass = BaseWorkflow.load_from_module(f"{self.namespace}.{expected_prefix}")
|
202
|
+
WorkflowClass.is_dynamic = True
|
172
203
|
workflow_instance = WorkflowClass(context=WorkflowContext.create_from(self), parent_state=state)
|
173
204
|
return workflow_instance
|
205
|
+
|
174
206
|
except Exception:
|
175
|
-
|
207
|
+
pass
|
208
|
+
|
209
|
+
return None
|
176
210
|
|
177
211
|
@classmethod
|
178
212
|
def create_from(cls, context):
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import io
|
2
|
+
import zipfile
|
3
|
+
|
4
|
+
|
5
|
+
def zip_file_map(file_map: dict[str, str]) -> bytes:
|
6
|
+
"""
|
7
|
+
Create a zip file from a dictionary of file names to content.
|
8
|
+
|
9
|
+
Args:
|
10
|
+
file_map: Dictionary mapping file names to their content
|
11
|
+
|
12
|
+
Returns:
|
13
|
+
Bytes representing the zip file
|
14
|
+
"""
|
15
|
+
zip_buffer = io.BytesIO()
|
16
|
+
|
17
|
+
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
18
|
+
for filename, content in file_map.items():
|
19
|
+
zip_file.writestr(filename, content)
|
20
|
+
|
21
|
+
zip_bytes = zip_buffer.getvalue()
|
22
|
+
zip_buffer.close()
|
23
|
+
|
24
|
+
return zip_bytes
|
25
|
+
|
26
|
+
|
27
|
+
def extract_zip_files(zip_bytes: bytes) -> dict[str, str]:
|
28
|
+
"""
|
29
|
+
Extract files from a zip archive.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
zip_bytes: Bytes representing the zip file
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Dictionary mapping file names to their content
|
36
|
+
"""
|
37
|
+
zip_buffer = io.BytesIO(zip_bytes)
|
38
|
+
extracted_files = {}
|
39
|
+
|
40
|
+
with zipfile.ZipFile(zip_buffer) as zip_file:
|
41
|
+
for file_name in zip_file.namelist():
|
42
|
+
with zip_file.open(file_name) as source:
|
43
|
+
content = source.read().decode("utf-8")
|
44
|
+
extracted_files[file_name] = content
|
45
|
+
|
46
|
+
return extracted_files
|
@@ -251,6 +251,9 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
251
251
|
for emitter in self.emitters:
|
252
252
|
emitter.register_context(self._context)
|
253
253
|
|
254
|
+
for resolver in self.resolvers:
|
255
|
+
resolver.register_workflow_instance(self)
|
256
|
+
|
254
257
|
self.validate()
|
255
258
|
|
256
259
|
@property
|
@@ -361,6 +364,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
361
364
|
state: Optional[StateType] = None,
|
362
365
|
entrypoint_nodes: Optional[RunFromNodeArg] = None,
|
363
366
|
external_inputs: Optional[ExternalInputsArg] = None,
|
367
|
+
previous_execution_id: Optional[Union[str, UUID]] = None,
|
364
368
|
cancel_signal: Optional[ThreadingEvent] = None,
|
365
369
|
node_output_mocks: Optional[MockNodeExecutionArg] = None,
|
366
370
|
max_concurrency: Optional[int] = None,
|
@@ -386,6 +390,9 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
386
390
|
external_inputs: Optional[ExternalInputsArg] = None
|
387
391
|
External inputs to pass to the Workflow. Useful for providing human-in-the-loop behavior to the Workflow.
|
388
392
|
|
393
|
+
previous_execution_id: Optional[Union[str, UUID]] = None
|
394
|
+
The execution ID of the previous execution to resume from.
|
395
|
+
|
389
396
|
cancel_signal: Optional[ThreadingEvent] = None
|
390
397
|
A threading event that can be used to cancel the Workflow Execution.
|
391
398
|
|
@@ -405,6 +412,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
405
412
|
state=state,
|
406
413
|
entrypoint_nodes=entrypoint_nodes,
|
407
414
|
external_inputs=external_inputs,
|
415
|
+
previous_execution_id=previous_execution_id,
|
408
416
|
cancel_signal=cancel_signal,
|
409
417
|
node_output_mocks=node_output_mocks,
|
410
418
|
max_concurrency=max_concurrency,
|
@@ -473,6 +481,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
473
481
|
state: Optional[StateType] = None,
|
474
482
|
entrypoint_nodes: Optional[RunFromNodeArg] = None,
|
475
483
|
external_inputs: Optional[ExternalInputsArg] = None,
|
484
|
+
previous_execution_id: Optional[Union[str, UUID]] = None,
|
476
485
|
cancel_signal: Optional[ThreadingEvent] = None,
|
477
486
|
node_output_mocks: Optional[MockNodeExecutionArg] = None,
|
478
487
|
max_concurrency: Optional[int] = None,
|
@@ -499,6 +508,9 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
499
508
|
external_inputs: Optional[ExternalInputsArg] = None
|
500
509
|
External inputs to pass to the Workflow. Useful for providing human-in-the-loop behavior to the Workflow.
|
501
510
|
|
511
|
+
previous_execution_id: Optional[Union[str, UUID]] = None
|
512
|
+
The execution ID of the previous execution to resume from.
|
513
|
+
|
502
514
|
cancel_signal: Optional[ThreadingEvent] = None
|
503
515
|
A threading event that can be used to cancel the Workflow Execution.
|
504
516
|
|
@@ -519,6 +531,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
519
531
|
state=state,
|
520
532
|
entrypoint_nodes=entrypoint_nodes,
|
521
533
|
external_inputs=external_inputs,
|
534
|
+
previous_execution_id=previous_execution_id,
|
522
535
|
cancel_signal=cancel_signal,
|
523
536
|
node_output_mocks=node_output_mocks,
|
524
537
|
max_concurrency=max_concurrency,
|