vellum-ai 0.14.46__py3-none-any.whl → 0.14.48__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/README.md +2 -2
- vellum/client/__init__.py +72 -6
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/core/file.py +13 -8
- vellum/client/core/http_client.py +26 -14
- vellum/client/core/pydantic_utilities.py +2 -2
- vellum/client/core/request_options.py +3 -0
- vellum/client/resources/ad_hoc/client.py +14 -2
- vellum/client/resources/container_images/client.py +6 -0
- vellum/client/resources/deployments/client.py +12 -0
- vellum/client/resources/document_indexes/client.py +18 -0
- vellum/client/resources/documents/client.py +6 -0
- vellum/client/resources/folder_entities/client.py +6 -0
- vellum/client/resources/metric_definitions/client.py +6 -0
- vellum/client/resources/prompts/client.py +6 -0
- vellum/client/resources/sandboxes/client.py +12 -0
- vellum/client/resources/test_suite_runs/client.py +6 -0
- vellum/client/resources/test_suites/client.py +2 -2
- vellum/client/resources/workflow_deployments/client.py +6 -0
- vellum/client/resources/workflow_sandboxes/client.py +6 -0
- vellum/client/resources/workflows/client.py +6 -4
- vellum/client/resources/workspace_secrets/client.py +6 -0
- vellum/client/types/api_request_parent_context.py +0 -6
- vellum/client/types/array_input.py +0 -5
- vellum/client/types/code_execution_node_array_result.py +0 -5
- vellum/client/types/code_execution_node_result.py +0 -5
- vellum/client/types/code_execution_node_result_data.py +0 -5
- vellum/client/types/code_executor_response.py +0 -5
- vellum/client/types/create_test_suite_test_case_request.py +0 -5
- vellum/client/types/deployment_history_item.py +0 -5
- vellum/client/types/deployment_read.py +0 -5
- vellum/client/types/execute_workflow_response.py +0 -5
- vellum/client/types/execution_array_vellum_value.py +0 -5
- vellum/client/types/external_test_case_execution.py +0 -5
- vellum/client/types/external_test_case_execution_request.py +0 -5
- vellum/client/types/fulfilled_execute_workflow_workflow_result_event.py +0 -7
- vellum/client/types/fulfilled_workflow_node_result_event.py +0 -5
- vellum/client/types/initiated_workflow_node_result_event.py +0 -5
- vellum/client/types/metadata_filter_config_request.py +0 -5
- vellum/client/types/metric_definition_execution.py +0 -5
- vellum/client/types/metric_definition_history_item.py +0 -5
- vellum/client/types/named_test_case_array_variable_value.py +0 -5
- vellum/client/types/named_test_case_array_variable_value_request.py +0 -7
- vellum/client/types/node_execution_fulfilled_event.py +0 -11
- vellum/client/types/node_execution_initiated_event.py +0 -11
- vellum/client/types/node_execution_paused_event.py +0 -11
- vellum/client/types/node_execution_rejected_event.py +0 -11
- vellum/client/types/node_execution_resumed_event.py +0 -11
- vellum/client/types/node_execution_span.py +0 -11
- vellum/client/types/node_execution_streaming_event.py +0 -11
- vellum/client/types/node_input_compiled_array_value.py +0 -5
- vellum/client/types/node_output_compiled_array_value.py +0 -5
- vellum/client/types/node_parent_context.py +0 -6
- vellum/client/types/paginated_slim_deployment_read_list.py +0 -5
- vellum/client/types/paginated_slim_workflow_deployment_list.py +0 -5
- vellum/client/types/paginated_test_suite_run_execution_list.py +0 -5
- vellum/client/types/paginated_test_suite_test_case_list.py +0 -5
- vellum/client/types/prompt_deployment_parent_context.py +0 -6
- vellum/client/types/prompt_exec_config.py +0 -6
- vellum/client/types/rejected_workflow_node_result_event.py +0 -5
- vellum/client/types/replace_test_suite_test_case_request.py +0 -5
- vellum/client/types/search_filters_request.py +0 -7
- vellum/client/types/search_request_options_request.py +0 -7
- vellum/client/types/slim_deployment_read.py +0 -5
- vellum/client/types/slim_workflow_deployment.py +0 -5
- vellum/client/types/slim_workflow_execution_read.py +0 -12
- vellum/client/types/span_link.py +0 -6
- vellum/client/types/streaming_workflow_node_result_event.py +0 -5
- vellum/client/types/templating_node_array_result.py +0 -5
- vellum/client/types/templating_node_result.py +0 -5
- vellum/client/types/templating_node_result_data.py +0 -5
- vellum/client/types/terminal_node_array_result.py +0 -5
- vellum/client/types/terminal_node_result.py +0 -5
- vellum/client/types/terminal_node_result_data.py +0 -5
- vellum/client/types/test_case_array_variable_value.py +0 -5
- vellum/client/types/test_suite_run_execution.py +0 -5
- vellum/client/types/test_suite_run_execution_array_output.py +0 -5
- vellum/client/types/test_suite_run_execution_metric_result.py +0 -5
- vellum/client/types/test_suite_run_external_exec_config.py +0 -5
- vellum/client/types/test_suite_run_external_exec_config_data.py +0 -5
- vellum/client/types/test_suite_run_external_exec_config_data_request.py +0 -7
- vellum/client/types/test_suite_run_external_exec_config_request.py +0 -7
- vellum/client/types/test_suite_run_metric_array_output.py +0 -5
- vellum/client/types/test_suite_run_read.py +0 -5
- vellum/client/types/test_suite_test_case.py +0 -5
- vellum/client/types/test_suite_test_case_create_bulk_operation_request.py +0 -7
- vellum/client/types/test_suite_test_case_replace_bulk_operation_request.py +0 -7
- vellum/client/types/test_suite_test_case_upsert_bulk_operation_request.py +0 -7
- vellum/client/types/upsert_test_suite_test_case_request.py +0 -5
- vellum/client/types/vellum_value_logical_condition_group_request.py +0 -3
- vellum/client/types/vellum_value_logical_condition_request.py +0 -5
- vellum/client/types/vellum_variable.py +0 -5
- vellum/client/types/workflow_deployment_event_executions_response.py +0 -26
- vellum/client/types/workflow_deployment_history_item.py +0 -5
- vellum/client/types/workflow_deployment_parent_context.py +0 -6
- vellum/client/types/workflow_deployment_read.py +0 -5
- vellum/client/types/workflow_deployment_release.py +0 -5
- vellum/client/types/workflow_deployment_release_workflow_version.py +0 -5
- vellum/client/types/workflow_event_execution_read.py +0 -12
- vellum/client/types/workflow_execution_actual.py +0 -5
- vellum/client/types/workflow_execution_fulfilled_event.py +0 -11
- vellum/client/types/workflow_execution_initiated_event.py +0 -11
- vellum/client/types/workflow_execution_node_result_event.py +0 -5
- vellum/client/types/workflow_execution_paused_event.py +0 -11
- vellum/client/types/workflow_execution_rejected_event.py +0 -11
- vellum/client/types/workflow_execution_resumed_event.py +0 -11
- vellum/client/types/workflow_execution_snapshotted_event.py +0 -13
- vellum/client/types/workflow_execution_span.py +0 -11
- vellum/client/types/workflow_execution_streaming_event.py +0 -11
- vellum/client/types/workflow_execution_view_online_eval_metric_result.py +0 -7
- vellum/client/types/workflow_execution_workflow_result_event.py +0 -5
- vellum/client/types/workflow_output_array.py +0 -5
- vellum/client/types/workflow_parent_context.py +0 -6
- vellum/client/types/workflow_result_event.py +0 -5
- vellum/client/types/workflow_result_event_output_data_array.py +0 -5
- vellum/client/types/workflow_sandbox_parent_context.py +0 -6
- vellum/workflows/nodes/bases/base.py +26 -6
- vellum/workflows/nodes/bases/tests/test_base_node.py +30 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +50 -0
- vellum/workflows/nodes/utils.py +5 -1
- vellum/workflows/types/code_execution_node_wrappers.py +5 -0
- {vellum_ai-0.14.46.dist-info → vellum_ai-0.14.48.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.46.dist-info → vellum_ai-0.14.48.dist-info}/RECORD +152 -152
- vellum_cli/__init__.py +3 -2
- vellum_cli/image_push.py +15 -3
- vellum_cli/tests/test_image_push.py +109 -0
- vellum_ee/workflows/display/nodes/base_node_display.py +25 -9
- vellum_ee/workflows/display/nodes/get_node_display_class.py +4 -5
- vellum_ee/workflows/display/nodes/vellum/api_node.py +11 -0
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +5 -0
- vellum_ee/workflows/display/nodes/vellum/error_node.py +22 -16
- vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +47 -13
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/search_node.py +8 -0
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py +4 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py +4 -3
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +67 -2
- vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +5 -4
- vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py +1 -1
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +44 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +2 -4
- vellum_ee/workflows/display/utils/expressions.py +31 -4
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +162 -0
- {vellum_ai-0.14.46.dist-info → vellum_ai-0.14.48.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.46.dist-info → vellum_ai-0.14.48.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.46.dist-info → vellum_ai-0.14.48.dist-info}/entry_points.txt +0 -0
@@ -2,9 +2,11 @@ import pytest
|
|
2
2
|
from uuid import UUID
|
3
3
|
from typing import Type
|
4
4
|
|
5
|
+
from vellum.client.types.variable_prompt_block import VariablePromptBlock
|
5
6
|
from vellum.workflows import BaseWorkflow
|
6
7
|
from vellum.workflows.nodes import BaseNode
|
7
8
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
9
|
+
from vellum.workflows.ports.port import Port
|
8
10
|
from vellum.workflows.references.lazy import LazyReference
|
9
11
|
from vellum.workflows.state.base import BaseState
|
10
12
|
from vellum_ee.workflows.display.nodes.vellum.inline_prompt_node import BaseInlinePromptNodeDisplay
|
@@ -78,7 +80,7 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Inl
|
|
78
80
|
@pytest.mark.parametrize(
|
79
81
|
["GetDisplayClass", "expected_input_id"],
|
80
82
|
[
|
81
|
-
(_no_display_class, "
|
83
|
+
(_no_display_class, "9b036991-67ff-4cd0-a4d7-b4ed581e8b6d"),
|
82
84
|
(_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
83
85
|
(_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
84
86
|
],
|
@@ -165,7 +167,7 @@ def test_serialize_node__prompt_inputs__state_reference():
|
|
165
167
|
},
|
166
168
|
},
|
167
169
|
{
|
168
|
-
"id": "
|
170
|
+
"id": "b83c40f7-0159-442f-af03-e80870363c52",
|
169
171
|
"key": "bar",
|
170
172
|
"value": {
|
171
173
|
"rules": [
|
@@ -181,3 +183,66 @@ def test_serialize_node__prompt_inputs__state_reference():
|
|
181
183
|
},
|
182
184
|
},
|
183
185
|
]
|
186
|
+
|
187
|
+
|
188
|
+
def test_serialize_node__unreferenced_variable_block__still_serializes():
|
189
|
+
# GIVEN a prompt node with an unreferenced variable block
|
190
|
+
class MyPromptNode(InlinePromptNode):
|
191
|
+
blocks = [VariablePromptBlock(input_variable="foo")]
|
192
|
+
|
193
|
+
# AND a workflow with the prompt node
|
194
|
+
class MyWorkflow(BaseWorkflow):
|
195
|
+
graph = MyPromptNode
|
196
|
+
|
197
|
+
# WHEN the prompt node is serialized
|
198
|
+
workflow_display = get_workflow_display(workflow_class=MyWorkflow)
|
199
|
+
serialized_workflow: dict = workflow_display.serialize()
|
200
|
+
|
201
|
+
# THEN the node should skip the state reference input rule
|
202
|
+
assert serialized_workflow["workflow_raw_data"]["nodes"][1]["data"]["exec_config"]["prompt_template_block_data"][
|
203
|
+
"blocks"
|
204
|
+
] == [
|
205
|
+
{
|
206
|
+
"id": "fecbb5f3-e0a3-42ed-9774-6c68fd5db50c",
|
207
|
+
"block_type": "VARIABLE",
|
208
|
+
"input_variable_id": "ea3f6348-8553-4375-bd27-527df4e4f3c2",
|
209
|
+
"state": "ENABLED",
|
210
|
+
"cache_config": None,
|
211
|
+
}
|
212
|
+
]
|
213
|
+
|
214
|
+
# AND we should have a warning of the invalid reference
|
215
|
+
# TODO: Come up with a proposal for how nodes should propagate warnings
|
216
|
+
# warnings = list(workflow_display.errors)
|
217
|
+
# assert len(warnings) == 1
|
218
|
+
# assert "Missing input variable 'foo' for prompt block 0" in str(warnings[0])
|
219
|
+
|
220
|
+
|
221
|
+
def test_serialize_node__port_groups():
|
222
|
+
# GIVEN a prompt node with ports
|
223
|
+
class MyPromptNode(InlinePromptNode):
|
224
|
+
class Ports(InlinePromptNode.Ports):
|
225
|
+
apple = Port.on_if(LazyReference(lambda: MyPromptNode.Outputs.text).equals("apple"))
|
226
|
+
banana = Port.on_if(LazyReference(lambda: MyPromptNode.Outputs.text).equals("banana"))
|
227
|
+
|
228
|
+
# AND a workflow with the prompt node
|
229
|
+
class MyWorkflow(BaseWorkflow):
|
230
|
+
graph = MyPromptNode
|
231
|
+
|
232
|
+
# WHEN the prompt node is serialized
|
233
|
+
workflow_display = get_workflow_display(workflow_class=MyWorkflow)
|
234
|
+
serialized_workflow: dict = workflow_display.serialize()
|
235
|
+
|
236
|
+
# THEN the node should have the ports serialized
|
237
|
+
my_prompt_node = next(
|
238
|
+
node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["id"] == str(MyPromptNode.__id__)
|
239
|
+
)
|
240
|
+
ports = my_prompt_node["ports"]
|
241
|
+
assert len(ports) == 2
|
242
|
+
assert ports[0]["id"] == "149d97a4-3da3-44a9-95f7-ea7b8d38b877"
|
243
|
+
assert ports[1]["id"] == "71f2d2b3-194f-4492-bc1c-a5ca1f60fb0a"
|
244
|
+
assert ports[0]["name"] == "apple"
|
245
|
+
assert ports[1]["name"] == "banana"
|
246
|
+
|
247
|
+
# AND the legacy source_handle_id should be the default port
|
248
|
+
assert my_prompt_node["data"]["source_handle_id"] == "149d97a4-3da3-44a9-95f7-ea7b8d38b877"
|
@@ -4,7 +4,8 @@ from uuid import UUID, uuid4
|
|
4
4
|
from typing import Type
|
5
5
|
|
6
6
|
from vellum.workflows import BaseWorkflow
|
7
|
-
from vellum.workflows.nodes import SubworkflowDeploymentNode
|
7
|
+
from vellum.workflows.nodes.displayable.subworkflow_deployment_node.node import SubworkflowDeploymentNode
|
8
|
+
from vellum_ee.workflows.display.nodes.vellum.subworkflow_deployment_node import BaseSubworkflowDeploymentNodeDisplay
|
8
9
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
9
10
|
|
10
11
|
|
@@ -13,14 +14,14 @@ def _no_display_class(Node: Type[SubworkflowDeploymentNode]): # type: ignore
|
|
13
14
|
|
14
15
|
|
15
16
|
def _display_class_with_node_input_ids_by_name(Node: Type[SubworkflowDeploymentNode]):
|
16
|
-
class SubworkflowNodeDisplay(
|
17
|
+
class SubworkflowNodeDisplay(BaseSubworkflowDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
|
17
18
|
node_input_ids_by_name = {"foo": UUID("aff4f838-577e-44b9-ac5c-6d8213abbb9c")}
|
18
19
|
|
19
20
|
return SubworkflowNodeDisplay
|
20
21
|
|
21
22
|
|
22
23
|
def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[SubworkflowDeploymentNode]):
|
23
|
-
class SubworkflowNodeDisplay(
|
24
|
+
class SubworkflowNodeDisplay(BaseSubworkflowDeploymentNodeDisplay[Node]): # type: ignore[valid-type]
|
24
25
|
node_input_ids_by_name = {"subworkflow_inputs.foo": UUID("aff4f838-577e-44b9-ac5c-6d8213abbb9c")}
|
25
26
|
|
26
27
|
return SubworkflowNodeDisplay
|
@@ -55,7 +56,7 @@ def mock_fetch_deployment(mocker):
|
|
55
56
|
@pytest.mark.parametrize(
|
56
57
|
["GetDisplayClass", "expected_input_id"],
|
57
58
|
[
|
58
|
-
(_no_display_class, "
|
59
|
+
(_no_display_class, "394132c2-1817-455e-9f3f-b7073eb63a2b"),
|
59
60
|
(_display_class_with_node_input_ids_by_name, "aff4f838-577e-44b9-ac5c-6d8213abbb9c"),
|
60
61
|
(_display_class_with_node_input_ids_by_name_with_inputs_prefix, "aff4f838-577e-44b9-ac5c-6d8213abbb9c"),
|
61
62
|
],
|
@@ -29,7 +29,7 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Tem
|
|
29
29
|
@pytest.mark.parametrize(
|
30
30
|
["GetDisplayClass", "expected_input_id"],
|
31
31
|
[
|
32
|
-
(_no_display_class, "
|
32
|
+
(_no_display_class, "91d982a9-6c41-42ac-aff9-7b623c450a55"),
|
33
33
|
(_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
34
34
|
(_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
35
35
|
],
|
@@ -3,6 +3,8 @@ from typing import Dict
|
|
3
3
|
|
4
4
|
from vellum.workflows.inputs import BaseInputs
|
5
5
|
from vellum.workflows.nodes import BaseNode
|
6
|
+
from vellum.workflows.ports.port import Port
|
7
|
+
from vellum.workflows.references.lazy import LazyReference
|
6
8
|
from vellum.workflows.state import BaseState
|
7
9
|
from vellum.workflows.workflows.base import BaseWorkflow
|
8
10
|
from vellum_ee.workflows.display.vellum import WorkflowInputsVellumDisplayOverrides
|
@@ -285,3 +287,45 @@ def test_vellum_workflow_display__serialize_with_parse_json_expression():
|
|
285
287
|
"operator": "parseJson",
|
286
288
|
},
|
287
289
|
}
|
290
|
+
|
291
|
+
|
292
|
+
def test_serialize__port_with_lazy_reference():
|
293
|
+
# GIVEN a node with a lazy reference in a Port
|
294
|
+
class MyNode(BaseNode):
|
295
|
+
class Ports(BaseNode.Ports):
|
296
|
+
foo = Port.on_if(LazyReference(lambda: MyNode.Outputs.bar))
|
297
|
+
|
298
|
+
class Outputs(BaseNode.Outputs):
|
299
|
+
bar: bool
|
300
|
+
|
301
|
+
# AND a workflow that uses the node
|
302
|
+
class Workflow(BaseWorkflow):
|
303
|
+
graph = MyNode
|
304
|
+
|
305
|
+
# WHEN we serialize the workflow
|
306
|
+
workflow_display = get_workflow_display(workflow_class=Workflow)
|
307
|
+
exec_config = workflow_display.serialize()
|
308
|
+
|
309
|
+
# THEN the lazy reference should be serialized correctly
|
310
|
+
raw_data = exec_config["workflow_raw_data"]
|
311
|
+
assert isinstance(raw_data, dict)
|
312
|
+
|
313
|
+
nodes = raw_data["nodes"]
|
314
|
+
assert isinstance(nodes, list)
|
315
|
+
|
316
|
+
my_node = nodes[1]
|
317
|
+
assert isinstance(my_node, dict)
|
318
|
+
ports = my_node.get("ports")
|
319
|
+
assert isinstance(ports, list)
|
320
|
+
assert ports == [
|
321
|
+
{
|
322
|
+
"id": "6c26bc2b-6469-47c1-b858-d63f0d311ea6",
|
323
|
+
"name": "foo",
|
324
|
+
"type": "IF",
|
325
|
+
"expression": {
|
326
|
+
"type": "NODE_OUTPUT",
|
327
|
+
"node_id": str(MyNode.__id__),
|
328
|
+
"node_output_id": str(MyNode.__output_ids__["bar"]),
|
329
|
+
},
|
330
|
+
}
|
331
|
+
]
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py
CHANGED
@@ -87,7 +87,7 @@ def test_serialize_workflow():
|
|
87
87
|
"type": "ERROR",
|
88
88
|
"inputs": [
|
89
89
|
{
|
90
|
-
"id": "
|
90
|
+
"id": "8e4c8d76-2e02-4d7e-a7bf-d71af392dc49",
|
91
91
|
"key": "error_source_input_id",
|
92
92
|
"value": {
|
93
93
|
"rules": [
|
@@ -101,11 +101,9 @@ def test_serialize_workflow():
|
|
101
101
|
}
|
102
102
|
],
|
103
103
|
"data": {
|
104
|
-
"name": "error-node",
|
105
104
|
"label": "Fail Node",
|
106
105
|
"target_handle_id": "70c19f1c-309c-4a5d-ba65-664c0bb2fedf",
|
107
|
-
"error_source_input_id": "
|
108
|
-
"error_output_id": "None",
|
106
|
+
"error_source_input_id": "8e4c8d76-2e02-4d7e-a7bf-d71af392dc49",
|
109
107
|
},
|
110
108
|
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
111
109
|
"base": {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING, Any
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, cast
|
2
2
|
|
3
3
|
from vellum.client.types.logical_operator import LogicalOperator
|
4
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
@@ -37,7 +37,7 @@ from vellum.workflows.references.output import OutputReference
|
|
37
37
|
from vellum.workflows.references.state_value import StateValueReference
|
38
38
|
from vellum.workflows.references.vellum_secret import VellumSecretReference
|
39
39
|
from vellum.workflows.references.workflow_input import WorkflowInputReference
|
40
|
-
from vellum.workflows.types.core import JsonObject
|
40
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
41
41
|
from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
|
42
42
|
|
43
43
|
if TYPE_CHECKING:
|
@@ -116,7 +116,7 @@ def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplay
|
|
116
116
|
return value._get()
|
117
117
|
|
118
118
|
|
119
|
-
def
|
119
|
+
def _serialize_condition(display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
|
120
120
|
if isinstance(
|
121
121
|
condition,
|
122
122
|
(
|
@@ -235,6 +235,33 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
|
|
235
235
|
"node_id": str(node_class_display.node_id),
|
236
236
|
}
|
237
237
|
|
238
|
+
if isinstance(value, list):
|
239
|
+
serialized_items = [serialize_value(display_context, item) for item in value]
|
240
|
+
if all(isinstance(item, dict) and item["type"] == "CONSTANT_VALUE" for item in serialized_items):
|
241
|
+
constant_values = []
|
242
|
+
for item in serialized_items:
|
243
|
+
item_dict = cast(Dict[str, Any], item)
|
244
|
+
value_inner = item_dict["value"]
|
245
|
+
|
246
|
+
if value_inner["type"] == "JSON" and "items" in value_inner:
|
247
|
+
# Nested JSON list
|
248
|
+
constant_values.append(value_inner["items"])
|
249
|
+
else:
|
250
|
+
constant_values.append(value_inner["value"])
|
251
|
+
|
252
|
+
return {
|
253
|
+
"type": "CONSTANT_VALUE",
|
254
|
+
"value": {
|
255
|
+
"type": "JSON",
|
256
|
+
"items": constant_values,
|
257
|
+
},
|
258
|
+
}
|
259
|
+
else:
|
260
|
+
return {
|
261
|
+
"type": "ARRAY_REFERENCE",
|
262
|
+
"items": cast(JsonArray, serialized_items), # list[JsonObject] -> JsonArray
|
263
|
+
}
|
264
|
+
|
238
265
|
if isinstance(value, dict) and any(isinstance(v, BaseDescriptor) for v in value.values()):
|
239
266
|
raise ValueError("Nested references are not supported.")
|
240
267
|
|
@@ -247,4 +274,4 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
|
|
247
274
|
|
248
275
|
# If it's not any of the references we know about,
|
249
276
|
# then try to serialize it as a nested value
|
250
|
-
return
|
277
|
+
return _serialize_condition(display_context, value)
|
@@ -425,3 +425,165 @@ def test_serialize_workflow__nested_lazy_reference():
|
|
425
425
|
"node_id": str(OuterNode.__id__),
|
426
426
|
"node_output_id": str(OuterNode.__output_ids__["bar"]),
|
427
427
|
}
|
428
|
+
|
429
|
+
|
430
|
+
def test_serialize_workflow__array_values():
|
431
|
+
# GIVEN a node with array and nested array values
|
432
|
+
class MyNode(BaseNode):
|
433
|
+
class Outputs(BaseNode.Outputs):
|
434
|
+
array_value = ["item1", "item2", "item3"]
|
435
|
+
nested_array_value = [["item1", "item2", "item3"], ["item4", "item5", "item6"]]
|
436
|
+
mixed_array_value = [["item1"], "item2", "item3"]
|
437
|
+
|
438
|
+
# AND a workflow that uses these outputs
|
439
|
+
class MyWorkflow(BaseWorkflow):
|
440
|
+
graph = MyNode
|
441
|
+
|
442
|
+
class Outputs(BaseWorkflow.Outputs):
|
443
|
+
array_output = MyNode.Outputs.array_value
|
444
|
+
nested_array_output = MyNode.Outputs.nested_array_value
|
445
|
+
|
446
|
+
# WHEN we serialize it
|
447
|
+
workflow_display = get_workflow_display(workflow_class=MyWorkflow)
|
448
|
+
data = workflow_display.serialize()
|
449
|
+
|
450
|
+
# THEN it should properly serialize the array and dictionary values
|
451
|
+
assert isinstance(data["workflow_raw_data"], dict)
|
452
|
+
assert isinstance(data["workflow_raw_data"]["nodes"], list)
|
453
|
+
raw_nodes = data["workflow_raw_data"]["nodes"]
|
454
|
+
generic_nodes = [node for node in raw_nodes if isinstance(node, dict) and node["type"] == "GENERIC"]
|
455
|
+
assert len(generic_nodes) > 0
|
456
|
+
my_node = generic_nodes[0]
|
457
|
+
|
458
|
+
outputs = my_node["outputs"]
|
459
|
+
assert isinstance(outputs, list)
|
460
|
+
|
461
|
+
array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "array_value"]
|
462
|
+
assert len(array_outputs) > 0
|
463
|
+
array_output = array_outputs[0]
|
464
|
+
|
465
|
+
assert isinstance(array_output, dict)
|
466
|
+
assert "value" in array_output
|
467
|
+
assert array_output["value"] == {
|
468
|
+
"type": "CONSTANT_VALUE",
|
469
|
+
"value": {"type": "JSON", "items": ["item1", "item2", "item3"]},
|
470
|
+
}
|
471
|
+
|
472
|
+
nested_array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "nested_array_value"]
|
473
|
+
assert len(nested_array_outputs) > 0
|
474
|
+
nested_array_output = nested_array_outputs[0]
|
475
|
+
|
476
|
+
assert isinstance(nested_array_output, dict)
|
477
|
+
assert "value" in nested_array_output
|
478
|
+
assert nested_array_output["value"] == {
|
479
|
+
"type": "CONSTANT_VALUE",
|
480
|
+
"value": {"type": "JSON", "items": [["item1", "item2", "item3"], ["item4", "item5", "item6"]]},
|
481
|
+
}
|
482
|
+
|
483
|
+
mixed_array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "mixed_array_value"]
|
484
|
+
assert len(mixed_array_outputs) > 0
|
485
|
+
mixed_array_output = mixed_array_outputs[0]
|
486
|
+
|
487
|
+
assert isinstance(mixed_array_output, dict)
|
488
|
+
assert "value" in mixed_array_output
|
489
|
+
assert mixed_array_output["value"] == {
|
490
|
+
"type": "CONSTANT_VALUE",
|
491
|
+
"value": {"type": "JSON", "items": [["item1"], "item2", "item3"]},
|
492
|
+
}
|
493
|
+
|
494
|
+
|
495
|
+
def test_serialize_workflow__array_reference():
|
496
|
+
# GIVEN a node with array containing non-constant values (node references)
|
497
|
+
class FirstNode(BaseNode):
|
498
|
+
class Outputs(BaseNode.Outputs):
|
499
|
+
value1: str
|
500
|
+
value2: str
|
501
|
+
|
502
|
+
class SecondNode(BaseNode):
|
503
|
+
class Outputs(BaseNode.Outputs):
|
504
|
+
# Array containing a mix of constants and node references
|
505
|
+
mixed_array = ["constant1", FirstNode.Outputs.value1, "constant2", FirstNode.Outputs.value2]
|
506
|
+
mixed_nested_array = [["constant1", FirstNode.Outputs.value1], ["constant2", FirstNode.Outputs.value2]]
|
507
|
+
|
508
|
+
# AND a workflow that uses these outputs
|
509
|
+
class MyWorkflow(BaseWorkflow):
|
510
|
+
graph = FirstNode >> SecondNode
|
511
|
+
|
512
|
+
class Outputs(BaseWorkflow.Outputs):
|
513
|
+
mixed_array_output = SecondNode.Outputs.mixed_array
|
514
|
+
mixed_nested_array_output = SecondNode.Outputs.mixed_nested_array
|
515
|
+
|
516
|
+
# WHEN we serialize it
|
517
|
+
workflow_display = get_workflow_display(workflow_class=MyWorkflow)
|
518
|
+
data = workflow_display.serialize()
|
519
|
+
|
520
|
+
# THEN it should serialize as an ARRAY_REFERENCE
|
521
|
+
assert isinstance(data["workflow_raw_data"], dict)
|
522
|
+
assert isinstance(data["workflow_raw_data"]["nodes"], list)
|
523
|
+
assert len(data["workflow_raw_data"]["nodes"]) == 5
|
524
|
+
second_node = data["workflow_raw_data"]["nodes"][2]
|
525
|
+
assert isinstance(second_node, dict)
|
526
|
+
|
527
|
+
assert "outputs" in second_node
|
528
|
+
assert isinstance(second_node["outputs"], list)
|
529
|
+
outputs = second_node["outputs"]
|
530
|
+
|
531
|
+
mixed_array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "mixed_array"]
|
532
|
+
assert len(mixed_array_outputs) > 0
|
533
|
+
mixed_array_output = mixed_array_outputs[0]
|
534
|
+
|
535
|
+
assert isinstance(mixed_array_output, dict)
|
536
|
+
assert "value" in mixed_array_output
|
537
|
+
assert mixed_array_output["value"] == {
|
538
|
+
"type": "ARRAY_REFERENCE",
|
539
|
+
"items": [
|
540
|
+
{"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "constant1"}},
|
541
|
+
{
|
542
|
+
"type": "NODE_OUTPUT",
|
543
|
+
"node_id": "702a08b5-61e8-4a7a-a83d-77f49e39c5be",
|
544
|
+
"node_output_id": "419b6afa-fab5-493a-ba1e-4606f4641616",
|
545
|
+
},
|
546
|
+
{"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "constant2"}},
|
547
|
+
{
|
548
|
+
"type": "NODE_OUTPUT",
|
549
|
+
"node_id": "702a08b5-61e8-4a7a-a83d-77f49e39c5be",
|
550
|
+
"node_output_id": "d1cacc41-478d-49a3-a6b3-1ba2d51291e2",
|
551
|
+
},
|
552
|
+
],
|
553
|
+
}
|
554
|
+
|
555
|
+
mixed_nested_array_outputs = [
|
556
|
+
val for val in outputs if isinstance(val, dict) and val["name"] == "mixed_nested_array"
|
557
|
+
]
|
558
|
+
assert len(mixed_nested_array_outputs) > 0
|
559
|
+
mixed_nested_array_output = mixed_nested_array_outputs[0]
|
560
|
+
|
561
|
+
assert isinstance(mixed_nested_array_output, dict)
|
562
|
+
assert "value" in mixed_nested_array_output
|
563
|
+
assert mixed_nested_array_output["value"] == {
|
564
|
+
"type": "ARRAY_REFERENCE",
|
565
|
+
"items": [
|
566
|
+
{
|
567
|
+
"type": "ARRAY_REFERENCE",
|
568
|
+
"items": [
|
569
|
+
{"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "constant1"}},
|
570
|
+
{
|
571
|
+
"type": "NODE_OUTPUT",
|
572
|
+
"node_id": "702a08b5-61e8-4a7a-a83d-77f49e39c5be",
|
573
|
+
"node_output_id": "419b6afa-fab5-493a-ba1e-4606f4641616",
|
574
|
+
},
|
575
|
+
],
|
576
|
+
},
|
577
|
+
{
|
578
|
+
"type": "ARRAY_REFERENCE",
|
579
|
+
"items": [
|
580
|
+
{"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "constant2"}},
|
581
|
+
{
|
582
|
+
"type": "NODE_OUTPUT",
|
583
|
+
"node_id": "702a08b5-61e8-4a7a-a83d-77f49e39c5be",
|
584
|
+
"node_output_id": "d1cacc41-478d-49a3-a6b3-1ba2d51291e2",
|
585
|
+
},
|
586
|
+
],
|
587
|
+
},
|
588
|
+
],
|
589
|
+
}
|
File without changes
|
File without changes
|
File without changes
|