vellum-ai 1.11.2__py3-none-any.whl → 1.13.5__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.
Potentially problematic release.
This version of vellum-ai might be problematic. Click here for more details.
- vellum/__init__.py +18 -0
- vellum/client/README.md +1 -1
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/core/force_multipart.py +4 -2
- vellum/client/core/http_response.py +1 -1
- vellum/client/core/pydantic_utilities.py +7 -4
- vellum/client/errors/too_many_requests_error.py +1 -2
- vellum/client/reference.md +677 -76
- vellum/client/resources/container_images/client.py +299 -0
- vellum/client/resources/container_images/raw_client.py +286 -0
- vellum/client/resources/documents/client.py +20 -10
- vellum/client/resources/documents/raw_client.py +20 -10
- vellum/client/resources/events/raw_client.py +4 -4
- vellum/client/resources/integration_auth_configs/client.py +2 -0
- vellum/client/resources/integration_auth_configs/raw_client.py +2 -0
- vellum/client/resources/integration_providers/client.py +28 -2
- vellum/client/resources/integration_providers/raw_client.py +24 -0
- vellum/client/resources/integrations/client.py +52 -4
- vellum/client/resources/integrations/raw_client.py +61 -0
- vellum/client/resources/workflow_deployments/client.py +156 -0
- vellum/client/resources/workflow_deployments/raw_client.py +334 -0
- vellum/client/resources/workflows/client.py +212 -8
- vellum/client/resources/workflows/raw_client.py +343 -6
- vellum/client/types/__init__.py +18 -0
- vellum/client/types/api_actor_type_enum.py +1 -1
- vellum/client/types/check_workflow_execution_status_error.py +21 -0
- vellum/client/types/check_workflow_execution_status_response.py +29 -0
- vellum/client/types/code_execution_package_request.py +21 -0
- vellum/client/types/composio_execute_tool_request.py +5 -0
- vellum/client/types/composio_tool_definition.py +1 -0
- vellum/client/types/container_image_build_config.py +1 -0
- vellum/client/types/container_image_container_image_tag.py +1 -0
- vellum/client/types/dataset_row_push_request.py +3 -0
- vellum/client/types/document_document_to_document_index.py +1 -0
- vellum/client/types/integration_name.py +24 -0
- vellum/client/types/node_execution_fulfilled_body.py +1 -0
- vellum/client/types/node_execution_log_body.py +24 -0
- vellum/client/types/node_execution_log_event.py +47 -0
- vellum/client/types/prompt_deployment_release_prompt_deployment.py +1 -0
- vellum/client/types/runner_config_request.py +24 -0
- vellum/client/types/severity_enum.py +5 -0
- vellum/client/types/slim_composio_tool_definition.py +1 -0
- vellum/client/types/slim_document_document_to_document_index.py +2 -0
- vellum/client/types/type_checker_enum.py +5 -0
- vellum/client/types/vellum_audio.py +5 -1
- vellum/client/types/vellum_audio_request.py +5 -1
- vellum/client/types/vellum_document.py +5 -1
- vellum/client/types/vellum_document_request.py +5 -1
- vellum/client/types/vellum_image.py +5 -1
- vellum/client/types/vellum_image_request.py +5 -1
- vellum/client/types/vellum_node_execution_event.py +2 -0
- vellum/client/types/vellum_variable.py +5 -0
- vellum/client/types/vellum_variable_extensions.py +1 -0
- vellum/client/types/vellum_variable_type.py +1 -0
- vellum/client/types/vellum_video.py +5 -1
- vellum/client/types/vellum_video_request.py +5 -1
- vellum/client/types/workflow_deployment_release_workflow_deployment.py +1 -0
- vellum/client/types/workflow_event.py +2 -0
- vellum/client/types/workflow_execution_fulfilled_body.py +1 -0
- vellum/client/types/workflow_result_event_output_data_array.py +1 -1
- vellum/client/types/workflow_result_event_output_data_chat_history.py +1 -1
- vellum/client/types/workflow_result_event_output_data_error.py +1 -1
- vellum/client/types/workflow_result_event_output_data_function_call.py +1 -1
- vellum/client/types/workflow_result_event_output_data_json.py +1 -1
- vellum/client/types/workflow_result_event_output_data_number.py +1 -1
- vellum/client/types/workflow_result_event_output_data_search_results.py +1 -1
- vellum/client/types/workflow_result_event_output_data_string.py +1 -1
- vellum/client/types/workflow_sandbox_execute_node_response.py +8 -0
- vellum/plugins/vellum_mypy.py +37 -2
- vellum/types/check_workflow_execution_status_error.py +3 -0
- vellum/types/check_workflow_execution_status_response.py +3 -0
- vellum/types/code_execution_package_request.py +3 -0
- vellum/types/node_execution_log_body.py +3 -0
- vellum/types/node_execution_log_event.py +3 -0
- vellum/types/runner_config_request.py +3 -0
- vellum/types/severity_enum.py +3 -0
- vellum/types/type_checker_enum.py +3 -0
- vellum/types/workflow_sandbox_execute_node_response.py +3 -0
- vellum/utils/files/mixin.py +26 -0
- vellum/utils/files/tests/test_mixin.py +62 -0
- vellum/utils/tests/test_vellum_client.py +95 -0
- vellum/utils/uuid.py +19 -2
- vellum/utils/vellum_client.py +10 -3
- vellum/workflows/__init__.py +7 -1
- vellum/workflows/descriptors/base.py +86 -0
- vellum/workflows/descriptors/tests/test_utils.py +9 -0
- vellum/workflows/errors/tests/__init__.py +0 -0
- vellum/workflows/errors/tests/test_types.py +52 -0
- vellum/workflows/errors/types.py +1 -0
- vellum/workflows/events/node.py +24 -0
- vellum/workflows/events/tests/test_event.py +123 -0
- vellum/workflows/events/types.py +2 -1
- vellum/workflows/events/workflow.py +28 -2
- vellum/workflows/expressions/add.py +3 -0
- vellum/workflows/expressions/tests/test_add.py +24 -0
- vellum/workflows/graph/graph.py +26 -5
- vellum/workflows/graph/tests/test_graph.py +228 -1
- vellum/workflows/inputs/base.py +22 -6
- vellum/workflows/inputs/dataset_row.py +121 -16
- vellum/workflows/inputs/tests/test_inputs.py +3 -3
- vellum/workflows/integrations/tests/test_vellum_integration_service.py +84 -0
- vellum/workflows/integrations/vellum_integration_service.py +12 -1
- vellum/workflows/loaders/base.py +2 -0
- vellum/workflows/nodes/bases/base.py +37 -16
- vellum/workflows/nodes/bases/tests/test_base_node.py +104 -1
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +1 -1
- vellum/workflows/nodes/core/map_node/node.py +7 -5
- vellum/workflows/nodes/core/map_node/tests/test_node.py +33 -0
- vellum/workflows/nodes/core/retry_node/node.py +1 -0
- vellum/workflows/nodes/core/try_node/node.py +1 -0
- vellum/workflows/nodes/displayable/api_node/node.py +3 -2
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +38 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +18 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +109 -2
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +13 -2
- vellum/workflows/nodes/displayable/code_execution_node/node.py +9 -15
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +65 -24
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -0
- vellum/workflows/nodes/displayable/final_output_node/node.py +24 -69
- vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +53 -3
- vellum/workflows/nodes/displayable/note_node/node.py +4 -1
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +16 -5
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +47 -0
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +74 -34
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +204 -8
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +92 -71
- vellum/workflows/nodes/mocks.py +47 -213
- vellum/workflows/nodes/tests/test_mocks.py +0 -177
- vellum/workflows/nodes/utils.py +23 -8
- vellum/workflows/outputs/base.py +36 -3
- vellum/workflows/references/environment_variable.py +1 -11
- vellum/workflows/references/lazy.py +8 -0
- vellum/workflows/references/state_value.py +24 -1
- vellum/workflows/references/tests/test_lazy.py +58 -0
- vellum/workflows/references/trigger.py +8 -3
- vellum/workflows/references/workflow_input.py +8 -0
- vellum/workflows/resolvers/resolver.py +13 -3
- vellum/workflows/resolvers/tests/test_resolver.py +31 -0
- vellum/workflows/runner/runner.py +159 -14
- vellum/workflows/runner/tests/__init__.py +0 -0
- vellum/workflows/runner/tests/test_runner.py +170 -0
- vellum/workflows/sandbox.py +7 -8
- vellum/workflows/state/base.py +89 -30
- vellum/workflows/state/context.py +74 -3
- vellum/workflows/state/tests/test_state.py +269 -1
- vellum/workflows/tests/test_dataset_row.py +8 -7
- vellum/workflows/tests/test_sandbox.py +97 -8
- vellum/workflows/triggers/__init__.py +2 -1
- vellum/workflows/triggers/base.py +160 -28
- vellum/workflows/triggers/chat_message.py +141 -0
- vellum/workflows/triggers/integration.py +12 -0
- vellum/workflows/triggers/manual.py +3 -1
- vellum/workflows/triggers/schedule.py +3 -1
- vellum/workflows/triggers/tests/test_chat_message.py +257 -0
- vellum/workflows/types/core.py +18 -0
- vellum/workflows/types/definition.py +6 -13
- vellum/workflows/types/generics.py +12 -0
- vellum/workflows/types/tests/test_utils.py +12 -0
- vellum/workflows/types/utils.py +32 -2
- vellum/workflows/types/workflow_metadata.py +124 -0
- vellum/workflows/utils/functions.py +152 -16
- vellum/workflows/utils/pydantic_schema.py +19 -1
- vellum/workflows/utils/tests/test_functions.py +123 -8
- vellum/workflows/utils/tests/test_validate.py +79 -0
- vellum/workflows/utils/tests/test_vellum_variables.py +62 -2
- vellum/workflows/utils/uuids.py +90 -0
- vellum/workflows/utils/validate.py +108 -0
- vellum/workflows/utils/vellum_variables.py +96 -16
- vellum/workflows/workflows/base.py +177 -35
- vellum/workflows/workflows/tests/test_base_workflow.py +51 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/METADATA +6 -1
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/RECORD +274 -227
- vellum_cli/__init__.py +21 -0
- vellum_cli/config.py +16 -2
- vellum_cli/pull.py +2 -0
- vellum_cli/push.py +23 -10
- vellum_cli/tests/conftest.py +8 -13
- vellum_cli/tests/test_image_push.py +4 -11
- vellum_cli/tests/test_pull.py +83 -68
- vellum_cli/tests/test_push.py +251 -2
- vellum_ee/assets/node-definitions.json +225 -12
- vellum_ee/scripts/generate_node_definitions.py +15 -3
- vellum_ee/workflows/display/base.py +4 -3
- vellum_ee/workflows/display/nodes/base_node_display.py +44 -11
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +93 -0
- vellum_ee/workflows/display/nodes/types.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/__init__.py +0 -2
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +5 -2
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +10 -2
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +17 -14
- vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/note_node.py +18 -3
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +37 -14
- vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +62 -2
- vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py +136 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py +44 -7
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +5 -13
- vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +27 -17
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +145 -22
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +107 -2
- vellum_ee/workflows/display/nodes/vellum/utils.py +54 -12
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +13 -16
- vellum_ee/workflows/display/tests/test_json_schema_validation.py +190 -0
- vellum_ee/workflows/display/tests/test_mocks.py +912 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +14 -2
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +109 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +3 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +187 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +34 -325
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +42 -393
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +13 -315
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +2 -122
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +24 -115
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +4 -93
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +7 -80
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +9 -101
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -308
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +62 -324
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -82
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +4 -142
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +1 -61
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_set_state_node_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +205 -134
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +34 -146
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +2 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +8 -6
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +137 -266
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_tool_wrapper_serialization.py +84 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +55 -16
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +15 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_tool_wrapper_serialization.py +71 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_vellum_integration_serialization.py +119 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +0 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_dict_reference_serialization.py +22 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_trigger_serialization.py +412 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_code_tool_node_reference_error.py +106 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +9 -41
- vellum_ee/workflows/display/tests/workflow_serialization/test_duplicate_trigger_name_validation.py +208 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_not_referenced_by_workflow_outputs.py +45 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_infinite_loop_validation.py +66 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_int_input_serialization.py +40 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_serialization.py +8 -14
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_validation.py +173 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_with_entrypoint_node_id.py +16 -13
- vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +5 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +12 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_multi_trigger_same_node_serialization.py +111 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_no_triggers_no_entrypoint_validation.py +64 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_partial_workflow_meta_display_override.py +55 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_dataset_mocks_serialization.py +268 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_invalid_pdf_data_url.py +49 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_validation_errors.py +112 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_scheduled_trigger_serialization.py +25 -16
- vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_in_unused_graphs_serialization.py +53 -0
- vellum_ee/workflows/display/utils/exceptions.py +34 -0
- vellum_ee/workflows/display/utils/expressions.py +463 -52
- vellum_ee/workflows/display/utils/metadata.py +98 -33
- vellum_ee/workflows/display/utils/tests/test_metadata.py +31 -0
- vellum_ee/workflows/display/utils/triggers.py +153 -0
- vellum_ee/workflows/display/utils/vellum.py +59 -5
- vellum_ee/workflows/display/workflows/base_workflow_display.py +656 -254
- vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +26 -0
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +77 -29
- vellum_ee/workflows/server/namespaces.py +18 -0
- vellum_ee/workflows/tests/test_display_meta.py +2 -0
- vellum_ee/workflows/tests/test_serialize_module.py +174 -7
- vellum_ee/workflows/tests/test_server.py +0 -3
- vellum_ee/workflows/display/nodes/vellum/function_node.py +0 -14
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/LICENSE +0 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/WHEEL +0 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import re
|
|
1
3
|
import types
|
|
2
4
|
from typing import TYPE_CHECKING, Generic, Optional, Type, TypeVar
|
|
3
5
|
|
|
4
6
|
from vellum.client import Vellum as VellumClient
|
|
7
|
+
from vellum.workflows import BaseWorkflow
|
|
5
8
|
from vellum.workflows.types.generics import WorkflowType
|
|
6
9
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
7
10
|
from vellum_ee.workflows.display.utils.registry import get_from_workflow_display_registry
|
|
@@ -10,6 +13,28 @@ if TYPE_CHECKING:
|
|
|
10
13
|
from vellum_ee.workflows.display.workflows import BaseWorkflowDisplay
|
|
11
14
|
|
|
12
15
|
|
|
16
|
+
def _ensure_display_module_imported(workflow_class: Type[WorkflowType]) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Best-effort import of the workflow's display module to ensure any custom
|
|
19
|
+
WorkflowDisplay subclass is registered before we look it up in the registry.
|
|
20
|
+
|
|
21
|
+
This allows workflows to work without a display/__init__.py file that
|
|
22
|
+
re-exports from .workflow and .nodes.
|
|
23
|
+
"""
|
|
24
|
+
module_name = workflow_class.__module__
|
|
25
|
+
|
|
26
|
+
if module_name == BaseWorkflow.__module__:
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
if module_name.endswith(".workflow"):
|
|
30
|
+
root = re.sub(r"\.workflow$", "", module_name)
|
|
31
|
+
display_workflow_module = f"{root}.display.workflow"
|
|
32
|
+
try:
|
|
33
|
+
importlib.import_module(display_workflow_module)
|
|
34
|
+
except ImportError:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
13
38
|
def _get_workflow_display_class(*, workflow_class: Type[WorkflowType]) -> Type["BaseWorkflowDisplay"]:
|
|
14
39
|
workflow_display_class = get_from_workflow_display_registry(workflow_class)
|
|
15
40
|
if workflow_display_class:
|
|
@@ -42,6 +67,7 @@ def get_workflow_display(
|
|
|
42
67
|
root_workflow_class: Optional[Type[WorkflowType]] = None,
|
|
43
68
|
base_display_class: Optional[Type["BaseWorkflowDisplay"]] = None,
|
|
44
69
|
) -> "BaseWorkflowDisplay":
|
|
70
|
+
_ensure_display_module_imported(workflow_class)
|
|
45
71
|
return _get_workflow_display_class(workflow_class=workflow_class)(
|
|
46
72
|
parent_display_context=parent_display_context,
|
|
47
73
|
client=client,
|
|
@@ -11,6 +11,7 @@ from vellum.workflows.nodes.core.try_node.node import TryNode
|
|
|
11
11
|
from vellum.workflows.nodes.displayable.final_output_node.node import FinalOutputNode
|
|
12
12
|
from vellum.workflows.references.lazy import LazyReference
|
|
13
13
|
from vellum.workflows.state.base import BaseState
|
|
14
|
+
from vellum.workflows.triggers.integration import IntegrationTrigger
|
|
14
15
|
from vellum.workflows.types.core import JsonObject
|
|
15
16
|
from vellum.workflows.workflows.base import BaseWorkflow
|
|
16
17
|
from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
|
|
@@ -18,6 +19,7 @@ from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
|
|
18
19
|
from vellum_ee.workflows.display.nodes.vellum.retry_node import BaseRetryNodeDisplay
|
|
19
20
|
from vellum_ee.workflows.display.nodes.vellum.try_node import BaseTryNodeDisplay
|
|
20
21
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
22
|
+
from vellum_ee.workflows.display.utils.exceptions import UserFacingException
|
|
21
23
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
|
22
24
|
|
|
23
25
|
|
|
@@ -41,7 +43,7 @@ def test_serialize_workflow__node_referenced_in_workflow_outputs_not_in_graph():
|
|
|
41
43
|
workflow_display = get_workflow_display(workflow_class=Workflow)
|
|
42
44
|
|
|
43
45
|
# THEN it should raise an error
|
|
44
|
-
with pytest.raises(
|
|
46
|
+
with pytest.raises(UserFacingException) as exc_info:
|
|
45
47
|
workflow_display.serialize()
|
|
46
48
|
|
|
47
49
|
# AND the error message should be user friendly
|
|
@@ -61,17 +63,31 @@ def test_serialize_workflow__workflow_outputs_reference_non_node_outputs():
|
|
|
61
63
|
|
|
62
64
|
# WHEN we serialize it
|
|
63
65
|
workflow_display = get_workflow_display(workflow_class=Workflow)
|
|
66
|
+
serialized_workflow = workflow_display.serialize()
|
|
67
|
+
|
|
68
|
+
# THEN it should successfully serialize the workflow output reference to a constant
|
|
69
|
+
assert isinstance(serialized_workflow, dict)
|
|
70
|
+
output_variables = serialized_workflow["output_variables"]
|
|
71
|
+
assert isinstance(output_variables, list)
|
|
72
|
+
assert output_variables == [{"id": "2b32416b-ccfc-4231-a3a6-d08e76327815", "key": "final", "type": "STRING"}]
|
|
73
|
+
|
|
74
|
+
# AND the output value should be a constant value
|
|
75
|
+
workflow_raw_data = serialized_workflow["workflow_raw_data"]
|
|
76
|
+
assert isinstance(workflow_raw_data, dict)
|
|
77
|
+
output_values = workflow_raw_data["output_values"]
|
|
78
|
+
assert isinstance(output_values, list)
|
|
79
|
+
assert output_values == [
|
|
80
|
+
{
|
|
81
|
+
"output_variable_id": "2b32416b-ccfc-4231-a3a6-d08e76327815",
|
|
82
|
+
"value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "bar"}},
|
|
83
|
+
}
|
|
84
|
+
]
|
|
64
85
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
assert (
|
|
71
|
-
str(exc_info.value)
|
|
72
|
-
== """Failed to serialize output 'final': Reference to outputs \
|
|
73
|
-
'test_serialize_workflow__workflow_outputs_reference_non_node_outputs.<locals>.FirstWorkflow.Outputs' is invalid."""
|
|
74
|
-
)
|
|
86
|
+
first_output_variable = output_variables[0]
|
|
87
|
+
assert isinstance(first_output_variable, dict)
|
|
88
|
+
first_output_value = output_values[0]
|
|
89
|
+
assert isinstance(first_output_value, dict)
|
|
90
|
+
assert first_output_variable["id"] == first_output_value["output_variable_id"]
|
|
75
91
|
|
|
76
92
|
|
|
77
93
|
def test_serialize_workflow__node_display_class_not_registered():
|
|
@@ -288,6 +304,38 @@ def test_get_event_display_context__workflow_output_display_with_none():
|
|
|
288
304
|
assert display_context.workflow_outputs.keys() == {"foo", "bar"}
|
|
289
305
|
|
|
290
306
|
|
|
307
|
+
def test_get_event_display_context__trigger_attributes_included():
|
|
308
|
+
"""Trigger attributes should be included in workflow_inputs for display in executions list."""
|
|
309
|
+
|
|
310
|
+
# GIVEN a workflow with an integration trigger that has attributes
|
|
311
|
+
class SlackTrigger(IntegrationTrigger):
|
|
312
|
+
message: str
|
|
313
|
+
channel: str
|
|
314
|
+
|
|
315
|
+
class Config:
|
|
316
|
+
provider = "COMPOSIO"
|
|
317
|
+
integration_name = "SLACK"
|
|
318
|
+
slug = "slack_new_message"
|
|
319
|
+
|
|
320
|
+
class ProcessNode(BaseNode):
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
class MyWorkflow(BaseWorkflow):
|
|
324
|
+
graph = SlackTrigger >> ProcessNode
|
|
325
|
+
|
|
326
|
+
# WHEN we gather the event display context
|
|
327
|
+
display_context = get_workflow_display(workflow_class=MyWorkflow).get_event_display_context()
|
|
328
|
+
|
|
329
|
+
# THEN the trigger attributes should be included in workflow_inputs
|
|
330
|
+
assert "message" in display_context.workflow_inputs
|
|
331
|
+
assert "channel" in display_context.workflow_inputs
|
|
332
|
+
|
|
333
|
+
# AND the IDs should match the trigger attribute IDs
|
|
334
|
+
trigger_attr_refs = SlackTrigger.attribute_references()
|
|
335
|
+
assert display_context.workflow_inputs["message"] == trigger_attr_refs["message"].id
|
|
336
|
+
assert display_context.workflow_inputs["channel"] == trigger_attr_refs["channel"].id
|
|
337
|
+
|
|
338
|
+
|
|
291
339
|
def test_serialize_workflow__inherited_node_display_class_not_registered():
|
|
292
340
|
# GIVEN a node meant to be used as a base
|
|
293
341
|
class StartNode(BaseNode):
|
|
@@ -528,8 +576,14 @@ def test_serialize_workflow__array_reference():
|
|
|
528
576
|
# THEN it should serialize as an ARRAY_REFERENCE
|
|
529
577
|
assert isinstance(data["workflow_raw_data"], dict)
|
|
530
578
|
assert isinstance(data["workflow_raw_data"]["nodes"], list)
|
|
531
|
-
|
|
532
|
-
|
|
579
|
+
second_node = next(
|
|
580
|
+
node
|
|
581
|
+
for node in data["workflow_raw_data"]["nodes"]
|
|
582
|
+
if isinstance(node, dict)
|
|
583
|
+
and "definition" in node
|
|
584
|
+
and isinstance(node["definition"], dict)
|
|
585
|
+
and node["definition"]["name"] == "SecondNode"
|
|
586
|
+
)
|
|
533
587
|
assert isinstance(second_node, dict)
|
|
534
588
|
|
|
535
589
|
assert "outputs" in second_node
|
|
@@ -828,17 +882,6 @@ def test_serialize_workflow__empty_rules_indexerror():
|
|
|
828
882
|
assert len(output_variables) == 1
|
|
829
883
|
assert output_variables[0]["key"] == "problematic_output"
|
|
830
884
|
|
|
831
|
-
# AND the workflow raw data should contain nodes including terminal node
|
|
832
|
-
workflow_raw_data = result["workflow_raw_data"]
|
|
833
|
-
assert "nodes" in workflow_raw_data
|
|
834
|
-
nodes = workflow_raw_data["nodes"]
|
|
835
|
-
|
|
836
|
-
assert len(nodes) >= 3
|
|
837
|
-
|
|
838
|
-
terminal_nodes = [node for node in nodes if node.get("type") == "TERMINAL"]
|
|
839
|
-
assert len(terminal_nodes) == 1
|
|
840
|
-
assert terminal_nodes[0]["data"]["name"] == "problematic_output"
|
|
841
|
-
|
|
842
885
|
|
|
843
886
|
def test_serialize_workflow__input_variables():
|
|
844
887
|
# GIVEN a workflow with inputs
|
|
@@ -870,6 +913,7 @@ def test_serialize_workflow__input_variables():
|
|
|
870
913
|
"default": {"type": "STRING", "value": ""},
|
|
871
914
|
"required": False,
|
|
872
915
|
"extensions": {"color": None},
|
|
916
|
+
"schema": {"type": "string"},
|
|
873
917
|
}
|
|
874
918
|
|
|
875
919
|
input_1 = next(var for var in input_variables if isinstance(var, dict) and var["key"] == "input_1")
|
|
@@ -880,6 +924,7 @@ def test_serialize_workflow__input_variables():
|
|
|
880
924
|
"default": None,
|
|
881
925
|
"required": True,
|
|
882
926
|
"extensions": {"color": None},
|
|
927
|
+
"schema": {"type": "string"},
|
|
883
928
|
}
|
|
884
929
|
|
|
885
930
|
input_2 = next(var for var in input_variables if isinstance(var, dict) and var["key"] == "input_2")
|
|
@@ -890,6 +935,7 @@ def test_serialize_workflow__input_variables():
|
|
|
890
935
|
"default": None,
|
|
891
936
|
"required": False,
|
|
892
937
|
"extensions": {"color": None},
|
|
938
|
+
"schema": {"anyOf": [{"type": "string"}, {"type": "null"}]},
|
|
893
939
|
}
|
|
894
940
|
|
|
895
941
|
input_3 = next(var for var in input_variables if isinstance(var, dict) and var["key"] == "input_3")
|
|
@@ -900,6 +946,7 @@ def test_serialize_workflow__input_variables():
|
|
|
900
946
|
"default": {"type": "NUMBER", "value": 1.0},
|
|
901
947
|
"required": False,
|
|
902
948
|
"extensions": {"color": None},
|
|
949
|
+
"schema": {"type": "integer"},
|
|
903
950
|
}
|
|
904
951
|
|
|
905
952
|
input_4 = next(var for var in input_variables if isinstance(var, dict) and var["key"] == "input_4")
|
|
@@ -910,6 +957,7 @@ def test_serialize_workflow__input_variables():
|
|
|
910
957
|
"default": {"type": "NUMBER", "value": 2.0},
|
|
911
958
|
"required": False,
|
|
912
959
|
"extensions": {"color": None},
|
|
960
|
+
"schema": {"anyOf": [{"type": "integer"}, {"type": "null"}]},
|
|
913
961
|
}
|
|
914
962
|
|
|
915
963
|
|
|
@@ -937,7 +985,7 @@ def test_serialize_workflow__state_variables():
|
|
|
937
985
|
|
|
938
986
|
empty_string = next(var for var in state_variables if isinstance(var, dict) and var["key"] == "empty_string")
|
|
939
987
|
assert empty_string == {
|
|
940
|
-
"id": "
|
|
988
|
+
"id": "c69e2507-f610-4a6f-84cc-a5bc2aa48551",
|
|
941
989
|
"key": "empty_string",
|
|
942
990
|
"type": "STRING",
|
|
943
991
|
"default": {"type": "STRING", "value": ""},
|
|
@@ -947,7 +995,7 @@ def test_serialize_workflow__state_variables():
|
|
|
947
995
|
|
|
948
996
|
state_1 = next(var for var in state_variables if isinstance(var, dict) and var["key"] == "state_1")
|
|
949
997
|
assert state_1 == {
|
|
950
|
-
"id": "
|
|
998
|
+
"id": "151113d2-9bbf-428d-a1c1-0a9cf4fdedf3",
|
|
951
999
|
"key": "state_1",
|
|
952
1000
|
"type": "STRING",
|
|
953
1001
|
"default": {"type": "STRING", "value": "hello"},
|
|
@@ -957,7 +1005,7 @@ def test_serialize_workflow__state_variables():
|
|
|
957
1005
|
|
|
958
1006
|
state_2 = next(var for var in state_variables if isinstance(var, dict) and var["key"] == "state_2")
|
|
959
1007
|
assert state_2 == {
|
|
960
|
-
"id": "
|
|
1008
|
+
"id": "9a8d7a55-8bd2-497d-820c-dee665144a48",
|
|
961
1009
|
"key": "state_2",
|
|
962
1010
|
"type": "STRING",
|
|
963
1011
|
"default": None,
|
|
@@ -967,7 +1015,7 @@ def test_serialize_workflow__state_variables():
|
|
|
967
1015
|
|
|
968
1016
|
state_3 = next(var for var in state_variables if isinstance(var, dict) and var["key"] == "state_3")
|
|
969
1017
|
assert state_3 == {
|
|
970
|
-
"id": "
|
|
1018
|
+
"id": "ffde4327-12c4-4c55-82d6-3ab88f0b1037",
|
|
971
1019
|
"key": "state_3",
|
|
972
1020
|
"type": "NUMBER",
|
|
973
1021
|
"default": {"type": "NUMBER", "value": 1.0},
|
|
@@ -977,7 +1025,7 @@ def test_serialize_workflow__state_variables():
|
|
|
977
1025
|
|
|
978
1026
|
state_4 = next(var for var in state_variables if isinstance(var, dict) and var["key"] == "state_4")
|
|
979
1027
|
assert state_4 == {
|
|
980
|
-
"id": "
|
|
1028
|
+
"id": "2467c1e6-b6aa-42d7-b079-84c8a650fbca",
|
|
981
1029
|
"key": "state_4",
|
|
982
1030
|
"type": "NUMBER",
|
|
983
1031
|
"default": {"type": "NUMBER", "value": 2.0},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Namespace utilities for workflow server."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import string
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_random_namespace() -> str:
|
|
8
|
+
"""
|
|
9
|
+
Generate a random namespace for virtual file loading.
|
|
10
|
+
|
|
11
|
+
This generates a workflow_tmp_* namespace like vembda uses for loading
|
|
12
|
+
workflows dynamically.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
A random namespace string in the format "workflow_tmp_{random_suffix}"
|
|
16
|
+
"""
|
|
17
|
+
random_suffix = "".join(random.choices(string.ascii_letters + string.digits, k=16))
|
|
18
|
+
return f"workflow_tmp_{random_suffix}"
|
|
@@ -16,6 +16,8 @@ def files() -> dict[str, str]:
|
|
|
16
16
|
|
|
17
17
|
for root, _, filenames in os.walk(base_directory):
|
|
18
18
|
for filename in filenames:
|
|
19
|
+
if filename.endswith(".pyc"):
|
|
20
|
+
continue
|
|
19
21
|
file_path = os.path.join(root, filename)
|
|
20
22
|
# Key will be the relative path inside `local_files`
|
|
21
23
|
relative_path = str(os.path.relpath(file_path, start=base_directory))
|
|
@@ -11,6 +11,8 @@ from pytest_mock import MockerFixture
|
|
|
11
11
|
from vellum.workflows.exceptions import WorkflowInitializationException
|
|
12
12
|
from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
|
|
13
13
|
|
|
14
|
+
from tests.workflows.test_node_output_mock_when_conditions.workflow import ProcessNode
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
@pytest.fixture
|
|
16
18
|
def temp_module_path():
|
|
@@ -66,7 +68,7 @@ def test_serialize_module_with_actual_dataset():
|
|
|
66
68
|
assert result.dataset[1]["inputs"]["message"] == "DatasetRow Test"
|
|
67
69
|
|
|
68
70
|
|
|
69
|
-
def
|
|
71
|
+
def test_serialize_module_with_actual_dataset_with_scheduled_trigger(metadata_trigger_factory):
|
|
70
72
|
"""Test that serialize_module correctly serializes dataset with trigger"""
|
|
71
73
|
module_path = "tests.workflows.test_dataset_with_trigger_serialization"
|
|
72
74
|
|
|
@@ -84,6 +86,12 @@ def test_serialize_module_with_actual_dataset_with_trigger(metadata_trigger_fact
|
|
|
84
86
|
assert result.dataset[0]["label"] == "Scenario 1"
|
|
85
87
|
assert result.dataset[0]["workflow_trigger_id"] == str(metadata_trigger_id)
|
|
86
88
|
|
|
89
|
+
inputs = result.dataset[0]["inputs"]
|
|
90
|
+
assert len(inputs) == 2
|
|
91
|
+
|
|
92
|
+
assert "current_run_at" in inputs
|
|
93
|
+
assert "next_run_at" in inputs
|
|
94
|
+
|
|
87
95
|
|
|
88
96
|
def test_serialize_module_happy_path():
|
|
89
97
|
"""Test that serialize_module works with a valid module path."""
|
|
@@ -100,34 +108,82 @@ def test_serialize_module_happy_path():
|
|
|
100
108
|
assert "output_variables" in result.exec_config
|
|
101
109
|
|
|
102
110
|
|
|
111
|
+
def test_serialize_module__includes_runner_config():
|
|
112
|
+
"""
|
|
113
|
+
Tests that serialize_module includes runner_config in exec_config.
|
|
114
|
+
"""
|
|
115
|
+
# GIVEN a valid module path without metadata.json runner_config
|
|
116
|
+
module_path = "tests.workflows.trivial"
|
|
117
|
+
|
|
118
|
+
# WHEN we serialize the module
|
|
119
|
+
result = BaseWorkflowDisplay.serialize_module(module_path)
|
|
120
|
+
|
|
121
|
+
# THEN the exec_config should contain runner_config
|
|
122
|
+
assert "runner_config" in result.exec_config
|
|
123
|
+
|
|
124
|
+
# AND runner_config should be an empty dict when no metadata.json runner_config exists
|
|
125
|
+
runner_config = result.exec_config["runner_config"]
|
|
126
|
+
assert isinstance(runner_config, dict)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_serialize_module__runner_config_from_metadata():
|
|
130
|
+
"""
|
|
131
|
+
Tests that serialize_module reads runner_config from metadata.json.
|
|
132
|
+
"""
|
|
133
|
+
# GIVEN a module path with metadata.json containing runner_config
|
|
134
|
+
module_path = "ee.vellum_ee.workflows.tests.local_workflow"
|
|
135
|
+
|
|
136
|
+
# WHEN we serialize the module
|
|
137
|
+
result = BaseWorkflowDisplay.serialize_module(module_path)
|
|
138
|
+
|
|
139
|
+
# THEN the exec_config should contain runner_config
|
|
140
|
+
assert "runner_config" in result.exec_config
|
|
141
|
+
|
|
142
|
+
# AND runner_config should be a dict (loaded from metadata.json)
|
|
143
|
+
runner_config = result.exec_config["runner_config"]
|
|
144
|
+
assert isinstance(runner_config, dict)
|
|
145
|
+
|
|
146
|
+
|
|
103
147
|
def test_serialize_module_includes_additional_files():
|
|
104
|
-
"""
|
|
148
|
+
"""
|
|
149
|
+
Tests that serialize_module includes only Python files from the module directory.
|
|
150
|
+
|
|
151
|
+
Non-Python files (like .txt) should be excluded from additional_files.
|
|
152
|
+
"""
|
|
153
|
+
# GIVEN a module path with additional files including both .py and non-.py files
|
|
105
154
|
module_path = "tests.workflows.module_with_additional_files"
|
|
106
155
|
|
|
156
|
+
# WHEN we serialize the module
|
|
107
157
|
result = BaseWorkflowDisplay.serialize_module(module_path)
|
|
108
158
|
|
|
159
|
+
# THEN the result should have the expected structure
|
|
109
160
|
assert hasattr(result, "exec_config")
|
|
110
161
|
assert hasattr(result, "errors")
|
|
111
162
|
assert isinstance(result.exec_config, dict)
|
|
112
163
|
assert isinstance(result.errors, list)
|
|
113
164
|
|
|
165
|
+
# AND module_data should contain additional_files
|
|
114
166
|
assert "module_data" in result.exec_config
|
|
115
167
|
assert "additional_files" in result.exec_config["module_data"]
|
|
116
168
|
|
|
117
169
|
additional_files = result.exec_config["module_data"]["additional_files"]
|
|
118
170
|
assert isinstance(additional_files, dict)
|
|
119
171
|
|
|
172
|
+
# AND Python helper files should be included
|
|
120
173
|
assert "helper.py" in additional_files
|
|
121
|
-
assert "data.txt" in additional_files
|
|
122
174
|
assert "utils/constants.py" in additional_files
|
|
175
|
+
assert "utils/__init__.py" in additional_files
|
|
176
|
+
|
|
177
|
+
# AND non-Python files should NOT be included
|
|
178
|
+
assert "data.txt" not in additional_files
|
|
123
179
|
|
|
180
|
+
# AND serialized workflow files should NOT be included
|
|
124
181
|
assert "workflow.py" not in additional_files
|
|
125
182
|
assert "__init__.py" not in additional_files
|
|
126
|
-
assert "utils/__init__.py" in additional_files
|
|
127
183
|
assert "nodes/test_node.py" not in additional_files
|
|
128
184
|
|
|
185
|
+
# AND the Python file contents should be correct
|
|
129
186
|
assert "def helper_function():" in additional_files["helper.py"]
|
|
130
|
-
assert "sample data file" in additional_files["data.txt"]
|
|
131
187
|
assert "CONSTANT_VALUE" in additional_files["utils/constants.py"]
|
|
132
188
|
|
|
133
189
|
|
|
@@ -154,7 +210,11 @@ def test_serialize_module_with_pydantic_array():
|
|
|
154
210
|
items_input = input_variables[0]
|
|
155
211
|
assert items_input["key"] == "items"
|
|
156
212
|
assert items_input["type"] == "JSON"
|
|
157
|
-
#
|
|
213
|
+
# The schema field now includes the OpenAPI spec for the input type
|
|
214
|
+
assert items_input["schema"] == {
|
|
215
|
+
"type": "array",
|
|
216
|
+
"items": {"$ref": "#/$defs/tests.workflows.pydantic_array_serialization.workflow.CustomItem"},
|
|
217
|
+
}
|
|
158
218
|
|
|
159
219
|
assert result.dataset is not None
|
|
160
220
|
assert isinstance(result.dataset, list)
|
|
@@ -204,7 +264,8 @@ def test_serialize_module_with_dataset_row_id_from_metadata():
|
|
|
204
264
|
|
|
205
265
|
assert result.dataset[2]["label"] == "Scenario 3 without ID"
|
|
206
266
|
assert result.dataset[2]["inputs"]["message"] == "No ID"
|
|
207
|
-
|
|
267
|
+
# AND the third row should have a deterministic hash-based ID since it has no explicit ID
|
|
268
|
+
assert result.dataset[2]["id"] == "cc40aa9b-179a-43fc-8fda-d40ea8a8e300"
|
|
208
269
|
|
|
209
270
|
|
|
210
271
|
def test_serialize_module_with_base_inputs_and_metadata():
|
|
@@ -231,6 +292,112 @@ def test_serialize_module_with_base_inputs_and_metadata():
|
|
|
231
292
|
assert result.dataset[1]["id"] == "base-inputs-id-2"
|
|
232
293
|
|
|
233
294
|
|
|
295
|
+
def test_serialize_module_with_node_output_mock_when_conditions():
|
|
296
|
+
"""
|
|
297
|
+
Tests that serialize_module correctly serializes node output mocks with when conditions.
|
|
298
|
+
|
|
299
|
+
Verifies that when conditions involving workflow inputs and node execution counters
|
|
300
|
+
are properly serialized in the dataset.
|
|
301
|
+
"""
|
|
302
|
+
module_path = "tests.workflows.test_node_output_mock_when_conditions"
|
|
303
|
+
|
|
304
|
+
# WHEN we serialize the module
|
|
305
|
+
result = BaseWorkflowDisplay.serialize_module(module_path)
|
|
306
|
+
|
|
307
|
+
assert hasattr(result, "dataset")
|
|
308
|
+
assert result.dataset is not None
|
|
309
|
+
assert isinstance(result.dataset, list)
|
|
310
|
+
assert len(result.dataset) == 2
|
|
311
|
+
|
|
312
|
+
first_scenario = result.dataset[0]
|
|
313
|
+
assert first_scenario["label"] == "Scenario 1"
|
|
314
|
+
assert first_scenario["inputs"]["threshold"] == 5
|
|
315
|
+
|
|
316
|
+
assert "mocks" in first_scenario
|
|
317
|
+
assert isinstance(first_scenario["mocks"], list)
|
|
318
|
+
assert len(first_scenario["mocks"]) == 2
|
|
319
|
+
|
|
320
|
+
first_mock = first_scenario["mocks"][0]
|
|
321
|
+
workflow_input_id = first_mock["when_condition"]["lhs"]["lhs"]["input_variable_id"]
|
|
322
|
+
node_id = str(ProcessNode.__id__)
|
|
323
|
+
|
|
324
|
+
assert first_mock == {
|
|
325
|
+
"type": "NODE_EXECUTION",
|
|
326
|
+
"node_id": node_id,
|
|
327
|
+
"when_condition": {
|
|
328
|
+
"type": "BINARY_EXPRESSION",
|
|
329
|
+
"lhs": {
|
|
330
|
+
"type": "BINARY_EXPRESSION",
|
|
331
|
+
"lhs": {"type": "WORKFLOW_INPUT", "input_variable_id": workflow_input_id},
|
|
332
|
+
"operator": "=",
|
|
333
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5.0}},
|
|
334
|
+
},
|
|
335
|
+
"operator": "and",
|
|
336
|
+
"rhs": {
|
|
337
|
+
"type": "BINARY_EXPRESSION",
|
|
338
|
+
"lhs": {"type": "EXECUTION_COUNTER", "node_id": node_id},
|
|
339
|
+
"operator": "=",
|
|
340
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 0.0}},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
"then_outputs": {"result": "first_execution_threshold_5"},
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
second_mock = first_scenario["mocks"][1]
|
|
347
|
+
assert second_mock == {
|
|
348
|
+
"type": "NODE_EXECUTION",
|
|
349
|
+
"node_id": node_id,
|
|
350
|
+
"when_condition": {
|
|
351
|
+
"type": "BINARY_EXPRESSION",
|
|
352
|
+
"lhs": {
|
|
353
|
+
"type": "BINARY_EXPRESSION",
|
|
354
|
+
"lhs": {"type": "WORKFLOW_INPUT", "input_variable_id": workflow_input_id},
|
|
355
|
+
"operator": "=",
|
|
356
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5.0}},
|
|
357
|
+
},
|
|
358
|
+
"operator": "and",
|
|
359
|
+
"rhs": {
|
|
360
|
+
"type": "BINARY_EXPRESSION",
|
|
361
|
+
"lhs": {"type": "EXECUTION_COUNTER", "node_id": node_id},
|
|
362
|
+
"operator": "=",
|
|
363
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 1.0}},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
"then_outputs": {"result": "second_execution_threshold_5"},
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
second_scenario = result.dataset[1]
|
|
370
|
+
assert second_scenario["label"] == "Scenario 2"
|
|
371
|
+
assert second_scenario["inputs"]["threshold"] == 10
|
|
372
|
+
|
|
373
|
+
assert "mocks" in second_scenario
|
|
374
|
+
assert isinstance(second_scenario["mocks"], list)
|
|
375
|
+
assert len(second_scenario["mocks"]) == 1
|
|
376
|
+
|
|
377
|
+
third_mock = second_scenario["mocks"][0]
|
|
378
|
+
assert third_mock == {
|
|
379
|
+
"type": "NODE_EXECUTION",
|
|
380
|
+
"node_id": node_id,
|
|
381
|
+
"when_condition": {
|
|
382
|
+
"type": "BINARY_EXPRESSION",
|
|
383
|
+
"lhs": {
|
|
384
|
+
"type": "BINARY_EXPRESSION",
|
|
385
|
+
"lhs": {"type": "WORKFLOW_INPUT", "input_variable_id": workflow_input_id},
|
|
386
|
+
"operator": "=",
|
|
387
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 10.0}},
|
|
388
|
+
},
|
|
389
|
+
"operator": "and",
|
|
390
|
+
"rhs": {
|
|
391
|
+
"type": "BINARY_EXPRESSION",
|
|
392
|
+
"lhs": {"type": "EXECUTION_COUNTER", "node_id": node_id},
|
|
393
|
+
"operator": "=",
|
|
394
|
+
"rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 0.0}},
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
"then_outputs": {"result": "first_execution_threshold_10"},
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
|
|
234
401
|
def test_serialize_module__with_invalid_nested_set_graph(temp_module_path):
|
|
235
402
|
"""
|
|
236
403
|
Tests that serialize_module raises a clear user-facing exception for workflows with nested sets in graph attribute.
|
|
@@ -945,9 +945,6 @@ from vellum.workflows.nodes import BaseNode
|
|
|
945
945
|
class StartNode(BaseNode):
|
|
946
946
|
class Outputs(BaseNode.Outputs):
|
|
947
947
|
result: str = "test output"
|
|
948
|
-
""",
|
|
949
|
-
"display/nodes/__init__.py": """\
|
|
950
|
-
from .start_node import StartNodeDisplay
|
|
951
948
|
""",
|
|
952
949
|
"display/nodes/start_node.py": """\
|
|
953
950
|
from uuid import UUID
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
from typing import Generic, TypeVar
|
|
2
|
-
|
|
3
|
-
from vellum.workflows.nodes.displayable.tool_calling_node.utils import FunctionNode
|
|
4
|
-
from vellum_ee.workflows.display.editor.types import NodeDisplayData
|
|
5
|
-
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
|
6
|
-
|
|
7
|
-
_FunctionNodeType = TypeVar("_FunctionNodeType", bound=FunctionNode)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class FunctionNodeDisplay(BaseNodeDisplay[_FunctionNodeType], Generic[_FunctionNodeType]):
|
|
11
|
-
display_data = NodeDisplayData(
|
|
12
|
-
icon="vellum:icon:rectangle-code",
|
|
13
|
-
color="purple",
|
|
14
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|