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
|
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Dict, Iterator, Optional, Tuple
|
|
|
9
9
|
from vellum.workflows.references.trigger import TriggerAttributeReference
|
|
10
10
|
from vellum.workflows.types.utils import get_class_attr_names, infer_types
|
|
11
11
|
from vellum.workflows.utils.files import virtual_open
|
|
12
|
-
from vellum.workflows.utils.uuids import uuid4_from_hash
|
|
12
|
+
from vellum.workflows.utils.uuids import get_trigger_attribute_id, uuid4_from_hash
|
|
13
13
|
from vellum_ee.workflows.display.editor import NodeDisplayComment
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -53,6 +53,37 @@ def _convert_to_relative_module_path(absolute_module_path: str, workflow_root: s
|
|
|
53
53
|
return "." + remaining_path
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
def _find_workflow_root_with_metadata(trigger_module: str) -> Optional[str]:
|
|
57
|
+
"""
|
|
58
|
+
Find the workflow root module by searching for metadata.json up the module hierarchy.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
trigger_module: The trigger's module path (e.g., "workflows.my_workflow.triggers.my_trigger")
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The workflow root module path if found, None otherwise
|
|
65
|
+
"""
|
|
66
|
+
module_parts = trigger_module.split(".")
|
|
67
|
+
|
|
68
|
+
# Try searching up the module hierarchy for metadata.json
|
|
69
|
+
for i in range(len(module_parts), 0, -1):
|
|
70
|
+
potential_root = ".".join(module_parts[:i])
|
|
71
|
+
module_dir = potential_root.replace(".", os.path.sep)
|
|
72
|
+
metadata_path = os.path.join(module_dir, "metadata.json")
|
|
73
|
+
|
|
74
|
+
# Try to open the file using virtual_open to support both regular and virtual filesystems
|
|
75
|
+
# virtual_open checks BaseWorkflowFinder instances before falling back to regular open()
|
|
76
|
+
try:
|
|
77
|
+
file_handle = virtual_open(metadata_path)
|
|
78
|
+
if file_handle is not None:
|
|
79
|
+
file_handle.close()
|
|
80
|
+
return potential_root
|
|
81
|
+
except (FileNotFoundError, OSError):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
56
87
|
def _get_trigger_id_from_metadata(trigger_class: Type["BaseTrigger"]) -> Optional[UUID]:
|
|
57
88
|
"""
|
|
58
89
|
Get the trigger ID from metadata.json for a given trigger class.
|
|
@@ -82,50 +113,56 @@ def _get_trigger_id_from_metadata(trigger_class: Type["BaseTrigger"]) -> Optiona
|
|
|
82
113
|
return trigger_path_to_id_mapping.get(relative_trigger_path)
|
|
83
114
|
|
|
84
115
|
|
|
85
|
-
|
|
116
|
+
@lru_cache(maxsize=128)
|
|
117
|
+
def _get_trigger_path_to_id_mapping(module_path: str) -> Dict[str, UUID]:
|
|
86
118
|
"""
|
|
87
|
-
|
|
119
|
+
Read trigger path to ID mapping from metadata.json for a given module.
|
|
120
|
+
|
|
121
|
+
This function is cached to avoid repeated file reads. It searches up the module
|
|
122
|
+
hierarchy for metadata.json and extracts the trigger_path_to_id_mapping.
|
|
88
123
|
|
|
89
124
|
Args:
|
|
90
|
-
|
|
125
|
+
module_path: The module path to search from (e.g., "workflows.my_workflow.triggers.my_trigger")
|
|
91
126
|
|
|
92
127
|
Returns:
|
|
93
|
-
|
|
128
|
+
Dictionary mapping trigger module paths to their UUIDs
|
|
94
129
|
"""
|
|
95
|
-
|
|
130
|
+
try:
|
|
131
|
+
# Find the workflow root that contains metadata.json
|
|
132
|
+
workflow_root = _find_workflow_root_with_metadata(module_path)
|
|
133
|
+
if not workflow_root:
|
|
134
|
+
return {}
|
|
96
135
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
potential_root = ".".join(module_parts[:i])
|
|
100
|
-
module_dir = potential_root.replace(".", os.path.sep)
|
|
136
|
+
# Convert module path to file path
|
|
137
|
+
module_dir = workflow_root.replace(".", os.path.sep)
|
|
101
138
|
metadata_path = os.path.join(module_dir, "metadata.json")
|
|
102
139
|
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if file_handle is not None:
|
|
108
|
-
file_handle.close()
|
|
109
|
-
return potential_root
|
|
110
|
-
except (FileNotFoundError, OSError):
|
|
111
|
-
pass
|
|
140
|
+
# Use virtual_open to support both regular and virtual environments
|
|
141
|
+
with virtual_open(metadata_path) as f:
|
|
142
|
+
metadata_json = json.load(f)
|
|
143
|
+
trigger_mapping = metadata_json.get("trigger_path_to_id_mapping", {})
|
|
112
144
|
|
|
113
|
-
|
|
145
|
+
# Convert string IDs to UUIDs
|
|
146
|
+
return {path: UUID(id_str) for path, id_str in trigger_mapping.items()}
|
|
147
|
+
|
|
148
|
+
except (FileNotFoundError, json.JSONDecodeError, ValueError, KeyError):
|
|
149
|
+
# If there's any error reading or parsing the file, return empty dict
|
|
150
|
+
return {}
|
|
114
151
|
|
|
115
152
|
|
|
116
153
|
@lru_cache(maxsize=128)
|
|
117
|
-
def
|
|
154
|
+
def _get_trigger_attribute_id_mapping(module_path: str) -> Dict[str, UUID]:
|
|
118
155
|
"""
|
|
119
|
-
Read trigger
|
|
156
|
+
Read trigger attribute ID mapping from metadata.json for a given module.
|
|
120
157
|
|
|
121
158
|
This function is cached to avoid repeated file reads. It searches up the module
|
|
122
|
-
hierarchy for metadata.json and extracts the
|
|
159
|
+
hierarchy for metadata.json and extracts the trigger_attribute_id_mapping.
|
|
123
160
|
|
|
124
161
|
Args:
|
|
125
162
|
module_path: The module path to search from (e.g., "workflows.my_workflow.triggers.my_trigger")
|
|
126
163
|
|
|
127
164
|
Returns:
|
|
128
|
-
Dictionary mapping
|
|
165
|
+
Dictionary mapping "<trigger_path>|<attribute_key>" to their UUIDs
|
|
129
166
|
"""
|
|
130
167
|
try:
|
|
131
168
|
# Find the workflow root that contains metadata.json
|
|
@@ -140,16 +177,55 @@ def _get_trigger_path_to_id_mapping(module_path: str) -> Dict[str, UUID]:
|
|
|
140
177
|
# Use virtual_open to support both regular and virtual environments
|
|
141
178
|
with virtual_open(metadata_path) as f:
|
|
142
179
|
metadata_json = json.load(f)
|
|
143
|
-
|
|
180
|
+
raw_mapping = metadata_json.get("trigger_attribute_id_mapping", {}) or {}
|
|
181
|
+
|
|
182
|
+
# Convert string IDs to UUIDs, skipping invalid entries
|
|
183
|
+
result: Dict[str, UUID] = {}
|
|
184
|
+
for key, id_str in raw_mapping.items():
|
|
185
|
+
try:
|
|
186
|
+
result[key] = UUID(id_str)
|
|
187
|
+
except ValueError:
|
|
188
|
+
# Skip invalid UUID entries; they will fall back to hash-based IDs
|
|
189
|
+
continue
|
|
144
190
|
|
|
145
|
-
|
|
146
|
-
return {path: UUID(id_str) for path, id_str in trigger_mapping.items()}
|
|
191
|
+
return result
|
|
147
192
|
|
|
148
|
-
except (FileNotFoundError, json.JSONDecodeError,
|
|
193
|
+
except (FileNotFoundError, json.JSONDecodeError, KeyError):
|
|
149
194
|
# If there's any error reading or parsing the file, return empty dict
|
|
150
195
|
return {}
|
|
151
196
|
|
|
152
197
|
|
|
198
|
+
def _get_trigger_attribute_id_from_metadata(trigger_class: Type["BaseTrigger"], attribute_name: str) -> Optional[UUID]:
|
|
199
|
+
"""
|
|
200
|
+
Get the trigger attribute ID from metadata.json for a given trigger class and attribute name.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
trigger_class: The trigger class containing the attribute
|
|
204
|
+
attribute_name: The name of the attribute
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The UUID from metadata.json, or None if not found
|
|
208
|
+
"""
|
|
209
|
+
workflow_root = _find_workflow_root_with_metadata(trigger_class.__module__)
|
|
210
|
+
if not workflow_root:
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
attribute_mapping = _get_trigger_attribute_id_mapping(trigger_class.__module__)
|
|
214
|
+
if not attribute_mapping:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
# Convert module path to relative path and append class name
|
|
218
|
+
# e.g., "root_module.triggers.scheduled" + "ScheduleTrigger" -> ".triggers.scheduled.ScheduleTrigger"
|
|
219
|
+
relative_module_path = _convert_to_relative_module_path(trigger_class.__module__, workflow_root)
|
|
220
|
+
if not relative_module_path:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
# Build the key: "<trigger_path>|<attribute_key>"
|
|
224
|
+
relative_trigger_path = f"{relative_module_path}.{trigger_class.__qualname__}"
|
|
225
|
+
key = f"{relative_trigger_path}|{attribute_name}"
|
|
226
|
+
return attribute_mapping.get(key)
|
|
227
|
+
|
|
228
|
+
|
|
153
229
|
class BaseTriggerMeta(ABCMeta):
|
|
154
230
|
def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
|
|
155
231
|
if "Display" not in dct:
|
|
@@ -388,4 +464,60 @@ class BaseTrigger(ABC, metaclass=BaseTriggerMeta):
|
|
|
388
464
|
def bind_to_state(self, state: "BaseState") -> None:
|
|
389
465
|
"""Persist this trigger's attribute values onto the provided state."""
|
|
390
466
|
|
|
467
|
+
if state.meta.trigger_attributes is None:
|
|
468
|
+
state.meta.trigger_attributes = {}
|
|
391
469
|
state.meta.trigger_attributes.update(self.to_trigger_attribute_values())
|
|
470
|
+
|
|
471
|
+
__trigger_name__: ClassVar[str] = "trigger"
|
|
472
|
+
|
|
473
|
+
@classmethod
|
|
474
|
+
def get_attribute_id(cls, attribute_name: str) -> UUID:
|
|
475
|
+
"""
|
|
476
|
+
Get the ID for a trigger attribute, first checking metadata.json for a stable ID,
|
|
477
|
+
then falling back to a deterministic hash-based ID.
|
|
478
|
+
|
|
479
|
+
This ensures trigger attribute IDs remain stable across serialization round-trips
|
|
480
|
+
when metadata.json is present.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
attribute_name: The name of the attribute
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
The UUID for the attribute
|
|
487
|
+
"""
|
|
488
|
+
# First try to get the ID from metadata.json
|
|
489
|
+
metadata_id = _get_trigger_attribute_id_from_metadata(cls, attribute_name)
|
|
490
|
+
if metadata_id is not None:
|
|
491
|
+
return metadata_id
|
|
492
|
+
|
|
493
|
+
# Fall back to deterministic hash-based ID
|
|
494
|
+
return get_trigger_attribute_id(cls, attribute_name)
|
|
495
|
+
|
|
496
|
+
class Config:
|
|
497
|
+
"""Configuration for trigger behavior. Subclasses can override."""
|
|
498
|
+
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
def __on_workflow_initiated__(self, state: "BaseState") -> None:
|
|
502
|
+
"""
|
|
503
|
+
Lifecycle hook called by WorkflowRunner when workflow execution starts.
|
|
504
|
+
|
|
505
|
+
Subclasses can override to perform state initialization (e.g., appending
|
|
506
|
+
user messages to chat history so nodes can reference them).
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
state: The initial workflow state
|
|
510
|
+
"""
|
|
511
|
+
pass
|
|
512
|
+
|
|
513
|
+
def __on_workflow_fulfilled__(self, state: "BaseState") -> None:
|
|
514
|
+
"""
|
|
515
|
+
Lifecycle hook called by WorkflowRunner before workflow outputs are resolved.
|
|
516
|
+
|
|
517
|
+
Subclasses can override to perform state updates that should be reflected
|
|
518
|
+
in workflow outputs (e.g., appending assistant messages to chat history).
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
state: The final workflow state
|
|
522
|
+
"""
|
|
523
|
+
pass
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from vellum.client.types import (
|
|
4
|
+
ArrayChatMessageContent,
|
|
5
|
+
ArrayChatMessageContentItem,
|
|
6
|
+
AudioChatMessageContent,
|
|
7
|
+
ChatMessage,
|
|
8
|
+
ChatMessageContent,
|
|
9
|
+
DocumentChatMessageContent,
|
|
10
|
+
FunctionCallChatMessageContent,
|
|
11
|
+
ImageChatMessageContent,
|
|
12
|
+
StringChatMessageContent,
|
|
13
|
+
VideoChatMessageContent,
|
|
14
|
+
)
|
|
15
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
16
|
+
from vellum.workflows.descriptors.utils import resolve_value
|
|
17
|
+
from vellum.workflows.references.lazy import LazyReference
|
|
18
|
+
from vellum.workflows.references.output import OutputReference
|
|
19
|
+
from vellum.workflows.triggers.base import BaseTrigger
|
|
20
|
+
from vellum.workflows.utils.pydantic_schema import validate_obj_as
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from vellum.workflows.state.base import BaseState
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ChatMessageTrigger(BaseTrigger):
|
|
27
|
+
"""
|
|
28
|
+
Trigger for chat-based workflows that supports multi-turn conversations.
|
|
29
|
+
|
|
30
|
+
Appends user message to state.chat_history at workflow start, and assistant
|
|
31
|
+
response after workflow completion. Use previous_execution_id to maintain
|
|
32
|
+
conversation state across executions.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
message: The incoming chat message content. Can be a string or a list of content items.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
message: Union[str, List[ArrayChatMessageContentItem]]
|
|
39
|
+
|
|
40
|
+
class Config(BaseTrigger.Config):
|
|
41
|
+
output: Optional[BaseDescriptor[Any]] = None
|
|
42
|
+
|
|
43
|
+
def __init__(self, **kwargs: Any):
|
|
44
|
+
"""Initialize ChatMessageTrigger, converting VellumValue objects to ChatMessageContent if needed."""
|
|
45
|
+
# Convert message from VellumValue format to ChatMessageContent format if needed
|
|
46
|
+
if "message" in kwargs:
|
|
47
|
+
message = kwargs["message"]
|
|
48
|
+
# Handle string messages by converting to a list with a single StringChatMessageContent
|
|
49
|
+
if isinstance(message, str):
|
|
50
|
+
kwargs["message"] = [StringChatMessageContent(value=message)]
|
|
51
|
+
elif isinstance(message, list):
|
|
52
|
+
converted_message = []
|
|
53
|
+
for item in message:
|
|
54
|
+
# If it's already a ChatMessageContent type, keep it as-is
|
|
55
|
+
if isinstance(
|
|
56
|
+
item,
|
|
57
|
+
(
|
|
58
|
+
StringChatMessageContent,
|
|
59
|
+
ImageChatMessageContent,
|
|
60
|
+
AudioChatMessageContent,
|
|
61
|
+
VideoChatMessageContent,
|
|
62
|
+
DocumentChatMessageContent,
|
|
63
|
+
FunctionCallChatMessageContent,
|
|
64
|
+
),
|
|
65
|
+
):
|
|
66
|
+
converted_message.append(item)
|
|
67
|
+
# Handle raw strings in the array by wrapping them in StringChatMessageContent
|
|
68
|
+
elif isinstance(item, str):
|
|
69
|
+
converted_message.append(StringChatMessageContent(value=item))
|
|
70
|
+
# Convert VellumValue objects or dicts to ChatMessageContent
|
|
71
|
+
# Use discriminated union validation
|
|
72
|
+
else:
|
|
73
|
+
# Get the dict representation (either from Pydantic model or already a dict)
|
|
74
|
+
item_dict = item.model_dump() if hasattr(item, "model_dump") else item
|
|
75
|
+
converted_message.append(
|
|
76
|
+
validate_obj_as(ArrayChatMessageContentItem, item_dict) # type: ignore[arg-type]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
kwargs["message"] = converted_message
|
|
80
|
+
|
|
81
|
+
super().__init__(**kwargs)
|
|
82
|
+
|
|
83
|
+
def __on_workflow_initiated__(self, state: "BaseState") -> None:
|
|
84
|
+
"""Appends user message to state.chat_history at workflow start."""
|
|
85
|
+
if not hasattr(state, "chat_history"):
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
if state.chat_history is None:
|
|
89
|
+
state.chat_history = []
|
|
90
|
+
|
|
91
|
+
if isinstance(self.message, str):
|
|
92
|
+
user_message = ChatMessage(
|
|
93
|
+
role="USER",
|
|
94
|
+
text=self.message,
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
user_message = ChatMessage(
|
|
98
|
+
role="USER",
|
|
99
|
+
content=ArrayChatMessageContent(value=self.message),
|
|
100
|
+
)
|
|
101
|
+
state.chat_history.append(user_message)
|
|
102
|
+
|
|
103
|
+
def __on_workflow_fulfilled__(self, state: "BaseState") -> None:
|
|
104
|
+
"""Appends assistant response to state.chat_history after workflow completion."""
|
|
105
|
+
if not hasattr(state, "chat_history"):
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
if state.chat_history is None:
|
|
109
|
+
state.chat_history = []
|
|
110
|
+
|
|
111
|
+
output = self.Config.output
|
|
112
|
+
if output is not None:
|
|
113
|
+
resolved_output = self._resolve_output(output, state)
|
|
114
|
+
assistant_message = ChatMessage(
|
|
115
|
+
role="ASSISTANT",
|
|
116
|
+
content=resolved_output if not isinstance(resolved_output, str) else None,
|
|
117
|
+
text=resolved_output if isinstance(resolved_output, str) else None,
|
|
118
|
+
)
|
|
119
|
+
state.chat_history.append(assistant_message)
|
|
120
|
+
|
|
121
|
+
def _resolve_output(
|
|
122
|
+
self,
|
|
123
|
+
output: BaseDescriptor[Any],
|
|
124
|
+
state: "BaseState",
|
|
125
|
+
) -> Union[str, ChatMessageContent]:
|
|
126
|
+
"""Resolves output reference, handling workflow output references."""
|
|
127
|
+
descriptor = output
|
|
128
|
+
if isinstance(output, LazyReference) and callable(output._get):
|
|
129
|
+
descriptor = output._get()
|
|
130
|
+
|
|
131
|
+
if isinstance(descriptor, OutputReference) and isinstance(descriptor.instance, BaseDescriptor):
|
|
132
|
+
return resolve_value(descriptor.instance, state)
|
|
133
|
+
|
|
134
|
+
return resolve_value(output, state)
|
|
135
|
+
|
|
136
|
+
__trigger_name__: ClassVar[str] = "chat"
|
|
137
|
+
|
|
138
|
+
class Display(BaseTrigger.Display):
|
|
139
|
+
label: str = "Chat Message"
|
|
140
|
+
icon: Optional[str] = "vellum:icon:message-dots"
|
|
141
|
+
color: Optional[str] = "blue"
|
|
@@ -13,6 +13,8 @@ class IntegrationTriggerMeta(BaseTriggerMeta):
|
|
|
13
13
|
This metaclass extends BaseTriggerMeta to automatically convert type annotations
|
|
14
14
|
into TriggerAttributeReference objects during class creation. This enables trigger
|
|
15
15
|
attributes to be referenced in workflow graphs while maintaining type safety.
|
|
16
|
+
|
|
17
|
+
It also sets __trigger_name__ based on Config.slug for trigger name resolution.
|
|
16
18
|
"""
|
|
17
19
|
|
|
18
20
|
def __new__(mcs, name: str, bases: tuple, namespace: dict, **kwargs: Any) -> "IntegrationTriggerMeta":
|
|
@@ -36,6 +38,16 @@ class IntegrationTriggerMeta(BaseTriggerMeta):
|
|
|
36
38
|
# Set as class attribute so it's directly accessible
|
|
37
39
|
setattr(cls, attr_name, reference)
|
|
38
40
|
|
|
41
|
+
# Set __trigger_name__ based on Config.slug if available
|
|
42
|
+
if has_config:
|
|
43
|
+
config_class = namespace.get("Config")
|
|
44
|
+
if config_class is not None:
|
|
45
|
+
slug = getattr(config_class, "slug", None)
|
|
46
|
+
if slug:
|
|
47
|
+
cls.__trigger_name__ = str(slug)
|
|
48
|
+
else:
|
|
49
|
+
cls.__trigger_name__ = "integration"
|
|
50
|
+
|
|
39
51
|
return cls
|
|
40
52
|
|
|
41
53
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import ClassVar, Optional
|
|
3
3
|
|
|
4
4
|
from vellum.workflows.triggers.base import BaseTrigger
|
|
5
5
|
|
|
@@ -16,3 +16,5 @@ class ScheduleTrigger(BaseTrigger):
|
|
|
16
16
|
class Config:
|
|
17
17
|
cron: str
|
|
18
18
|
timezone: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
__trigger_name__: ClassVar[str] = "scheduled"
|