vellum-ai 0.14.5__py3-none-any.whl → 0.14.7__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 +18 -0
- vellum/client/__init__.py +8 -8
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/resources/__init__.py +2 -0
- vellum/client/resources/workflow_sandboxes/__init__.py +3 -0
- vellum/client/resources/workflow_sandboxes/client.py +146 -0
- vellum/client/resources/workflow_sandboxes/types/__init__.py +5 -0
- vellum/client/resources/workflow_sandboxes/types/list_workflow_sandbox_examples_request_tag.py +5 -0
- vellum/client/types/__init__.py +16 -0
- vellum/client/types/array_chat_message_content_item.py +6 -1
- vellum/client/types/array_chat_message_content_item_request.py +2 -0
- vellum/client/types/chat_message_content.py +2 -0
- vellum/client/types/chat_message_content_request.py +2 -0
- vellum/client/types/document_chat_message_content.py +25 -0
- vellum/client/types/document_chat_message_content_request.py +25 -0
- vellum/client/types/document_vellum_value.py +25 -0
- vellum/client/types/document_vellum_value_request.py +25 -0
- vellum/client/types/paginated_workflow_sandbox_example_list.py +23 -0
- vellum/client/types/vellum_document.py +20 -0
- vellum/client/types/vellum_document_request.py +20 -0
- vellum/client/types/vellum_value.py +2 -0
- vellum/client/types/vellum_value_request.py +2 -0
- vellum/client/types/vellum_variable_type.py +1 -0
- vellum/client/types/workflow_sandbox_example.py +22 -0
- vellum/resources/workflow_sandboxes/types/__init__.py +3 -0
- vellum/resources/workflow_sandboxes/types/list_workflow_sandbox_examples_request_tag.py +3 -0
- vellum/types/document_chat_message_content.py +3 -0
- vellum/types/document_chat_message_content_request.py +3 -0
- vellum/types/document_vellum_value.py +3 -0
- vellum/types/document_vellum_value_request.py +3 -0
- vellum/types/paginated_workflow_sandbox_example_list.py +3 -0
- vellum/types/vellum_document.py +3 -0
- vellum/types/vellum_document_request.py +3 -0
- vellum/types/workflow_sandbox_example.py +3 -0
- vellum/workflows/exceptions.py +18 -0
- vellum/workflows/inputs/base.py +27 -1
- vellum/workflows/inputs/tests/__init__.py +0 -0
- vellum/workflows/inputs/tests/test_inputs.py +49 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -1
- vellum/workflows/nodes/core/map_node/node.py +7 -7
- vellum/workflows/nodes/core/try_node/node.py +1 -1
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +2 -2
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +5 -3
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +5 -4
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +4 -4
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +49 -15
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +165 -0
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +3 -1
- vellum/workflows/outputs/base.py +1 -1
- vellum/workflows/runner/runner.py +16 -10
- vellum/workflows/state/context.py +7 -7
- vellum/workflows/workflows/base.py +61 -59
- vellum/workflows/workflows/tests/test_base_workflow.py +131 -40
- {vellum_ai-0.14.5.dist-info → vellum_ai-0.14.7.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.5.dist-info → vellum_ai-0.14.7.dist-info}/RECORD +68 -44
- vellum_cli/__init__.py +36 -0
- vellum_cli/init.py +128 -0
- vellum_cli/pull.py +6 -3
- vellum_cli/tests/test_init.py +355 -0
- vellum_cli/tests/test_pull.py +127 -0
- vellum_ee/workflows/display/nodes/base_node_display.py +4 -4
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +31 -0
- vellum_ee/workflows/display/nodes/vellum/utils.py +8 -0
- vellum_ee/workflows/display/vellum.py +0 -4
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +29 -0
- {vellum_ai-0.14.5.dist-info → vellum_ai-0.14.7.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.5.dist-info → vellum_ai-0.14.7.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.5.dist-info → vellum_ai-0.14.7.dist-info}/entry_points.txt +0 -0
vellum_cli/tests/test_pull.py
CHANGED
@@ -112,6 +112,133 @@ def test_pull__second_module(vellum_client, mock_module):
|
|
112
112
|
assert f.read() == "print('hello')"
|
113
113
|
|
114
114
|
|
115
|
+
@pytest.mark.parametrize(
|
116
|
+
"base_command",
|
117
|
+
[
|
118
|
+
["pull"],
|
119
|
+
["workflows", "pull"],
|
120
|
+
],
|
121
|
+
ids=["pull", "workflows_pull"],
|
122
|
+
)
|
123
|
+
def test_pull__with_target_dir(vellum_client, mock_module, base_command):
|
124
|
+
# GIVEN a module on the user's filesystem
|
125
|
+
temp_dir = mock_module.temp_dir
|
126
|
+
module = mock_module.module
|
127
|
+
workflow_sandbox_id = mock_module.workflow_sandbox_id
|
128
|
+
|
129
|
+
# AND a target directory
|
130
|
+
target_dir = os.path.join(temp_dir, "dir")
|
131
|
+
os.makedirs(target_dir, exist_ok=True)
|
132
|
+
|
133
|
+
# AND the workflow pull API call returns a zip file
|
134
|
+
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
135
|
+
|
136
|
+
# WHEN the user runs the pull command with target-dir
|
137
|
+
runner = CliRunner()
|
138
|
+
result = runner.invoke(cli_main, base_command + [module, "--target-dir", target_dir])
|
139
|
+
|
140
|
+
# THEN the command returns successfully
|
141
|
+
assert result.exit_code == 0
|
142
|
+
|
143
|
+
# AND the workflow.py file is written to the target directory
|
144
|
+
module_path = os.path.join(target_dir, *module.split("."))
|
145
|
+
workflow_py = os.path.join(module_path, "workflow.py")
|
146
|
+
assert os.path.exists(workflow_py)
|
147
|
+
with open(workflow_py) as f:
|
148
|
+
assert f.read() == "print('hello')"
|
149
|
+
|
150
|
+
# AND the files are not in the default module directory
|
151
|
+
default_module_path = os.path.join(temp_dir, *module.split("."), "workflow.py")
|
152
|
+
assert not os.path.exists(default_module_path)
|
153
|
+
|
154
|
+
# AND the vellum.lock.json file is still updated
|
155
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
156
|
+
assert os.path.exists(vellum_lock_json)
|
157
|
+
with open(vellum_lock_json) as f:
|
158
|
+
lock_data = json.load(f)
|
159
|
+
assert lock_data == {
|
160
|
+
"version": "1.0",
|
161
|
+
"workflows": [
|
162
|
+
{
|
163
|
+
"module": module,
|
164
|
+
"workflow_sandbox_id": workflow_sandbox_id,
|
165
|
+
"container_image_name": None,
|
166
|
+
"container_image_tag": None,
|
167
|
+
"ignore": None,
|
168
|
+
"deployments": [],
|
169
|
+
"workspace": "default",
|
170
|
+
}
|
171
|
+
],
|
172
|
+
"workspaces": [],
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
@pytest.mark.parametrize(
|
177
|
+
"base_command",
|
178
|
+
[
|
179
|
+
["pull"],
|
180
|
+
["workflows", "pull"],
|
181
|
+
],
|
182
|
+
ids=["pull", "workflows_pull"],
|
183
|
+
)
|
184
|
+
def test_pull__with_nested_target_dir(vellum_client, mock_module, base_command):
|
185
|
+
# GIVEN a module on the user's filesystem
|
186
|
+
temp_dir = mock_module.temp_dir
|
187
|
+
module = mock_module.module
|
188
|
+
workflow_sandbox_id = mock_module.workflow_sandbox_id
|
189
|
+
|
190
|
+
# AND a nested target directory that doesn't exist yet
|
191
|
+
nested_target_dir = os.path.join(temp_dir, "dir-1", "dir-2")
|
192
|
+
|
193
|
+
# AND the workflow pull API call returns a zip file
|
194
|
+
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
195
|
+
|
196
|
+
# WHEN the user runs the pull command with nested target-dir
|
197
|
+
runner = CliRunner()
|
198
|
+
result = runner.invoke(cli_main, base_command + [module, "--target-dir", nested_target_dir])
|
199
|
+
|
200
|
+
# THEN the command returns successfully
|
201
|
+
assert result.exit_code == 0
|
202
|
+
|
203
|
+
# AND the nested directory with module subdirectory should be created
|
204
|
+
module_path = os.path.join(nested_target_dir, *module.split("."))
|
205
|
+
assert os.path.exists(module_path)
|
206
|
+
|
207
|
+
# AND the nested directory should be created
|
208
|
+
assert os.path.exists(module_path)
|
209
|
+
|
210
|
+
# AND the workflow.py file is written to the nested target directory
|
211
|
+
workflow_py = os.path.join(module_path, "workflow.py")
|
212
|
+
assert os.path.exists(workflow_py)
|
213
|
+
with open(workflow_py) as f:
|
214
|
+
assert f.read() == "print('hello')"
|
215
|
+
|
216
|
+
# AND the files are not in the default module directory
|
217
|
+
default_module_path = os.path.join(temp_dir, *module.split("."), "workflow.py")
|
218
|
+
assert not os.path.exists(default_module_path)
|
219
|
+
|
220
|
+
# AND the vellum.lock.json file is still updated
|
221
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
222
|
+
assert os.path.exists(vellum_lock_json)
|
223
|
+
with open(vellum_lock_json) as f:
|
224
|
+
lock_data = json.load(f)
|
225
|
+
assert lock_data == {
|
226
|
+
"version": "1.0",
|
227
|
+
"workflows": [
|
228
|
+
{
|
229
|
+
"module": module,
|
230
|
+
"workflow_sandbox_id": workflow_sandbox_id,
|
231
|
+
"container_image_name": None,
|
232
|
+
"container_image_tag": None,
|
233
|
+
"ignore": None,
|
234
|
+
"deployments": [],
|
235
|
+
"workspace": "default",
|
236
|
+
}
|
237
|
+
],
|
238
|
+
"workspaces": [],
|
239
|
+
}
|
240
|
+
|
241
|
+
|
115
242
|
def test_pull__sandbox_id_with_no_config(vellum_client):
|
116
243
|
# GIVEN a workflow sandbox id
|
117
244
|
workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
|
@@ -47,7 +47,7 @@ from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_di
|
|
47
47
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
|
48
48
|
from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
|
49
49
|
from vellum_ee.workflows.display.utils.vellum import convert_descriptor_to_operator, primitive_to_vellum_value
|
50
|
-
from vellum_ee.workflows.display.vellum import CodeResourceDefinition,
|
50
|
+
from vellum_ee.workflows.display.vellum import CodeResourceDefinition, NodeDisplayData
|
51
51
|
|
52
52
|
if TYPE_CHECKING:
|
53
53
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
@@ -295,9 +295,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
295
295
|
|
296
296
|
cls._node_display_registry[node_class] = cls
|
297
297
|
|
298
|
-
def _get_generic_node_display_data(self) ->
|
299
|
-
explicit_value = self._get_explicit_node_display_attr("display_data",
|
300
|
-
return explicit_value if explicit_value else
|
298
|
+
def _get_generic_node_display_data(self) -> NodeDisplayData:
|
299
|
+
explicit_value = self._get_explicit_node_display_attr("display_data", NodeDisplayData)
|
300
|
+
return explicit_value if explicit_value else NodeDisplayData()
|
301
301
|
|
302
302
|
def serialize_condition(self, display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
|
303
303
|
if isinstance(
|
@@ -7,6 +7,7 @@ from vellum.workflows.descriptors.base import BaseDescriptor
|
|
7
7
|
from vellum.workflows.inputs import BaseInputs
|
8
8
|
from vellum.workflows.nodes.bases import BaseNode
|
9
9
|
from vellum.workflows.outputs import BaseOutputs
|
10
|
+
from vellum.workflows.references import LazyReference
|
10
11
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
11
12
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
12
13
|
from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input_value_pointer_rules
|
@@ -43,6 +44,10 @@ class MyNodeADisplay(BaseNodeVellumDisplay[MyNodeA]):
|
|
43
44
|
class MyNodeB(BaseNode):
|
44
45
|
example = MyNodeA.Outputs.output
|
45
46
|
fallback_example = MyNodeA.Outputs.output.coalesce(Inputs.example_workflow_input).coalesce("fallback")
|
47
|
+
constant_coalesce = Inputs.example_workflow_input.coalesce("default_value")
|
48
|
+
lazy_coalesce: BaseDescriptor[str] = LazyReference(
|
49
|
+
lambda: MyNodeA.Outputs.output.coalesce(Inputs.example_workflow_input)
|
50
|
+
)
|
46
51
|
|
47
52
|
|
48
53
|
@pytest.mark.parametrize(
|
@@ -76,6 +81,32 @@ class MyNodeB(BaseNode):
|
|
76
81
|
ConstantValuePointer(type="CONSTANT_VALUE", data=StringVellumValue(value="fallback")),
|
77
82
|
],
|
78
83
|
),
|
84
|
+
(
|
85
|
+
MyNodeB.constant_coalesce,
|
86
|
+
[
|
87
|
+
InputVariablePointer(
|
88
|
+
type="INPUT_VARIABLE",
|
89
|
+
data=InputVariableData(input_variable_id="a154c29d-fac0-4cd0-ba88-bc52034f5470"),
|
90
|
+
),
|
91
|
+
ConstantValuePointer(type="CONSTANT_VALUE", data=StringVellumValue(value="default_value")),
|
92
|
+
],
|
93
|
+
),
|
94
|
+
(
|
95
|
+
MyNodeB.lazy_coalesce,
|
96
|
+
[
|
97
|
+
NodeOutputPointer(
|
98
|
+
type="NODE_OUTPUT",
|
99
|
+
data=NodeOutputData(
|
100
|
+
node_id="b48fa5e0-d7d3-4fe3-ae48-615415011cc5",
|
101
|
+
output_id="4b16a629-11a1-4b3f-a965-a57b872d13b8",
|
102
|
+
),
|
103
|
+
),
|
104
|
+
InputVariablePointer(
|
105
|
+
type="INPUT_VARIABLE",
|
106
|
+
data=InputVariableData(input_variable_id="a154c29d-fac0-4cd0-ba88-bc52034f5470"),
|
107
|
+
),
|
108
|
+
],
|
109
|
+
),
|
79
110
|
],
|
80
111
|
)
|
81
112
|
def test_create_node_input_value_pointer_rules(
|
@@ -5,8 +5,10 @@ from vellum.workflows.descriptors.base import BaseDescriptor
|
|
5
5
|
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
6
6
|
from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
|
7
7
|
from vellum.workflows.references import NodeReference
|
8
|
+
from vellum.workflows.references.lazy import LazyReference
|
8
9
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
9
10
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
11
|
+
from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
|
10
12
|
from vellum_ee.workflows.display.utils.vellum import create_node_input_value_pointer_rule
|
11
13
|
from vellum_ee.workflows.display.vellum import (
|
12
14
|
ConstantValuePointer,
|
@@ -56,6 +58,12 @@ def create_node_input_value_pointer_rules(
|
|
56
58
|
raise ValueError(f"Expected NodeReference {value.name} to have an instance")
|
57
59
|
value = cast(BaseDescriptor, value.instance)
|
58
60
|
|
61
|
+
if isinstance(value, LazyReference):
|
62
|
+
child_descriptor = get_child_descriptor(value, display_context)
|
63
|
+
return create_node_input_value_pointer_rules(
|
64
|
+
child_descriptor, display_context, [], pointer_type=pointer_type
|
65
|
+
)
|
66
|
+
|
59
67
|
if isinstance(value, CoalesceExpression):
|
60
68
|
# Recursively handle the left-hand side
|
61
69
|
lhs_rules = create_node_input_value_pointer_rules(value.lhs, display_context, [], pointer_type=pointer_type)
|
@@ -41,10 +41,6 @@ class NodeDisplayData(UniversalBaseModel):
|
|
41
41
|
comment: Optional[NodeDisplayComment] = None
|
42
42
|
|
43
43
|
|
44
|
-
class GenericNodeDisplayData(UniversalBaseModel):
|
45
|
-
position: NodeDisplayPosition = Field(default_factory=NodeDisplayPosition)
|
46
|
-
|
47
|
-
|
48
44
|
class CodeResourceDefinition(UniversalBaseModel):
|
49
45
|
name: str
|
50
46
|
module: List[str]
|
@@ -2,6 +2,8 @@ import pytest
|
|
2
2
|
|
3
3
|
from vellum.workflows.nodes.bases.base import BaseNode
|
4
4
|
from vellum.workflows.workflows.base import BaseWorkflow
|
5
|
+
from vellum_ee.workflows.display.nodes import BaseNodeDisplay
|
6
|
+
from vellum_ee.workflows.display.vellum import NodeDisplayData, NodeDisplayPosition
|
5
7
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
6
8
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
7
9
|
|
@@ -63,3 +65,30 @@ def test_serialize_workflow__workflow_outputs_reference_non_node_outputs():
|
|
63
65
|
== """Failed to serialize output 'final': Reference to outputs \
|
64
66
|
'test_serialize_workflow__workflow_outputs_reference_non_node_outputs.<locals>.FirstWorkflow.Outputs' is invalid."""
|
65
67
|
)
|
68
|
+
|
69
|
+
|
70
|
+
def test_serialize_workflow__node_display_class_not_registered():
|
71
|
+
# GIVEN a workflow with a node that has a display class referencing display data
|
72
|
+
class StartNode(BaseNode):
|
73
|
+
class Outputs(BaseNode.Outputs):
|
74
|
+
result: str
|
75
|
+
|
76
|
+
class StartNodeDisplay(BaseNodeDisplay[StartNode]):
|
77
|
+
node_input_ids_by_name = {}
|
78
|
+
display_data = NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=None, height=None)
|
79
|
+
|
80
|
+
class MyWorkflow(BaseWorkflow):
|
81
|
+
graph = StartNode
|
82
|
+
|
83
|
+
class Outputs(BaseWorkflow.Outputs):
|
84
|
+
answer = StartNode.Outputs.result
|
85
|
+
|
86
|
+
# WHEN we serialize it
|
87
|
+
workflow_display = get_workflow_display(
|
88
|
+
base_display_class=VellumWorkflowDisplay,
|
89
|
+
workflow_class=MyWorkflow,
|
90
|
+
)
|
91
|
+
data = workflow_display.serialize()
|
92
|
+
|
93
|
+
# THEN it should should succeed
|
94
|
+
assert data is not None
|
File without changes
|
File without changes
|
File without changes
|