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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
from uuid import UUID
|
|
6
|
-
from typing import Dict, Optional, Type
|
|
6
|
+
from typing import Any, Dict, Optional, Tuple, Type
|
|
7
7
|
|
|
8
8
|
from vellum.workflows.nodes.bases.base import BaseNode
|
|
9
9
|
from vellum.workflows.utils.files import virtual_open
|
|
@@ -33,30 +33,69 @@ def find_workflow_root_with_metadata(module_path: str) -> Optional[str]:
|
|
|
33
33
|
return None
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def _load_workflow_metadata(module_path: str) -> Tuple[Optional[str], Optional[Dict]]:
|
|
37
37
|
"""
|
|
38
|
-
Load
|
|
38
|
+
Load the full metadata.json content for a given module.
|
|
39
39
|
|
|
40
|
-
This function searches up the module hierarchy for metadata.json and
|
|
41
|
-
the edges_to_id_mapping.
|
|
40
|
+
This function searches up the module hierarchy for metadata.json and loads its content.
|
|
42
41
|
|
|
43
42
|
Args:
|
|
44
43
|
module_path: The module path to search from (e.g., "workflows.my_workflow")
|
|
45
44
|
|
|
46
45
|
Returns:
|
|
47
|
-
|
|
46
|
+
A tuple of (workflow_root, metadata_dict). Returns (None, None) if not found.
|
|
48
47
|
"""
|
|
49
48
|
try:
|
|
50
|
-
|
|
51
|
-
if not
|
|
52
|
-
return
|
|
53
|
-
file_path = os.path.join(
|
|
49
|
+
workflow_root = find_workflow_root_with_metadata(module_path)
|
|
50
|
+
if not workflow_root:
|
|
51
|
+
return None, None
|
|
52
|
+
file_path = os.path.join(workflow_root.replace(".", os.path.sep), "metadata.json")
|
|
54
53
|
with virtual_open(file_path) as f:
|
|
55
54
|
data = json.load(f)
|
|
56
|
-
|
|
57
|
-
return edges_map if isinstance(edges_map, dict) else {}
|
|
55
|
+
return workflow_root, data
|
|
58
56
|
except Exception:
|
|
59
|
-
return
|
|
57
|
+
return None, None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _load_edges_mapping(module_path: str) -> Tuple[Optional[str], Dict[str, str]]:
|
|
61
|
+
"""
|
|
62
|
+
Load edge path to ID mapping from metadata.json for a given module.
|
|
63
|
+
|
|
64
|
+
This function searches up the module hierarchy for metadata.json and extracts
|
|
65
|
+
the edges_to_id_mapping.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
module_path: The module path to search from (e.g., "workflows.my_workflow")
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dictionary mapping edge keys to their UUID strings
|
|
72
|
+
"""
|
|
73
|
+
workflow_root, data = _load_workflow_metadata(module_path)
|
|
74
|
+
if data is None:
|
|
75
|
+
return None, {}
|
|
76
|
+
edges_map = data.get("edges_to_id_mapping")
|
|
77
|
+
if isinstance(edges_map, dict):
|
|
78
|
+
return workflow_root, edges_map
|
|
79
|
+
return workflow_root, {}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_edges_to_id_mapping(module_path: str) -> Dict[str, str]:
|
|
83
|
+
_, mapping = _load_edges_mapping(module_path)
|
|
84
|
+
return mapping
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _build_metadata_class_path(module: str, class_name: str, workflow_root: Optional[str]) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Format a class path so that it matches the metadata.json key structure.
|
|
90
|
+
"""
|
|
91
|
+
if workflow_root:
|
|
92
|
+
if module == workflow_root:
|
|
93
|
+
return f".{class_name}"
|
|
94
|
+
prefix = f"{workflow_root}."
|
|
95
|
+
if module.startswith(prefix):
|
|
96
|
+
relative = module[len(prefix) :]
|
|
97
|
+
return f".{relative}.{class_name}" if relative else f".{class_name}"
|
|
98
|
+
return f"{module}.{class_name}"
|
|
60
99
|
|
|
61
100
|
|
|
62
101
|
def get_trigger_edge_id(trigger_cls: Type, target_node: Type[BaseNode], module_path: str) -> Optional[str]:
|
|
@@ -71,9 +110,12 @@ def get_trigger_edge_id(trigger_cls: Type, target_node: Type[BaseNode], module_p
|
|
|
71
110
|
Returns:
|
|
72
111
|
The stable edge ID if found in metadata.json, None otherwise
|
|
73
112
|
"""
|
|
74
|
-
edges_mapping =
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
workflow_root, edges_mapping = _load_edges_mapping(module_path)
|
|
114
|
+
if not edges_mapping:
|
|
115
|
+
return None
|
|
116
|
+
trigger_path = _build_metadata_class_path(trigger_cls.__module__, trigger_cls.__name__, workflow_root)
|
|
117
|
+
target_base = _build_metadata_class_path(target_node.__module__, target_node.__name__, workflow_root)
|
|
118
|
+
target_path = f"{target_base}.Trigger"
|
|
77
119
|
edge_key = f"{trigger_path}|{target_path}"
|
|
78
120
|
return edges_mapping.get(edge_key)
|
|
79
121
|
|
|
@@ -89,9 +131,12 @@ def get_entrypoint_edge_id(target_node: Type[BaseNode], module_path: str) -> Opt
|
|
|
89
131
|
Returns:
|
|
90
132
|
The stable edge ID if found in metadata.json, None otherwise
|
|
91
133
|
"""
|
|
92
|
-
edges_mapping =
|
|
134
|
+
workflow_root, edges_mapping = _load_edges_mapping(module_path)
|
|
135
|
+
if not edges_mapping:
|
|
136
|
+
return None
|
|
93
137
|
manual_path = "vellum.workflows.triggers.manual.Manual"
|
|
94
|
-
|
|
138
|
+
target_base = _build_metadata_class_path(target_node.__module__, target_node.__name__, workflow_root)
|
|
139
|
+
target_path = f"{target_base}.Trigger"
|
|
95
140
|
edge_key = f"{manual_path}|{target_path}"
|
|
96
141
|
return edges_mapping.get(edge_key)
|
|
97
142
|
|
|
@@ -111,9 +156,13 @@ def get_regular_edge_id(
|
|
|
111
156
|
Returns:
|
|
112
157
|
The stable edge ID if found in metadata.json, None otherwise
|
|
113
158
|
"""
|
|
114
|
-
edges_mapping =
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
workflow_root, edges_mapping = _load_edges_mapping(module_path)
|
|
160
|
+
if not edges_mapping:
|
|
161
|
+
return None
|
|
162
|
+
source_base = _build_metadata_class_path(source_node_cls.__module__, source_node_cls.__name__, workflow_root)
|
|
163
|
+
source_path = f"{source_base}.Ports.{str(source_handle_id)}"
|
|
164
|
+
target_base = _build_metadata_class_path(target_node.__module__, target_node.__name__, workflow_root)
|
|
165
|
+
target_path = f"{target_base}.Trigger"
|
|
117
166
|
edge_key = f"{source_path}|{target_path}"
|
|
118
167
|
return edges_mapping.get(edge_key)
|
|
119
168
|
|
|
@@ -131,16 +180,32 @@ def load_dataset_row_index_to_id_mapping(module_path: str) -> Dict[int, str]:
|
|
|
131
180
|
Returns:
|
|
132
181
|
Dictionary mapping dataset row indices (as integers) to their ID strings
|
|
133
182
|
"""
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
_, data = _load_workflow_metadata(module_path)
|
|
184
|
+
if data is None:
|
|
185
|
+
return {}
|
|
186
|
+
mapping = data.get("dataset_row_index_to_id_mapping")
|
|
187
|
+
if isinstance(mapping, dict):
|
|
188
|
+
return {int(k): v for k, v in mapping.items()}
|
|
189
|
+
return {}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def load_runner_config(module_path: str) -> Dict[str, Any]:
|
|
193
|
+
"""
|
|
194
|
+
Load runner_config from metadata.json for a given module.
|
|
195
|
+
|
|
196
|
+
This function searches up the module hierarchy for metadata.json and extracts
|
|
197
|
+
the runner_config.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
module_path: The module path to search from (e.g., "workflows.my_workflow")
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
The runner_config dictionary if found in metadata.json, empty dict otherwise
|
|
204
|
+
"""
|
|
205
|
+
_, data = _load_workflow_metadata(module_path)
|
|
206
|
+
if data is None:
|
|
146
207
|
return {}
|
|
208
|
+
runner_config = data.get("runner_config")
|
|
209
|
+
if isinstance(runner_config, dict):
|
|
210
|
+
return runner_config
|
|
211
|
+
return {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from vellum_ee.workflows.display.utils.metadata import _build_metadata_class_path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def build_metadata_class_path_test_cases():
|
|
7
|
+
return [
|
|
8
|
+
# (module, class_name, workflow_root, expected)
|
|
9
|
+
("my_workflow.nodes.my_node", "MyNode", "my_workflow", ".nodes.my_node.MyNode"),
|
|
10
|
+
("my_workflow", "MyWorkflow", "my_workflow", ".MyWorkflow"),
|
|
11
|
+
("my_workflow.triggers.scheduled", "ScheduleTrigger", "my_workflow", ".triggers.scheduled.ScheduleTrigger"),
|
|
12
|
+
("external.module", "ExternalClass", "my_workflow", "external.module.ExternalClass"),
|
|
13
|
+
("vellum.workflows.triggers.manual", "Manual", "my_workflow", "vellum.workflows.triggers.manual.Manual"),
|
|
14
|
+
("my_workflow.nodes.my_node", "MyNode", None, "my_workflow.nodes.my_node.MyNode"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize(
|
|
19
|
+
"module, class_name, workflow_root, expected",
|
|
20
|
+
build_metadata_class_path_test_cases(),
|
|
21
|
+
)
|
|
22
|
+
def test_build_metadata_class_path(module, class_name, workflow_root, expected):
|
|
23
|
+
"""
|
|
24
|
+
Tests that _build_metadata_class_path correctly formats class paths relative to workflow root.
|
|
25
|
+
"""
|
|
26
|
+
# GIVEN a module path, class name, and workflow root
|
|
27
|
+
# WHEN we build the metadata class path
|
|
28
|
+
result = _build_metadata_class_path(module, class_name, workflow_root)
|
|
29
|
+
|
|
30
|
+
# THEN we should get the expected formatted path
|
|
31
|
+
assert result == expected
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Type, cast
|
|
2
|
+
|
|
3
|
+
from vellum.workflows.triggers.base import BaseTrigger
|
|
4
|
+
from vellum.workflows.triggers.chat_message import ChatMessageTrigger
|
|
5
|
+
from vellum.workflows.triggers.integration import IntegrationTrigger
|
|
6
|
+
from vellum.workflows.triggers.manual import ManualTrigger
|
|
7
|
+
from vellum.workflows.triggers.schedule import ScheduleTrigger
|
|
8
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
|
9
|
+
from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
|
|
10
|
+
from vellum_ee.workflows.display.base import WorkflowTriggerType
|
|
11
|
+
from vellum_ee.workflows.display.utils.vellum import compile_descriptor_annotation
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_trigger_type(trigger_class: Type[BaseTrigger]) -> WorkflowTriggerType:
|
|
15
|
+
"""Get the WorkflowTriggerType for a trigger class."""
|
|
16
|
+
if issubclass(trigger_class, ManualTrigger):
|
|
17
|
+
return WorkflowTriggerType.MANUAL
|
|
18
|
+
elif issubclass(trigger_class, IntegrationTrigger):
|
|
19
|
+
return WorkflowTriggerType.INTEGRATION
|
|
20
|
+
elif issubclass(trigger_class, ScheduleTrigger):
|
|
21
|
+
return WorkflowTriggerType.SCHEDULED
|
|
22
|
+
elif issubclass(trigger_class, ChatMessageTrigger):
|
|
23
|
+
return WorkflowTriggerType.CHAT_MESSAGE
|
|
24
|
+
else:
|
|
25
|
+
raise ValueError(
|
|
26
|
+
f"Unknown trigger type: {trigger_class.__name__}. "
|
|
27
|
+
f"Please add it to the trigger type mapping in get_trigger_type_mapping()."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def serialize_trigger_attributes(trigger_class: Type[BaseTrigger]) -> JsonArray:
|
|
32
|
+
"""Serialize trigger attributes from attribute_references as VellumVariables."""
|
|
33
|
+
attribute_references = trigger_class.attribute_references().values()
|
|
34
|
+
|
|
35
|
+
def get_attribute_type(reference: Any) -> str:
|
|
36
|
+
# We can remove this type ignore with some mypy plugin changes
|
|
37
|
+
message_name = ChatMessageTrigger.message.name # type: ignore[union-attr]
|
|
38
|
+
# For ChatMessageTrigger.message, always return ARRAY to maintain backwards compatibility
|
|
39
|
+
if issubclass(trigger_class, ChatMessageTrigger) and reference.name == message_name:
|
|
40
|
+
return "ARRAY"
|
|
41
|
+
try:
|
|
42
|
+
return primitive_type_to_vellum_variable_type(reference)
|
|
43
|
+
except ValueError:
|
|
44
|
+
return "JSON"
|
|
45
|
+
|
|
46
|
+
trigger_attributes: JsonArray = cast(
|
|
47
|
+
JsonArray,
|
|
48
|
+
[
|
|
49
|
+
cast(
|
|
50
|
+
JsonObject,
|
|
51
|
+
{
|
|
52
|
+
"id": str(reference.id),
|
|
53
|
+
"key": reference.name,
|
|
54
|
+
"type": get_attribute_type(reference),
|
|
55
|
+
"required": type(None) not in reference.types,
|
|
56
|
+
"default": {
|
|
57
|
+
"type": get_attribute_type(reference),
|
|
58
|
+
"value": None,
|
|
59
|
+
},
|
|
60
|
+
"extensions": None,
|
|
61
|
+
"schema": compile_descriptor_annotation(reference),
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
for reference in sorted(attribute_references, key=lambda ref: ref.name)
|
|
65
|
+
],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return trigger_attributes
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def serialize_trigger_display_data(trigger_class: Type[BaseTrigger], trigger_type: WorkflowTriggerType) -> JsonObject:
|
|
72
|
+
"""Serialize display_data from trigger's Display class."""
|
|
73
|
+
display_class = trigger_class.Display
|
|
74
|
+
display_data: JsonObject = {}
|
|
75
|
+
|
|
76
|
+
if hasattr(display_class, "label") and display_class.label is not None:
|
|
77
|
+
display_data["label"] = display_class.label
|
|
78
|
+
|
|
79
|
+
if (
|
|
80
|
+
hasattr(display_class, "x")
|
|
81
|
+
and display_class.x is not None
|
|
82
|
+
and hasattr(display_class, "y")
|
|
83
|
+
and display_class.y is not None
|
|
84
|
+
):
|
|
85
|
+
display_data["position"] = {
|
|
86
|
+
"x": display_class.x,
|
|
87
|
+
"y": display_class.y,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if hasattr(display_class, "z_index") and display_class.z_index is not None:
|
|
91
|
+
display_data["z_index"] = display_class.z_index
|
|
92
|
+
|
|
93
|
+
if hasattr(display_class, "icon") and display_class.icon is not None:
|
|
94
|
+
display_data["icon"] = display_class.icon
|
|
95
|
+
|
|
96
|
+
if hasattr(display_class, "color") and display_class.color is not None:
|
|
97
|
+
display_data["color"] = display_class.color
|
|
98
|
+
|
|
99
|
+
if hasattr(display_class, "comment") and display_class.comment is not None:
|
|
100
|
+
display_data["comment"] = {
|
|
101
|
+
"value": display_class.comment.value,
|
|
102
|
+
"expanded": display_class.comment.expanded,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return display_data
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def serialize_trigger_definition(trigger_class: Type[BaseTrigger]) -> Dict[str, Any]:
|
|
109
|
+
"""
|
|
110
|
+
Serialize a trigger class definition for use in node-definitions.json.
|
|
111
|
+
|
|
112
|
+
This produces a simplified trigger definition that includes:
|
|
113
|
+
- type: The WorkflowTriggerType enum value
|
|
114
|
+
- name: The trigger class name
|
|
115
|
+
- module: The module path as an array
|
|
116
|
+
- attributes: The trigger's attribute references
|
|
117
|
+
- display_data: Optional display metadata (label, icon, color)
|
|
118
|
+
"""
|
|
119
|
+
trigger_type = get_trigger_type(trigger_class)
|
|
120
|
+
|
|
121
|
+
definition: Dict[str, Any] = {
|
|
122
|
+
"type": trigger_type.value,
|
|
123
|
+
"name": trigger_class.__name__,
|
|
124
|
+
"module": trigger_class.__module__.split("."),
|
|
125
|
+
"attributes": serialize_trigger_attributes(trigger_class),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
display_data = serialize_trigger_display_data(trigger_class, trigger_type)
|
|
129
|
+
|
|
130
|
+
# Don't include display_data for manual triggers (consistent with workflow serialization)
|
|
131
|
+
if display_data and trigger_type != WorkflowTriggerType.MANUAL:
|
|
132
|
+
definition["display_data"] = display_data
|
|
133
|
+
|
|
134
|
+
return definition
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_all_trigger_classes() -> List[Type[BaseTrigger]]:
|
|
138
|
+
"""
|
|
139
|
+
Get all trigger classes dynamically from the triggers module.
|
|
140
|
+
|
|
141
|
+
This mirrors the approach used by get_all_displayable_node_classes for nodes.
|
|
142
|
+
"""
|
|
143
|
+
import vellum.workflows.triggers as triggers_module
|
|
144
|
+
|
|
145
|
+
trigger_classes = []
|
|
146
|
+
for class_name in triggers_module.__all__:
|
|
147
|
+
trigger_class = getattr(triggers_module, class_name)
|
|
148
|
+
# Skip BaseTrigger itself - we only want concrete trigger types
|
|
149
|
+
if trigger_class is BaseTrigger:
|
|
150
|
+
continue
|
|
151
|
+
if isinstance(trigger_class, type) and issubclass(trigger_class, BaseTrigger):
|
|
152
|
+
trigger_classes.append(trigger_class)
|
|
153
|
+
return trigger_classes
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
2
3
|
|
|
3
4
|
from vellum.client.core.api_error import ApiError
|
|
@@ -14,14 +15,23 @@ from vellum.workflows.references.environment_variable import EnvironmentVariable
|
|
|
14
15
|
from vellum.workflows.references.execution_count import ExecutionCountReference
|
|
15
16
|
from vellum.workflows.references.lazy import LazyReference
|
|
16
17
|
from vellum.workflows.references.node import NodeReference
|
|
18
|
+
from vellum.workflows.references.state_value import StateValueReference
|
|
19
|
+
from vellum.workflows.references.trigger import TriggerAttributeReference
|
|
17
20
|
from vellum.workflows.references.vellum_secret import VellumSecretReference
|
|
21
|
+
from vellum.workflows.utils.functions import compile_annotation
|
|
18
22
|
from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
|
|
19
|
-
from vellum_ee.workflows.display.utils.exceptions import
|
|
23
|
+
from vellum_ee.workflows.display.utils.exceptions import (
|
|
24
|
+
InvalidInputReferenceError,
|
|
25
|
+
InvalidOutputReferenceError,
|
|
26
|
+
UnsupportedSerializationException,
|
|
27
|
+
)
|
|
20
28
|
from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
|
|
21
29
|
|
|
22
30
|
if TYPE_CHECKING:
|
|
23
31
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
24
32
|
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
25
35
|
|
|
26
36
|
class ConstantValuePointer(UniversalBaseModel):
|
|
27
37
|
type: Literal["CONSTANT_VALUE"] = "CONSTANT_VALUE"
|
|
@@ -78,6 +88,25 @@ class EnvironmentVariablePointer(UniversalBaseModel):
|
|
|
78
88
|
data: EnvironmentVariableData
|
|
79
89
|
|
|
80
90
|
|
|
91
|
+
class WorkflowStateData(UniversalBaseModel):
|
|
92
|
+
state_variable_id: str
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class WorkflowStatePointer(UniversalBaseModel):
|
|
96
|
+
type: Literal["WORKFLOW_STATE"] = "WORKFLOW_STATE"
|
|
97
|
+
data: WorkflowStateData
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TriggerAttributeData(UniversalBaseModel):
|
|
101
|
+
trigger_id: str
|
|
102
|
+
attribute_id: str
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class TriggerAttributePointer(UniversalBaseModel):
|
|
106
|
+
type: Literal["TRIGGER_ATTRIBUTE"] = "TRIGGER_ATTRIBUTE"
|
|
107
|
+
data: TriggerAttributeData
|
|
108
|
+
|
|
109
|
+
|
|
81
110
|
NodeInputValuePointerRule = Union[
|
|
82
111
|
NodeOutputPointer,
|
|
83
112
|
InputVariablePointer,
|
|
@@ -85,6 +114,8 @@ NodeInputValuePointerRule = Union[
|
|
|
85
114
|
WorkspaceSecretPointer,
|
|
86
115
|
ExecutionCounterPointer,
|
|
87
116
|
EnvironmentVariablePointer,
|
|
117
|
+
WorkflowStatePointer,
|
|
118
|
+
TriggerAttributePointer,
|
|
88
119
|
]
|
|
89
120
|
|
|
90
121
|
|
|
@@ -108,6 +139,13 @@ def infer_vellum_variable_type(value: Any) -> VellumVariableType:
|
|
|
108
139
|
return inferred_type
|
|
109
140
|
|
|
110
141
|
|
|
142
|
+
def compile_descriptor_annotation(descriptor: BaseDescriptor) -> dict:
|
|
143
|
+
"""Compile a BaseDescriptor's type annotation to a JSON schema."""
|
|
144
|
+
if not descriptor.types:
|
|
145
|
+
return {}
|
|
146
|
+
return compile_annotation(descriptor.normalized_type, {})
|
|
147
|
+
|
|
148
|
+
|
|
111
149
|
def create_node_input_value_pointer_rule(
|
|
112
150
|
value: Any, display_context: "WorkflowDisplayContext"
|
|
113
151
|
) -> NodeInputValuePointerRule:
|
|
@@ -132,7 +170,12 @@ def create_node_input_value_pointer_rule(
|
|
|
132
170
|
data=NodeOutputData(node_id=str(upstream_node_display.node_id), output_id=str(output_display.id)),
|
|
133
171
|
)
|
|
134
172
|
if isinstance(value, LazyReference):
|
|
135
|
-
|
|
173
|
+
try:
|
|
174
|
+
child_descriptor = get_child_descriptor(value, display_context)
|
|
175
|
+
except InvalidOutputReferenceError as e:
|
|
176
|
+
logger.warning("Failed to parse lazy reference '%s', skipping serialization", value.name)
|
|
177
|
+
display_context.add_validation_error(e)
|
|
178
|
+
raise UnsupportedSerializationException(f"Failed to parse lazy reference: {value.name}")
|
|
136
179
|
return create_node_input_value_pointer_rule(child_descriptor, display_context)
|
|
137
180
|
if isinstance(value, WorkflowInputReference):
|
|
138
181
|
if value not in display_context.global_workflow_input_displays:
|
|
@@ -164,14 +207,25 @@ def create_node_input_value_pointer_rule(
|
|
|
164
207
|
data=ExecutionCounterData(node_id=str(node_class_display.node_id)),
|
|
165
208
|
)
|
|
166
209
|
if isinstance(value, EnvironmentVariableReference):
|
|
167
|
-
if value.serialize_as_constant:
|
|
168
|
-
vellum_value = primitive_to_vellum_value(value.name)
|
|
169
|
-
return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_value)
|
|
170
210
|
return EnvironmentVariablePointer(
|
|
171
211
|
data=EnvironmentVariableData(
|
|
172
212
|
environment_variable=value.name,
|
|
173
213
|
),
|
|
174
214
|
)
|
|
215
|
+
if isinstance(value, StateValueReference):
|
|
216
|
+
state_value_display = display_context.global_state_value_displays[value]
|
|
217
|
+
return WorkflowStatePointer(
|
|
218
|
+
data=WorkflowStateData(
|
|
219
|
+
state_variable_id=str(state_value_display.id),
|
|
220
|
+
),
|
|
221
|
+
)
|
|
222
|
+
if isinstance(value, TriggerAttributeReference):
|
|
223
|
+
return TriggerAttributePointer(
|
|
224
|
+
data=TriggerAttributeData(
|
|
225
|
+
trigger_id=str(value.trigger_class.__id__),
|
|
226
|
+
attribute_id=str(value.id),
|
|
227
|
+
),
|
|
228
|
+
)
|
|
175
229
|
|
|
176
230
|
if not isinstance(value, BaseDescriptor):
|
|
177
231
|
vellum_value = primitive_to_vellum_value(value)
|