vellum-ai 0.14.33__py3-none-any.whl → 0.14.34__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/client/core/client_wrapper.py +1 -1
- vellum/client/types/workflow_deployment_release.py +6 -0
- vellum/client/types/workflow_deployment_release_workflow_version.py +10 -1
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -3
- vellum/workflows/nodes/core/map_node/node.py +1 -3
- vellum/workflows/nodes/core/retry_node/node.py +1 -3
- vellum/workflows/nodes/core/try_node/node.py +1 -3
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +4 -1
- vellum/workflows/state/context.py +4 -0
- {vellum_ai-0.14.33.dist-info → vellum_ai-0.14.34.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.33.dist-info → vellum_ai-0.14.34.dist-info}/RECORD +17 -17
- vellum_cli/pull.py +45 -1
- vellum_cli/tests/test_pull.py +323 -2
- vellum_ee/workflows/tests/test_server.py +356 -2
- {vellum_ai-0.14.33.dist-info → vellum_ai-0.14.34.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.33.dist-info → vellum_ai-0.14.34.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.33.dist-info → vellum_ai-0.14.34.dist-info}/entry_points.txt +0 -0
@@ -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.34",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
2
2
|
|
3
|
+
from __future__ import annotations
|
3
4
|
from ..core.pydantic_utilities import UniversalBaseModel
|
5
|
+
from .array_vellum_value import ArrayVellumValue
|
4
6
|
import datetime as dt
|
5
7
|
from .release_environment import ReleaseEnvironment
|
6
8
|
import typing
|
@@ -10,6 +12,7 @@ from .release_release_tag import ReleaseReleaseTag
|
|
10
12
|
from .slim_release_review import SlimReleaseReview
|
11
13
|
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
12
14
|
import pydantic
|
15
|
+
from ..core.pydantic_utilities import update_forward_refs
|
13
16
|
|
14
17
|
|
15
18
|
class WorkflowDeploymentRelease(UniversalBaseModel):
|
@@ -30,3 +33,6 @@ class WorkflowDeploymentRelease(UniversalBaseModel):
|
|
30
33
|
frozen = True
|
31
34
|
smart_union = True
|
32
35
|
extra = pydantic.Extra.allow
|
36
|
+
|
37
|
+
|
38
|
+
update_forward_refs(ArrayVellumValue, WorkflowDeploymentRelease=WorkflowDeploymentRelease)
|
@@ -1,13 +1,19 @@
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
2
2
|
|
3
|
+
from __future__ import annotations
|
3
4
|
from ..core.pydantic_utilities import UniversalBaseModel
|
4
|
-
from
|
5
|
+
from .array_vellum_value import ArrayVellumValue
|
5
6
|
import typing
|
7
|
+
from .vellum_variable import VellumVariable
|
8
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
6
9
|
import pydantic
|
10
|
+
from ..core.pydantic_utilities import update_forward_refs
|
7
11
|
|
8
12
|
|
9
13
|
class WorkflowDeploymentReleaseWorkflowVersion(UniversalBaseModel):
|
10
14
|
id: str
|
15
|
+
input_variables: typing.List[VellumVariable]
|
16
|
+
output_variables: typing.List[VellumVariable]
|
11
17
|
|
12
18
|
if IS_PYDANTIC_V2:
|
13
19
|
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
@@ -17,3 +23,6 @@ class WorkflowDeploymentReleaseWorkflowVersion(UniversalBaseModel):
|
|
17
23
|
frozen = True
|
18
24
|
smart_union = True
|
19
25
|
extra = pydantic.Extra.allow
|
26
|
+
|
27
|
+
|
28
|
+
update_forward_refs(ArrayVellumValue, WorkflowDeploymentReleaseWorkflowVersion=WorkflowDeploymentReleaseWorkflowVersion)
|
@@ -75,9 +75,7 @@ class InlineSubworkflowNode(
|
|
75
75
|
with execution_context(parent_context=get_parent_context()):
|
76
76
|
subworkflow = self.subworkflow(
|
77
77
|
parent_state=self.state,
|
78
|
-
context=WorkflowContext(
|
79
|
-
vellum_client=self._context.vellum_client, generated_files=self._context.generated_files
|
80
|
-
),
|
78
|
+
context=WorkflowContext.create_from(self._context),
|
81
79
|
)
|
82
80
|
subworkflow_stream = subworkflow.stream(
|
83
81
|
inputs=self._compile_subworkflow_inputs(),
|
@@ -171,9 +171,7 @@ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType])
|
|
171
171
|
self._run_subworkflow(item=item, index=index)
|
172
172
|
|
173
173
|
def _run_subworkflow(self, *, item: MapNodeItemType, index: int) -> None:
|
174
|
-
context = WorkflowContext(
|
175
|
-
vellum_client=self._context.vellum_client, generated_files=self._context.generated_files
|
176
|
-
)
|
174
|
+
context = WorkflowContext.create_from(self._context)
|
177
175
|
subworkflow = self.subworkflow(
|
178
176
|
parent_state=self.state,
|
179
177
|
context=context,
|
@@ -45,9 +45,7 @@ class RetryNode(BaseAdornmentNode[StateType], Generic[StateType]):
|
|
45
45
|
with execution_context(parent_context=parent_context):
|
46
46
|
subworkflow = self.subworkflow(
|
47
47
|
parent_state=self.state,
|
48
|
-
context=WorkflowContext(
|
49
|
-
vellum_client=self._context.vellum_client, generated_files=self._context.generated_files
|
50
|
-
),
|
48
|
+
context=WorkflowContext.create_from(self._context),
|
51
49
|
)
|
52
50
|
subworkflow_stream = subworkflow.stream(
|
53
51
|
inputs=self.SubworkflowInputs(attempt_number=attempt_number),
|
@@ -32,9 +32,7 @@ class TryNode(BaseAdornmentNode[StateType], Generic[StateType]):
|
|
32
32
|
with execution_context(parent_context=parent_context):
|
33
33
|
subworkflow = self.subworkflow(
|
34
34
|
parent_state=self.state,
|
35
|
-
context=WorkflowContext(
|
36
|
-
vellum_client=self._context.vellum_client, generated_files=self._context.generated_files
|
37
|
-
),
|
35
|
+
context=WorkflowContext.create_from(self._context),
|
38
36
|
)
|
39
37
|
subworkflow_stream = subworkflow.stream(
|
40
38
|
event_filter=all_workflow_event_filter,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
import os
|
3
|
+
import re
|
3
4
|
from typing import Any, List, Union
|
4
5
|
|
5
6
|
from pydantic import BaseModel
|
@@ -653,7 +654,9 @@ def main():
|
|
653
654
|
node.run()
|
654
655
|
|
655
656
|
# AND the error should contain the execution error details
|
656
|
-
assert
|
657
|
+
assert re.match(
|
658
|
+
r"Expected an output of type '(int \| float|float \| int)', but received 'str'", exc_info.value.message
|
659
|
+
)
|
657
660
|
|
658
661
|
|
659
662
|
def test_run_node__chat_history_output_type():
|
@@ -74,3 +74,7 @@ class WorkflowContext:
|
|
74
74
|
|
75
75
|
def _get_all_node_output_mocks(self) -> List[MockNodeExecution]:
|
76
76
|
return [mock for mocks in self._node_output_mocks_map.values() for mock in mocks]
|
77
|
+
|
78
|
+
@classmethod
|
79
|
+
def create_from(cls, context):
|
80
|
+
return cls(vellum_client=context.vellum_client, generated_files=context.generated_files)
|
@@ -7,7 +7,7 @@ vellum_cli/image_push.py,sha256=8DDvRDJEZ-FukUCqGW1827bg1ybF4xBbx9WyqWYQE-g,6816
|
|
7
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
|
-
vellum_cli/pull.py,sha256=
|
10
|
+
vellum_cli/pull.py,sha256=YTo5cVCcp7RjS9lHednOuud4rW4bH7jqV3GMdbic_Uk,12002
|
11
11
|
vellum_cli/push.py,sha256=xjTNbLwOVFNU3kpBrm56Bk5QkSRrJ9z86qceghCzfIA,9655
|
12
12
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
|
@@ -16,7 +16,7 @@ vellum_cli/tests/test_image_push.py,sha256=i3lJuW8nFRwL1M1OF6752IZYvGAFgKmkB2hd_
|
|
16
16
|
vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ,18754
|
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
|
-
vellum_cli/tests/test_pull.py,sha256=
|
19
|
+
vellum_cli/tests/test_pull.py,sha256=fci5idbcAsnvfJcmP8KlULX4w4gkjLtXkn3iV4wsLZA,46472
|
20
20
|
vellum_cli/tests/test_push.py,sha256=zDv_Q1hbXtLwmTJDPRAvwDjbuHC09uNRYOy4FQujUow,23476
|
21
21
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -121,14 +121,14 @@ vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zi
|
|
121
121
|
vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
|
122
122
|
vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
|
123
123
|
vellum_ee/workflows/tests/test_display_meta.py,sha256=C25dErwghPNXio49pvSRxyOuc96srH6eYEwTAWdE2zY,2258
|
124
|
-
vellum_ee/workflows/tests/test_server.py,sha256=
|
124
|
+
vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
|
125
125
|
vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
|
126
126
|
vellum/__init__.py,sha256=Ur02Hk_LtwXW45f-LBBtBhRX1xJRSq05ytaQUfgIGmc,40970
|
127
127
|
vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
128
128
|
vellum/client/__init__.py,sha256=ki-TDOmYzC0FePN7swDyE6UpHFQV_4dK7lqy4h-3s1Y,118148
|
129
129
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
130
130
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
131
|
-
vellum/client/core/client_wrapper.py,sha256
|
131
|
+
vellum/client/core/client_wrapper.py,sha256=-wKm98fWVAP4wszQZhj8UDobt3IotWN6IYciYhNcNck,1869
|
132
132
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
133
133
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
134
134
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -698,8 +698,8 @@ vellum/client/types/workflow_deployment_event_executions_response.py,sha256=x7mZ
|
|
698
698
|
vellum/client/types/workflow_deployment_history_item.py,sha256=4WUPzcthBvEZ7iaisKfEg0soUtHjcTEnL_VUVaKpTyw,1420
|
699
699
|
vellum/client/types/workflow_deployment_parent_context.py,sha256=QNyPj2o-jauaC48KrRjCWan1IKIbDgyuxLxURmhXsXM,2347
|
700
700
|
vellum/client/types/workflow_deployment_read.py,sha256=tp1WaojTVE_dz1oiZ97h8ixMbIWDgy2yRu08A7wPMpw,2363
|
701
|
-
vellum/client/types/workflow_deployment_release.py,sha256=
|
702
|
-
vellum/client/types/workflow_deployment_release_workflow_version.py,sha256=
|
701
|
+
vellum/client/types/workflow_deployment_release.py,sha256=UshzYZE5Fv1hAwztaMTD1Fs0JNQqbeNus0IrwXLkKL0,1444
|
702
|
+
vellum/client/types/workflow_deployment_release_workflow_version.py,sha256=NP3FoxLpgMUIK1OAPf_ei58mxE9F7BLAuw7q55CIcT8,989
|
703
703
|
vellum/client/types/workflow_error.py,sha256=EQajkEmLS64T0wYm0goHQl0rT7Lguurk8pLwkhjsgAI,282
|
704
704
|
vellum/client/types/workflow_event_display_context.py,sha256=tnO9lgIJKnLtuS6xum_QilI83LjOmnWCLtnSHLn1oNo,929
|
705
705
|
vellum/client/types/workflow_event_error.py,sha256=HIewu_kh3KNPpWegAQArvAGHCp-cBIXqlUAAc_dBZhc,687
|
@@ -1523,22 +1523,22 @@ vellum/workflows/nodes/core/__init__.py,sha256=5zDMCmyt1v0HTJzlUBwq3U9L825yZGZhT
|
|
1523
1523
|
vellum/workflows/nodes/core/error_node/__init__.py,sha256=g7RRnlHhqu4qByfLjBwCunmgGA8dI5gNsjS3h6TwlSI,60
|
1524
1524
|
vellum/workflows/nodes/core/error_node/node.py,sha256=MFHU5vITYSK-L9CuMZ49In2ZeNLWnhZD0f8r5dWvb5Y,1270
|
1525
1525
|
vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py,sha256=nKNEH1QTl-1PcvmYoqSWEl0-t6gAur8GLTXHzklRQfM,84
|
1526
|
-
vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=
|
1526
|
+
vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=rgcjc3gaCEX9uSfkLSErHjSnNQEeqREVZk-7TEr9hUo,6595
|
1527
1527
|
vellum/workflows/nodes/core/inline_subworkflow_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1528
1528
|
vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py,sha256=kUqwcRMMxjTHALbwGUXCJT_aJBrHS1bkg49oL8R0JO8,4337
|
1529
1529
|
vellum/workflows/nodes/core/map_node/__init__.py,sha256=MXpZYmGfhsMJHqqlpd64WiJRtbAtAMQz-_3fCU_cLV0,56
|
1530
|
-
vellum/workflows/nodes/core/map_node/node.py,sha256=
|
1530
|
+
vellum/workflows/nodes/core/map_node/node.py,sha256=RER4mLOXSe1BSpjEYCoJq83ZeydQ5Q5uKCZAsqhlOW8,9227
|
1531
1531
|
vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1532
1532
|
vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=uMR0AyIFn539LqTKHdwuBswnx1i-PHyqPpgtYrnmYMY,3496
|
1533
1533
|
vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
|
1534
|
-
vellum/workflows/nodes/core/retry_node/node.py,sha256=
|
1534
|
+
vellum/workflows/nodes/core/retry_node/node.py,sha256=at7RjwUmlBeUv-tHvqeOhCAxkyuSw47ySmIQKC0fJf8,5245
|
1535
1535
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1536
1536
|
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
|
1537
1537
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1538
1538
|
vellum/workflows/nodes/core/templating_node/node.py,sha256=iqBmr2i-f-BqhisNQJiDfewjol0ur7-XpupLStyMJsg,3731
|
1539
1539
|
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nXkgGDBg4wP36NwykdMEVWwx_xjv8oGT2rYkwuCB_VU,10075
|
1540
1540
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1541
|
-
vellum/workflows/nodes/core/try_node/node.py,sha256
|
1541
|
+
vellum/workflows/nodes/core/try_node/node.py,sha256=2r2I70j30IyZPvn3Q3zP2VgLbx3WQ1DdOr8NUlXjcq0,4429
|
1542
1542
|
vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1543
1543
|
vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=h6eUc3SggvhzBWlOD0PrPUlkoCSQHwjqYn81VkxSIxU,4948
|
1544
1544
|
vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
|
@@ -1567,7 +1567,7 @@ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=Ko_Dy17Ajf
|
|
1567
1567
|
vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1568
1568
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1569
1569
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
|
1570
|
-
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=
|
1570
|
+
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=AzgTK2YSvVj7zr6gWZfz0-YGf1cVQ9DiSx9fe5BR4uE,24690
|
1571
1571
|
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=G-sc7yOL5g6rLk99X8HAbXNcLxRaqpju9IXq1iUwnQI,4470
|
1572
1572
|
vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
|
1573
1573
|
vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
|
@@ -1638,7 +1638,7 @@ vellum/workflows/runner/runner.py,sha256=ww4fjZJBENkB5HJxdj92kTz7k_EyifCeAreupy5
|
|
1638
1638
|
vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
|
1639
1639
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1640
1640
|
vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
|
1641
|
-
vellum/workflows/state/context.py,sha256=
|
1641
|
+
vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
|
1642
1642
|
vellum/workflows/state/encoder.py,sha256=TnOQojc5lTQ83g9QbpA4UCqShJvutmTMxbpKt-9gNe4,1911
|
1643
1643
|
vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
|
1644
1644
|
vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1670,8 +1670,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1670
1670
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1671
1671
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=tCxrV3QBHL8wfdEO3bvKteDdw32xBlUl1_WxkAwaONw,8344
|
1672
1672
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1673
|
-
vellum_ai-0.14.
|
1674
|
-
vellum_ai-0.14.
|
1675
|
-
vellum_ai-0.14.
|
1676
|
-
vellum_ai-0.14.
|
1677
|
-
vellum_ai-0.14.
|
1673
|
+
vellum_ai-0.14.34.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1674
|
+
vellum_ai-0.14.34.dist-info/METADATA,sha256=1DTJPYOhbXBtorWcrH9e992hHsfsTsvKtleo96NqcaY,5484
|
1675
|
+
vellum_ai-0.14.34.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1676
|
+
vellum_ai-0.14.34.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1677
|
+
vellum_ai-0.14.34.dist-info/RECORD,,
|
vellum_cli/pull.py
CHANGED
@@ -2,6 +2,7 @@ import io
|
|
2
2
|
import json
|
3
3
|
import os
|
4
4
|
from pathlib import Path
|
5
|
+
from uuid import UUID
|
5
6
|
import zipfile
|
6
7
|
from typing import Optional
|
7
8
|
|
@@ -10,8 +11,9 @@ from pydash import snake_case
|
|
10
11
|
|
11
12
|
from vellum.client.core.api_error import ApiError
|
12
13
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
14
|
+
from vellum.utils.uuid import is_valid_uuid
|
13
15
|
from vellum.workflows.vellum_client import create_vellum_client
|
14
|
-
from vellum_cli.config import VellumCliConfig, WorkflowConfig, load_vellum_cli_config
|
16
|
+
from vellum_cli.config import VellumCliConfig, WorkflowConfig, WorkflowDeploymentConfig, load_vellum_cli_config
|
15
17
|
from vellum_cli.logger import load_cli_logger
|
16
18
|
|
17
19
|
ERROR_LOG_FILE_NAME = "error.log"
|
@@ -31,6 +33,7 @@ class RunnerConfig(UniversalBaseModel):
|
|
31
33
|
class PullContentsMetadata(UniversalBaseModel):
|
32
34
|
label: Optional[str] = None
|
33
35
|
runner_config: Optional[RunnerConfig] = None
|
36
|
+
deployment_id: Optional[UUID] = None
|
34
37
|
deployment_name: Optional[str] = None
|
35
38
|
|
36
39
|
|
@@ -79,6 +82,29 @@ def _resolve_workflow_config(
|
|
79
82
|
pk=workflow_config.workflow_sandbox_id,
|
80
83
|
)
|
81
84
|
elif workflow_deployment:
|
85
|
+
if is_valid_uuid(workflow_deployment):
|
86
|
+
# name may also be a valid UUID
|
87
|
+
workflow_config = next(
|
88
|
+
(
|
89
|
+
w
|
90
|
+
for w in config.workflows
|
91
|
+
if w.deployments
|
92
|
+
and (
|
93
|
+
str(w.deployments[0].id) == workflow_deployment or w.deployments[0].name == workflow_deployment
|
94
|
+
)
|
95
|
+
),
|
96
|
+
None,
|
97
|
+
)
|
98
|
+
else:
|
99
|
+
workflow_config = next(
|
100
|
+
(w for w in config.workflows if w.deployments and w.deployments[0].name == workflow_deployment), None
|
101
|
+
)
|
102
|
+
if workflow_config:
|
103
|
+
return WorkflowConfigResolutionResult(
|
104
|
+
workflow_config=workflow_config,
|
105
|
+
pk=workflow_deployment,
|
106
|
+
)
|
107
|
+
|
82
108
|
workflow_config = WorkflowConfig(
|
83
109
|
module="",
|
84
110
|
)
|
@@ -177,6 +203,24 @@ def pull_command(
|
|
177
203
|
if not workflow_config.module and pull_contents_metadata.label:
|
178
204
|
workflow_config.module = snake_case(pull_contents_metadata.label)
|
179
205
|
|
206
|
+
# Save or update the deployment info when pulling with --workflow-deployment
|
207
|
+
if workflow_deployment:
|
208
|
+
workflow_deployment_id = pull_contents_metadata.deployment_id
|
209
|
+
existing_deployment = next(
|
210
|
+
(d for d in workflow_config.deployments if d.id == workflow_deployment_id), None
|
211
|
+
)
|
212
|
+
|
213
|
+
if existing_deployment:
|
214
|
+
if pull_contents_metadata.label:
|
215
|
+
existing_deployment.label = pull_contents_metadata.label
|
216
|
+
else:
|
217
|
+
deployment_config = WorkflowDeploymentConfig(
|
218
|
+
id=workflow_deployment_id,
|
219
|
+
label=pull_contents_metadata.label,
|
220
|
+
name=pull_contents_metadata.deployment_name,
|
221
|
+
)
|
222
|
+
workflow_config.deployments.append(deployment_config)
|
223
|
+
|
180
224
|
if not workflow_config.module:
|
181
225
|
raise ValueError(f"Failed to resolve a module name for Workflow {pk}")
|
182
226
|
|
vellum_cli/tests/test_pull.py
CHANGED
@@ -337,6 +337,7 @@ def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_mod
|
|
337
337
|
def test_pull__workflow_deployment_with_no_config(vellum_client):
|
338
338
|
# GIVEN a workflow deployment
|
339
339
|
workflow_deployment = "my-deployment"
|
340
|
+
deployment_id = str(uuid4())
|
340
341
|
|
341
342
|
# AND the workflow pull API call returns a zip file
|
342
343
|
vellum_client.workflows.pull.return_value = iter(
|
@@ -344,7 +345,9 @@ def test_pull__workflow_deployment_with_no_config(vellum_client):
|
|
344
345
|
_zip_file_map(
|
345
346
|
{
|
346
347
|
"workflow.py": "print('hello')",
|
347
|
-
"metadata.json": json.dumps(
|
348
|
+
"metadata.json": json.dumps(
|
349
|
+
{"deployment_id": deployment_id, "deployment_name": workflow_deployment, "label": "Some Label"}
|
350
|
+
),
|
348
351
|
}
|
349
352
|
)
|
350
353
|
]
|
@@ -382,7 +385,15 @@ def test_pull__workflow_deployment_with_no_config(vellum_client):
|
|
382
385
|
"module": "my_deployment",
|
383
386
|
"workflow_sandbox_id": None,
|
384
387
|
"ignore": None,
|
385
|
-
"deployments": [
|
388
|
+
"deployments": [
|
389
|
+
{
|
390
|
+
"id": deployment_id,
|
391
|
+
"label": "Some Label",
|
392
|
+
"name": "my-deployment",
|
393
|
+
"description": None,
|
394
|
+
"release_tags": None,
|
395
|
+
}
|
396
|
+
],
|
386
397
|
"container_image_tag": None,
|
387
398
|
"container_image_name": None,
|
388
399
|
"workspace": "default",
|
@@ -937,3 +948,313 @@ def test_pull__unauthorized_error_path(vellum_client):
|
|
937
948
|
# THEN the command returns an error
|
938
949
|
assert result.exit_code == 1
|
939
950
|
assert str(result.exception) == "Please make sure your `VELLUM_API_KEY` environment variable is set correctly."
|
951
|
+
|
952
|
+
|
953
|
+
@pytest.mark.parametrize(
|
954
|
+
"workflow_deployment",
|
955
|
+
[
|
956
|
+
"test-workflow-deployment-id",
|
957
|
+
str(uuid4()),
|
958
|
+
],
|
959
|
+
)
|
960
|
+
def test_pull__workflow_deployment_adds_deployment_to_config(vellum_client, workflow_deployment):
|
961
|
+
# GIVEN a workflow deployment ID
|
962
|
+
deployment_id = str(uuid4()) # config will always use the deployment_id return from the API
|
963
|
+
deployment_name = "Test Deployment"
|
964
|
+
deployment_label = "Test Label"
|
965
|
+
|
966
|
+
# AND the workflow pull API call returns a zip file with metadata
|
967
|
+
vellum_client.workflows.pull.return_value = iter(
|
968
|
+
[
|
969
|
+
_zip_file_map(
|
970
|
+
{
|
971
|
+
"workflow.py": "print('hello')",
|
972
|
+
"metadata.json": json.dumps(
|
973
|
+
{
|
974
|
+
"deployment_id": deployment_id,
|
975
|
+
"deployment_name": deployment_name,
|
976
|
+
"label": deployment_label,
|
977
|
+
}
|
978
|
+
),
|
979
|
+
}
|
980
|
+
)
|
981
|
+
]
|
982
|
+
)
|
983
|
+
|
984
|
+
# AND we are currently in a new directory
|
985
|
+
current_dir = os.getcwd()
|
986
|
+
temp_dir = tempfile.mkdtemp()
|
987
|
+
os.chdir(temp_dir)
|
988
|
+
|
989
|
+
# WHEN the user runs the pull command with the workflow deployment
|
990
|
+
runner = CliRunner()
|
991
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-deployment", workflow_deployment])
|
992
|
+
|
993
|
+
# THEN the command returns successfully
|
994
|
+
assert result.exit_code == 0
|
995
|
+
|
996
|
+
# AND the deployment is saved in the config
|
997
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
998
|
+
assert os.path.exists(vellum_lock_json)
|
999
|
+
with open(vellum_lock_json) as f:
|
1000
|
+
lock_data = json.loads(f.read())
|
1001
|
+
assert len(lock_data["workflows"]) == 1
|
1002
|
+
assert len(lock_data["workflows"][0]["deployments"]) == 1
|
1003
|
+
deployment = lock_data["workflows"][0]["deployments"][0]
|
1004
|
+
assert str(deployment["id"]) == deployment_id
|
1005
|
+
assert deployment["name"] == deployment_name
|
1006
|
+
assert deployment["label"] == deployment_label
|
1007
|
+
|
1008
|
+
os.chdir(current_dir)
|
1009
|
+
|
1010
|
+
|
1011
|
+
def test_pull__workflow_deployment_name_is_uuid(vellum_client):
|
1012
|
+
# GIVEN a workflow deployment name that is a valid UUID
|
1013
|
+
deployment_id = str(uuid4())
|
1014
|
+
deployment_name = str(uuid4())
|
1015
|
+
|
1016
|
+
# AND an existing configuration with this deployment
|
1017
|
+
current_dir = os.getcwd()
|
1018
|
+
temp_dir = tempfile.mkdtemp()
|
1019
|
+
os.chdir(temp_dir)
|
1020
|
+
|
1021
|
+
# Create initial config with a deployment
|
1022
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
1023
|
+
with open(vellum_lock_json, "w") as f:
|
1024
|
+
json.dump(
|
1025
|
+
{
|
1026
|
+
"version": "1.0",
|
1027
|
+
"workflows": [
|
1028
|
+
{
|
1029
|
+
"module": "test_workflow",
|
1030
|
+
"workflow_sandbox_id": None,
|
1031
|
+
"ignore": None,
|
1032
|
+
"deployments": [
|
1033
|
+
{
|
1034
|
+
"id": deployment_id,
|
1035
|
+
"label": "Test Label",
|
1036
|
+
"name": deployment_name,
|
1037
|
+
"description": None,
|
1038
|
+
"release_tags": None,
|
1039
|
+
}
|
1040
|
+
],
|
1041
|
+
"container_image_name": None,
|
1042
|
+
"container_image_tag": None,
|
1043
|
+
"workspace": "default",
|
1044
|
+
"target_directory": None,
|
1045
|
+
}
|
1046
|
+
],
|
1047
|
+
"workspaces": [],
|
1048
|
+
},
|
1049
|
+
f,
|
1050
|
+
)
|
1051
|
+
|
1052
|
+
# AND the workflow pull API call returns a zip file with updated metadata
|
1053
|
+
updated_label = "Updated Label"
|
1054
|
+
vellum_client.workflows.pull.return_value = iter(
|
1055
|
+
[
|
1056
|
+
_zip_file_map(
|
1057
|
+
{
|
1058
|
+
"workflow.py": "print('hello')",
|
1059
|
+
"metadata.json": json.dumps(
|
1060
|
+
{
|
1061
|
+
"deployment_id": deployment_id,
|
1062
|
+
"deployment_name": deployment_name,
|
1063
|
+
"label": updated_label,
|
1064
|
+
}
|
1065
|
+
),
|
1066
|
+
}
|
1067
|
+
)
|
1068
|
+
]
|
1069
|
+
)
|
1070
|
+
|
1071
|
+
# WHEN the user runs the pull command with the workflow deployment
|
1072
|
+
runner = CliRunner()
|
1073
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-deployment", deployment_name])
|
1074
|
+
|
1075
|
+
# THEN the command returns successfully
|
1076
|
+
assert result.exit_code == 0
|
1077
|
+
|
1078
|
+
# AND the deployment info is updated in the config
|
1079
|
+
with open(vellum_lock_json) as f:
|
1080
|
+
lock_data = json.loads(f.read())
|
1081
|
+
assert len(lock_data["workflows"]) == 1
|
1082
|
+
assert len(lock_data["workflows"][0]["deployments"]) == 1
|
1083
|
+
deployment = lock_data["workflows"][0]["deployments"][0]
|
1084
|
+
assert str(deployment["id"]) == deployment_id
|
1085
|
+
assert deployment["name"] == deployment_name
|
1086
|
+
assert deployment["label"] == updated_label
|
1087
|
+
|
1088
|
+
os.chdir(current_dir)
|
1089
|
+
|
1090
|
+
|
1091
|
+
@pytest.mark.parametrize("get_identifier", [(lambda d: d), (lambda d: "Test Name")])
|
1092
|
+
def test_pull__workflow_deployment_updates_existing_deployment(vellum_client, get_identifier):
|
1093
|
+
"""
|
1094
|
+
This test is to ensure that the deployment info is updated in the config
|
1095
|
+
when the user runs the pull command with the workflow deployment
|
1096
|
+
|
1097
|
+
get_identifier is a function that returns the identifier to use for the deployment
|
1098
|
+
it can be the deployment_id or the deployment_name
|
1099
|
+
"""
|
1100
|
+
# GIVEN a workflow deployment id and name
|
1101
|
+
deployment_id = str(uuid4())
|
1102
|
+
deployment_name = "Test Name"
|
1103
|
+
|
1104
|
+
# AND an existing configuration with this deployment
|
1105
|
+
current_dir = os.getcwd()
|
1106
|
+
temp_dir = tempfile.mkdtemp()
|
1107
|
+
os.chdir(temp_dir)
|
1108
|
+
|
1109
|
+
# Create initial config with a deployment
|
1110
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
1111
|
+
with open(vellum_lock_json, "w") as f:
|
1112
|
+
json.dump(
|
1113
|
+
{
|
1114
|
+
"version": "1.0",
|
1115
|
+
"workflows": [
|
1116
|
+
{
|
1117
|
+
"module": "test_workflow",
|
1118
|
+
"workflow_sandbox_id": None,
|
1119
|
+
"ignore": None,
|
1120
|
+
"deployments": [
|
1121
|
+
{
|
1122
|
+
"id": deployment_id,
|
1123
|
+
"label": "Test Label",
|
1124
|
+
"name": deployment_name,
|
1125
|
+
"description": None,
|
1126
|
+
"release_tags": None,
|
1127
|
+
}
|
1128
|
+
],
|
1129
|
+
"container_image_name": None,
|
1130
|
+
"container_image_tag": None,
|
1131
|
+
"workspace": "default",
|
1132
|
+
"target_directory": None,
|
1133
|
+
}
|
1134
|
+
],
|
1135
|
+
"workspaces": [],
|
1136
|
+
},
|
1137
|
+
f,
|
1138
|
+
)
|
1139
|
+
|
1140
|
+
# AND the workflow pull API call returns a zip file with updated metadata
|
1141
|
+
updated_label = "Updated Label"
|
1142
|
+
vellum_client.workflows.pull.return_value = iter(
|
1143
|
+
[
|
1144
|
+
_zip_file_map(
|
1145
|
+
{
|
1146
|
+
"workflow.py": "print('hello')",
|
1147
|
+
"metadata.json": json.dumps(
|
1148
|
+
{
|
1149
|
+
"deployment_id": deployment_id,
|
1150
|
+
"deployment_name": deployment_name,
|
1151
|
+
"label": updated_label,
|
1152
|
+
}
|
1153
|
+
),
|
1154
|
+
}
|
1155
|
+
)
|
1156
|
+
]
|
1157
|
+
)
|
1158
|
+
|
1159
|
+
# WHEN the user runs the pull command with the workflow deployment
|
1160
|
+
runner = CliRunner()
|
1161
|
+
identifier_to_use = get_identifier(deployment_id)
|
1162
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-deployment", identifier_to_use])
|
1163
|
+
|
1164
|
+
# THEN the command returns successfully
|
1165
|
+
assert result.exit_code == 0
|
1166
|
+
|
1167
|
+
# AND the deployment info is updated in the config
|
1168
|
+
with open(vellum_lock_json) as f:
|
1169
|
+
lock_data = json.loads(f.read())
|
1170
|
+
assert len(lock_data["workflows"]) == 1
|
1171
|
+
assert len(lock_data["workflows"][0]["deployments"]) == 1
|
1172
|
+
deployment = lock_data["workflows"][0]["deployments"][0]
|
1173
|
+
assert str(deployment["id"]) == deployment_id
|
1174
|
+
assert deployment["name"] == deployment_name
|
1175
|
+
assert deployment["label"] == updated_label
|
1176
|
+
|
1177
|
+
os.chdir(current_dir)
|
1178
|
+
|
1179
|
+
|
1180
|
+
def test_pull__workflow_deployment_with_name_and_id(vellum_client):
|
1181
|
+
"""
|
1182
|
+
This test is to ensure that pulling with id and name will not add a new deployment to the config
|
1183
|
+
"""
|
1184
|
+
# GIVEN a workflow deployment ID
|
1185
|
+
deployment_id = str(uuid4()) # config will always use the deployment_id return from the API
|
1186
|
+
deployment_name = "Test Deployment"
|
1187
|
+
deployment_label = "Test Label"
|
1188
|
+
|
1189
|
+
# AND the workflow pull API call returns a zip file with metadata
|
1190
|
+
vellum_client.workflows.pull.return_value = iter(
|
1191
|
+
[
|
1192
|
+
_zip_file_map(
|
1193
|
+
{
|
1194
|
+
"workflow.py": "print('hello')",
|
1195
|
+
"metadata.json": json.dumps(
|
1196
|
+
{
|
1197
|
+
"deployment_id": deployment_id,
|
1198
|
+
"deployment_name": deployment_name,
|
1199
|
+
"label": deployment_label,
|
1200
|
+
}
|
1201
|
+
),
|
1202
|
+
}
|
1203
|
+
)
|
1204
|
+
]
|
1205
|
+
)
|
1206
|
+
|
1207
|
+
# AND we are currently in a new directory
|
1208
|
+
current_dir = os.getcwd()
|
1209
|
+
temp_dir = tempfile.mkdtemp()
|
1210
|
+
os.chdir(temp_dir)
|
1211
|
+
|
1212
|
+
# WHEN the user runs the pull command with the workflow deployment
|
1213
|
+
runner = CliRunner()
|
1214
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-deployment", deployment_id])
|
1215
|
+
|
1216
|
+
# THEN the command returns successfully
|
1217
|
+
assert result.exit_code == 0
|
1218
|
+
|
1219
|
+
# AND the deployment is saved in the config
|
1220
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
1221
|
+
assert os.path.exists(vellum_lock_json)
|
1222
|
+
with open(vellum_lock_json) as f:
|
1223
|
+
lock_data = json.loads(f.read())
|
1224
|
+
assert len(lock_data["workflows"]) == 1
|
1225
|
+
assert len(lock_data["workflows"][0]["deployments"]) == 1
|
1226
|
+
deployment = lock_data["workflows"][0]["deployments"][0]
|
1227
|
+
assert str(deployment["id"]) == deployment_id
|
1228
|
+
assert deployment["name"] == deployment_name
|
1229
|
+
assert deployment["label"] == deployment_label
|
1230
|
+
|
1231
|
+
os.chdir(current_dir)
|
1232
|
+
|
1233
|
+
# AND pull with name will not add a new deployment to the config
|
1234
|
+
vellum_client.workflows.pull.return_value = iter(
|
1235
|
+
[
|
1236
|
+
_zip_file_map(
|
1237
|
+
{
|
1238
|
+
"workflow.py": "print('hello')",
|
1239
|
+
"metadata.json": json.dumps(
|
1240
|
+
{
|
1241
|
+
"deployment_id": deployment_id,
|
1242
|
+
"deployment_name": deployment_name,
|
1243
|
+
"label": deployment_label,
|
1244
|
+
}
|
1245
|
+
),
|
1246
|
+
}
|
1247
|
+
)
|
1248
|
+
]
|
1249
|
+
)
|
1250
|
+
|
1251
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-deployment", deployment_name])
|
1252
|
+
assert result.exit_code == 0
|
1253
|
+
with open(vellum_lock_json) as f:
|
1254
|
+
lock_data = json.loads(f.read())
|
1255
|
+
assert len(lock_data["workflows"][0]["deployments"]) == 1
|
1256
|
+
assert lock_data["workflows"][0]["deployments"][0]["id"] == deployment_id
|
1257
|
+
assert lock_data["workflows"][0]["deployments"][0]["name"] == deployment_name
|
1258
|
+
assert lock_data["workflows"][0]["deployments"][0]["label"] == deployment_label
|
1259
|
+
|
1260
|
+
os.chdir(current_dir)
|
@@ -12,8 +12,7 @@ from vellum_ee.workflows.server.virtual_file_loader import VirtualFileFinder
|
|
12
12
|
|
13
13
|
|
14
14
|
def test_load_workflow_event_display_context():
|
15
|
-
|
16
|
-
from vellum_ee.workflows.display.types import WorkflowEventDisplayContext
|
15
|
+
from vellum.workflows.events.workflow import WorkflowEventDisplayContext
|
17
16
|
|
18
17
|
# We are actually just ensuring there are no circular dependencies when
|
19
18
|
# our Workflow Server imports this class.
|
@@ -140,3 +139,358 @@ class CodeExecutionNode(BaseCodeExecutionNode[BaseState, int]):
|
|
140
139
|
|
141
140
|
# AND we get the code execution result
|
142
141
|
assert event.body.outputs == {"final_output": 5.0}
|
142
|
+
|
143
|
+
|
144
|
+
def test_load_from_module__simple_code_execution_node_with_try(
|
145
|
+
vellum_client,
|
146
|
+
):
|
147
|
+
# GIVEN a simple Python script
|
148
|
+
py_code = """def main() -> int:
|
149
|
+
print("Hello")
|
150
|
+
return 1
|
151
|
+
"""
|
152
|
+
|
153
|
+
# AND a workflow module with only a code execution node
|
154
|
+
files = {
|
155
|
+
"__init__.py": "",
|
156
|
+
"workflow.py": """
|
157
|
+
from vellum.workflows import BaseWorkflow
|
158
|
+
from vellum.workflows.state import BaseState
|
159
|
+
|
160
|
+
from .nodes.code_execution_node import CodeExecutionNode
|
161
|
+
|
162
|
+
class Workflow(BaseWorkflow):
|
163
|
+
graph = CodeExecutionNode
|
164
|
+
|
165
|
+
class Outputs(BaseWorkflow.Outputs):
|
166
|
+
final_output = CodeExecutionNode.Outputs.result
|
167
|
+
""",
|
168
|
+
"nodes/__init__.py": """
|
169
|
+
from .code_execution_node import CodeExecutionNode
|
170
|
+
|
171
|
+
__all__ = ["CodeExecutionNode"]
|
172
|
+
""",
|
173
|
+
"nodes/code_execution_node/__init__.py": """
|
174
|
+
from typing import Union
|
175
|
+
|
176
|
+
from vellum.workflows.nodes.core import TryNode
|
177
|
+
from vellum.workflows.nodes.displayable import CodeExecutionNode as BaseCodeExecutionNode
|
178
|
+
from vellum.workflows.state import BaseState
|
179
|
+
|
180
|
+
@TryNode.wrap()
|
181
|
+
class CodeExecutionNode(BaseCodeExecutionNode[BaseState, Union[float, int]]):
|
182
|
+
filepath = "./script.py"
|
183
|
+
code_inputs = {}
|
184
|
+
runtime = "PYTHON_3_11_6"
|
185
|
+
packages = []
|
186
|
+
""",
|
187
|
+
"nodes/code_execution_node/script.py": py_code,
|
188
|
+
}
|
189
|
+
|
190
|
+
namespace = str(uuid4())
|
191
|
+
|
192
|
+
# AND the virtual file loader is registered
|
193
|
+
finder = VirtualFileFinder(files, namespace)
|
194
|
+
sys.meta_path.append(finder)
|
195
|
+
|
196
|
+
# AND we know what the Code Execution Node will respond with
|
197
|
+
mock_code_execution = CodeExecutorResponse(
|
198
|
+
log="hello",
|
199
|
+
output=NumberVellumValue(value=1),
|
200
|
+
)
|
201
|
+
vellum_client.execute_code.return_value = mock_code_execution
|
202
|
+
|
203
|
+
# WHEN the workflow is loaded
|
204
|
+
Workflow = BaseWorkflow.load_from_module(namespace)
|
205
|
+
workflow = Workflow(context=WorkflowContext(generated_files=files))
|
206
|
+
|
207
|
+
# THEN the workflow is successfully initialized
|
208
|
+
assert workflow
|
209
|
+
|
210
|
+
# WHEN we run the workflow
|
211
|
+
event = workflow.run()
|
212
|
+
|
213
|
+
# THEN the execution is fulfilled
|
214
|
+
assert event.name == "workflow.execution.fulfilled"
|
215
|
+
|
216
|
+
# AND we get the code execution result
|
217
|
+
assert event.body.outputs == {"final_output": 1.0}
|
218
|
+
|
219
|
+
|
220
|
+
def test_load_from_module__simple_code_execution_node_with_retry(
|
221
|
+
vellum_client,
|
222
|
+
):
|
223
|
+
# GIVEN a simple Python script
|
224
|
+
py_code = """def main() -> int:
|
225
|
+
print("Hello")
|
226
|
+
return 1
|
227
|
+
"""
|
228
|
+
|
229
|
+
# AND a workflow module with only a code execution node wrapped with RetryNode
|
230
|
+
files = {
|
231
|
+
"__init__.py": "",
|
232
|
+
"workflow.py": """
|
233
|
+
from vellum.workflows import BaseWorkflow
|
234
|
+
from vellum.workflows.state import BaseState
|
235
|
+
|
236
|
+
from .nodes.code_execution_node import CodeExecutionNode
|
237
|
+
|
238
|
+
class Workflow(BaseWorkflow):
|
239
|
+
graph = CodeExecutionNode
|
240
|
+
|
241
|
+
class Outputs(BaseWorkflow.Outputs):
|
242
|
+
final_output = CodeExecutionNode.Outputs.result
|
243
|
+
""",
|
244
|
+
"nodes/__init__.py": """
|
245
|
+
from .code_execution_node import CodeExecutionNode
|
246
|
+
|
247
|
+
__all__ = ["CodeExecutionNode"]
|
248
|
+
""",
|
249
|
+
"nodes/code_execution_node/__init__.py": """
|
250
|
+
from typing import Union
|
251
|
+
|
252
|
+
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
253
|
+
from vellum.workflows.nodes.displayable import CodeExecutionNode as BaseCodeExecutionNode
|
254
|
+
from vellum.workflows.state import BaseState
|
255
|
+
|
256
|
+
@RetryNode.wrap(max_attempts=3)
|
257
|
+
class CodeExecutionNode(BaseCodeExecutionNode[BaseState, Union[float, int]]):
|
258
|
+
filepath = "./script.py"
|
259
|
+
code_inputs = {}
|
260
|
+
runtime = "PYTHON_3_11_6"
|
261
|
+
packages = []
|
262
|
+
""",
|
263
|
+
"nodes/code_execution_node/script.py": py_code,
|
264
|
+
}
|
265
|
+
|
266
|
+
namespace = str(uuid4())
|
267
|
+
|
268
|
+
# AND the virtual file loader is registered
|
269
|
+
finder = VirtualFileFinder(files, namespace)
|
270
|
+
sys.meta_path.append(finder)
|
271
|
+
|
272
|
+
# AND we know what the Code Execution Node will respond with
|
273
|
+
mock_code_execution = CodeExecutorResponse(
|
274
|
+
log="hello",
|
275
|
+
output=NumberVellumValue(value=1),
|
276
|
+
)
|
277
|
+
vellum_client.execute_code.return_value = mock_code_execution
|
278
|
+
|
279
|
+
# WHEN the workflow is loaded
|
280
|
+
Workflow = BaseWorkflow.load_from_module(namespace)
|
281
|
+
workflow = Workflow(context=WorkflowContext(generated_files=files))
|
282
|
+
|
283
|
+
# THEN the workflow is successfully initialized
|
284
|
+
assert workflow
|
285
|
+
|
286
|
+
# WHEN we run the workflow
|
287
|
+
event = workflow.run()
|
288
|
+
|
289
|
+
# THEN the execution is fulfilled
|
290
|
+
assert event.name == "workflow.execution.fulfilled"
|
291
|
+
|
292
|
+
# AND we get the code execution result
|
293
|
+
assert event.body.outputs == {"final_output": 1.0}
|
294
|
+
|
295
|
+
|
296
|
+
def test_load_from_module__code_execution_within_subworkflow(
|
297
|
+
vellum_client,
|
298
|
+
):
|
299
|
+
# GIVEN a simple Python script
|
300
|
+
py_code = """def main() -> int:
|
301
|
+
print("Hello")
|
302
|
+
return 1
|
303
|
+
"""
|
304
|
+
|
305
|
+
# AND a workflow module with a subworkflow containing a code execution node
|
306
|
+
files = {
|
307
|
+
"__init__.py": "",
|
308
|
+
"workflow.py": """
|
309
|
+
from vellum.workflows import BaseWorkflow
|
310
|
+
from vellum.workflows.state import BaseState
|
311
|
+
|
312
|
+
from .nodes.subworkflow import Subworkflow
|
313
|
+
|
314
|
+
class Workflow(BaseWorkflow):
|
315
|
+
graph = Subworkflow
|
316
|
+
|
317
|
+
class Outputs(BaseWorkflow.Outputs):
|
318
|
+
final_output = Subworkflow.Outputs.result
|
319
|
+
""",
|
320
|
+
"nodes/__init__.py": """
|
321
|
+
from .subworkflow import Subworkflow
|
322
|
+
|
323
|
+
__all__ = ["Subworkflow"]
|
324
|
+
""",
|
325
|
+
"nodes/subworkflow/__init__.py": """
|
326
|
+
from vellum.workflows.nodes.displayable import InlineSubworkflowNode
|
327
|
+
|
328
|
+
from .workflow import SubworkflowWorkflow
|
329
|
+
|
330
|
+
class Subworkflow(InlineSubworkflowNode):
|
331
|
+
subworkflow = SubworkflowWorkflow
|
332
|
+
""",
|
333
|
+
"nodes/subworkflow/nodes/__init__.py": """
|
334
|
+
from .code_execution_node import CodeExecutionNode
|
335
|
+
|
336
|
+
__all__ = ["CodeExecutionNode"]
|
337
|
+
""",
|
338
|
+
"nodes/subworkflow/nodes/code_execution_node/__init__.py": """
|
339
|
+
from typing import Union
|
340
|
+
|
341
|
+
from vellum.workflows.nodes.displayable import CodeExecutionNode as BaseCodeExecutionNode
|
342
|
+
from vellum.workflows.state import BaseState
|
343
|
+
|
344
|
+
class CodeExecutionNode(BaseCodeExecutionNode[BaseState, Union[float, int]]):
|
345
|
+
filepath = "./script.py"
|
346
|
+
code_inputs = {}
|
347
|
+
runtime = "PYTHON_3_11_6"
|
348
|
+
packages = []
|
349
|
+
""",
|
350
|
+
"nodes/subworkflow/nodes/code_execution_node/script.py": py_code,
|
351
|
+
"nodes/subworkflow/workflow.py": """
|
352
|
+
from vellum.workflows import BaseWorkflow
|
353
|
+
|
354
|
+
from .nodes.code_execution_node import CodeExecutionNode
|
355
|
+
|
356
|
+
class SubworkflowWorkflow(BaseWorkflow):
|
357
|
+
graph = CodeExecutionNode
|
358
|
+
|
359
|
+
class Outputs(BaseWorkflow.Outputs):
|
360
|
+
result = CodeExecutionNode.Outputs.result
|
361
|
+
""",
|
362
|
+
}
|
363
|
+
|
364
|
+
namespace = str(uuid4())
|
365
|
+
|
366
|
+
# AND the virtual file loader is registered
|
367
|
+
finder = VirtualFileFinder(files, namespace)
|
368
|
+
sys.meta_path.append(finder)
|
369
|
+
|
370
|
+
# AND we know what the Code Execution Node will respond with
|
371
|
+
mock_code_execution = CodeExecutorResponse(
|
372
|
+
log="hello",
|
373
|
+
output=NumberVellumValue(value=1),
|
374
|
+
)
|
375
|
+
vellum_client.execute_code.return_value = mock_code_execution
|
376
|
+
|
377
|
+
# WHEN the workflow is loaded
|
378
|
+
Workflow = BaseWorkflow.load_from_module(namespace)
|
379
|
+
workflow = Workflow(context=WorkflowContext(generated_files=files))
|
380
|
+
|
381
|
+
# THEN the workflow is successfully initialized
|
382
|
+
assert workflow
|
383
|
+
|
384
|
+
# WHEN we run the workflow
|
385
|
+
event = workflow.run()
|
386
|
+
|
387
|
+
# THEN the execution is fulfilled
|
388
|
+
assert event.name == "workflow.execution.fulfilled"
|
389
|
+
|
390
|
+
# AND we get the code execution result from the subworkflow
|
391
|
+
assert event.body.outputs == {"final_output": 1.0}
|
392
|
+
|
393
|
+
|
394
|
+
def test_load_from_module__code_execution_within_map_node(
|
395
|
+
vellum_client,
|
396
|
+
):
|
397
|
+
# GIVEN a simple Python script
|
398
|
+
py_code = """def main() -> int:
|
399
|
+
print("Hello")
|
400
|
+
return 1
|
401
|
+
"""
|
402
|
+
|
403
|
+
# AND a workflow module with a map node containing a code execution node
|
404
|
+
files = {
|
405
|
+
"__init__.py": "",
|
406
|
+
"workflow.py": """
|
407
|
+
from vellum.workflows import BaseWorkflow
|
408
|
+
from vellum.workflows.state import BaseState
|
409
|
+
|
410
|
+
from .nodes.map_node import MapNode
|
411
|
+
|
412
|
+
class Workflow(BaseWorkflow):
|
413
|
+
graph = MapNode
|
414
|
+
|
415
|
+
class Outputs(BaseWorkflow.Outputs): # noqa: W293
|
416
|
+
results = MapNode.Outputs.final_output
|
417
|
+
""",
|
418
|
+
"nodes/__init__.py": """
|
419
|
+
from .map_node import MapNode
|
420
|
+
|
421
|
+
__all__ = ["MapNode"]
|
422
|
+
""",
|
423
|
+
"nodes/map_node/__init__.py": """
|
424
|
+
from vellum.workflows.nodes.core.map_node import MapNode as BaseMapNode
|
425
|
+
|
426
|
+
from .workflow import MapNodeWorkflow
|
427
|
+
|
428
|
+
class MapNode(BaseMapNode):
|
429
|
+
items = ["foo", "bar", "baz"]
|
430
|
+
subworkflow = MapNodeWorkflow
|
431
|
+
max_concurrency = 4
|
432
|
+
""",
|
433
|
+
"nodes/map_node/nodes/__init__.py": """
|
434
|
+
from .code_execution_node import CodeExecutionNode
|
435
|
+
|
436
|
+
__all__ = ["CodeExecutionNode"]
|
437
|
+
""",
|
438
|
+
"nodes/map_node/nodes/code_execution_node/__init__.py": """
|
439
|
+
from typing import Union
|
440
|
+
|
441
|
+
from vellum.workflows.nodes.displayable import CodeExecutionNode as BaseCodeExecutionNode
|
442
|
+
from vellum.workflows.state import BaseState
|
443
|
+
|
444
|
+
class CodeExecutionNode(BaseCodeExecutionNode[BaseState, Union[float, int]]):
|
445
|
+
filepath = "./script.py"
|
446
|
+
code_inputs = {}
|
447
|
+
runtime = "PYTHON_3_11_6"
|
448
|
+
packages = []
|
449
|
+
""",
|
450
|
+
"nodes/map_node/nodes/code_execution_node/script.py": py_code,
|
451
|
+
"nodes/map_node/workflow.py": """
|
452
|
+
from vellum.workflows import BaseWorkflow
|
453
|
+
from vellum.workflows.state import BaseState
|
454
|
+
|
455
|
+
from .nodes.code_execution_node import CodeExecutionNode
|
456
|
+
|
457
|
+
class MapNodeInputs:
|
458
|
+
item: str
|
459
|
+
index: int
|
460
|
+
|
461
|
+
class MapNodeWorkflow(BaseWorkflow):
|
462
|
+
graph = CodeExecutionNode
|
463
|
+
|
464
|
+
class Outputs(BaseWorkflow.Outputs): # noqa: W293
|
465
|
+
final_output = CodeExecutionNode.Outputs.result
|
466
|
+
""",
|
467
|
+
}
|
468
|
+
|
469
|
+
namespace = str(uuid4())
|
470
|
+
|
471
|
+
# AND the virtual file loader is registered
|
472
|
+
finder = VirtualFileFinder(files, namespace)
|
473
|
+
sys.meta_path.append(finder)
|
474
|
+
|
475
|
+
# AND we know what the Code Execution Node will respond with
|
476
|
+
mock_code_execution = CodeExecutorResponse(
|
477
|
+
log="hello",
|
478
|
+
output=NumberVellumValue(value=3),
|
479
|
+
)
|
480
|
+
vellum_client.execute_code.return_value = mock_code_execution
|
481
|
+
|
482
|
+
# WHEN the workflow is loaded
|
483
|
+
Workflow = BaseWorkflow.load_from_module(namespace)
|
484
|
+
workflow = Workflow(context=WorkflowContext(generated_files=files))
|
485
|
+
|
486
|
+
# THEN the workflow is successfully initialized
|
487
|
+
assert workflow
|
488
|
+
|
489
|
+
# WHEN we run the workflow
|
490
|
+
event = workflow.run()
|
491
|
+
|
492
|
+
# THEN the execution is fulfilled
|
493
|
+
assert event.name == "workflow.execution.fulfilled"
|
494
|
+
|
495
|
+
# AND we get the map node results as a list
|
496
|
+
assert event.body.outputs == {"results": [1.0, 1.0, 1.0]}
|
File without changes
|
File without changes
|
File without changes
|