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
vellum/utils/files/mixin.py
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import binascii
|
|
3
|
+
import re
|
|
1
4
|
from typing import Any, ContextManager, Iterator, Optional
|
|
2
5
|
|
|
6
|
+
from pydantic import field_serializer
|
|
7
|
+
|
|
3
8
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
|
9
|
+
from vellum.utils.files.constants import BASE64_DATA_URL_PATTERN
|
|
10
|
+
from vellum.utils.files.exceptions import InvalidFileSourceError
|
|
4
11
|
from vellum.utils.files.read import read_vellum_file
|
|
5
12
|
from vellum.utils.files.stream import stream_vellum_file
|
|
6
13
|
from vellum.utils.files.upload import upload_vellum_file
|
|
@@ -107,3 +114,22 @@ class VellumFileMixin(UniversalBaseModel):
|
|
|
107
114
|
str: A signed URL for accessing the uploaded file
|
|
108
115
|
"""
|
|
109
116
|
return get_signed_url(self, expiry_seconds=expiry_seconds, vellum_client=vellum_client)
|
|
117
|
+
|
|
118
|
+
@field_serializer("src", check_fields=False)
|
|
119
|
+
@classmethod
|
|
120
|
+
def validate_src_on_serialize(cls, src: str) -> str:
|
|
121
|
+
"""Validate the src field during serialization.
|
|
122
|
+
|
|
123
|
+
For PDF base64 data URLs, this validates that the base64 content is properly encoded.
|
|
124
|
+
"""
|
|
125
|
+
data_url_match = re.match(BASE64_DATA_URL_PATTERN, src)
|
|
126
|
+
if data_url_match:
|
|
127
|
+
mime_type = data_url_match.group(1) or ""
|
|
128
|
+
if mime_type == "application/pdf":
|
|
129
|
+
base64_content = data_url_match.group(3)
|
|
130
|
+
try:
|
|
131
|
+
base64.b64decode(base64_content, validate=True)
|
|
132
|
+
except binascii.Error as e:
|
|
133
|
+
raise InvalidFileSourceError(f"Invalid base64 encoding in PDF data URL: {e}") from e
|
|
134
|
+
|
|
135
|
+
return src
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import pytest
|
|
1
2
|
import base64
|
|
2
3
|
from unittest.mock import Mock, patch
|
|
3
4
|
|
|
5
|
+
from pydantic_core import PydanticSerializationError
|
|
6
|
+
|
|
4
7
|
from vellum.utils.files import VellumFileMixin
|
|
5
8
|
|
|
6
9
|
# Sample content for testing
|
|
@@ -203,3 +206,62 @@ def test_vellum_file_mixin_get_signed_url_method_already_uploaded():
|
|
|
203
206
|
assert result == signed_url
|
|
204
207
|
mock_client.uploaded_files.create.assert_not_called()
|
|
205
208
|
mock_client.uploaded_files.retrieve.assert_called_once_with(file_id, expiry_seconds=604800)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_vellum_file_mixin_serialization_valid_base64():
|
|
212
|
+
"""Tests that serialization succeeds with valid base64 data URLs."""
|
|
213
|
+
|
|
214
|
+
# GIVEN a test document with valid base64 content
|
|
215
|
+
content = b"Hello, this is a test document!"
|
|
216
|
+
base64_content = base64.b64encode(content).decode("utf-8")
|
|
217
|
+
src = f"data:application/pdf;base64,{base64_content}"
|
|
218
|
+
test_file = TestDocument(src=src)
|
|
219
|
+
|
|
220
|
+
# WHEN serializing the document
|
|
221
|
+
serialized = test_file.model_dump()
|
|
222
|
+
|
|
223
|
+
# THEN the serialization should succeed and preserve the src
|
|
224
|
+
assert serialized["src"] == src
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_vellum_file_mixin_serialization_invalid_base64():
|
|
228
|
+
"""Tests that serialization raises an error for invalid base64 data URLs."""
|
|
229
|
+
|
|
230
|
+
# GIVEN a test document with invalid base64 content
|
|
231
|
+
invalid_base64 = "not-valid-base64!!!"
|
|
232
|
+
src = f"data:application/pdf;base64,{invalid_base64}"
|
|
233
|
+
test_file = TestDocument(src=src)
|
|
234
|
+
|
|
235
|
+
# WHEN serializing the document
|
|
236
|
+
# THEN a PydanticSerializationError should be raised with InvalidFileSourceError as the cause
|
|
237
|
+
with pytest.raises(PydanticSerializationError, match="Invalid base64 encoding in PDF data URL"):
|
|
238
|
+
test_file.model_dump()
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_vellum_file_mixin_serialization_non_data_url():
|
|
242
|
+
"""Tests that serialization succeeds for non-data URL sources."""
|
|
243
|
+
|
|
244
|
+
# GIVEN a test document with a regular URL source
|
|
245
|
+
src = "https://example.com/document.pdf"
|
|
246
|
+
test_file = TestDocument(src=src)
|
|
247
|
+
|
|
248
|
+
# WHEN serializing the document
|
|
249
|
+
serialized = test_file.model_dump()
|
|
250
|
+
|
|
251
|
+
# THEN the serialization should succeed and preserve the src
|
|
252
|
+
assert serialized["src"] == src
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_vellum_file_mixin_serialization_vellum_uploaded_file():
|
|
256
|
+
"""Tests that serialization succeeds for vellum:uploaded-file sources."""
|
|
257
|
+
|
|
258
|
+
# GIVEN a test document with a vellum:uploaded-file source
|
|
259
|
+
file_id = "12345678-1234-1234-1234-123456789abc"
|
|
260
|
+
src = f"vellum:uploaded-file:{file_id}"
|
|
261
|
+
test_file = TestDocument(src=src)
|
|
262
|
+
|
|
263
|
+
# WHEN serializing the document
|
|
264
|
+
serialized = test_file.model_dump()
|
|
265
|
+
|
|
266
|
+
# THEN the serialization should succeed and preserve the src
|
|
267
|
+
assert serialized["src"] == src
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
from vellum.utils.vellum_client import create_vellum_client, create_vellum_environment
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestCreateVellumClient:
|
|
8
|
+
"""Tests for create_vellum_client and create_vellum_environment."""
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
["api_url", "predict_api_url", "expected_predict_url"],
|
|
12
|
+
[
|
|
13
|
+
(None, None, "https://predict.vellum.ai"),
|
|
14
|
+
(None, "https://custom-predict.example.com", "https://custom-predict.example.com"),
|
|
15
|
+
("CUSTOM_API_URL", None, "https://predict.vellum.ai"),
|
|
16
|
+
("CUSTOM_API_URL", "https://custom-predict.example.com", "https://custom-predict.example.com"),
|
|
17
|
+
],
|
|
18
|
+
ids=[
|
|
19
|
+
"defaults_to_standard_predict_url",
|
|
20
|
+
"predict_api_url_overrides_default",
|
|
21
|
+
"api_url_env_var_does_not_affect_predict_when_not_set",
|
|
22
|
+
"predict_api_url_takes_precedence_over_api_url",
|
|
23
|
+
],
|
|
24
|
+
)
|
|
25
|
+
def test_create_vellum_client__predict_url_resolution(self, api_url, predict_api_url, expected_predict_url):
|
|
26
|
+
"""Tests that predict_api_url is resolved correctly based on provided parameters."""
|
|
27
|
+
|
|
28
|
+
# GIVEN no environment variables are set
|
|
29
|
+
with patch.dict("os.environ", {}, clear=True):
|
|
30
|
+
# WHEN creating a vellum client with the given parameters
|
|
31
|
+
client = create_vellum_client(
|
|
32
|
+
api_key="test-api-key",
|
|
33
|
+
api_url=api_url,
|
|
34
|
+
predict_api_url=predict_api_url,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# THEN the predict URL should be resolved correctly
|
|
38
|
+
assert client._client_wrapper._environment.predict == expected_predict_url
|
|
39
|
+
|
|
40
|
+
@pytest.mark.parametrize(
|
|
41
|
+
["env_vars", "predict_api_url", "expected_predict_url"],
|
|
42
|
+
[
|
|
43
|
+
({"VELLUM_PREDICT_API_URL": "https://env-predict.example.com"}, None, "https://env-predict.example.com"),
|
|
44
|
+
(
|
|
45
|
+
{"VELLUM_PREDICT_API_URL": "https://env-predict.example.com"},
|
|
46
|
+
"https://param-predict.example.com",
|
|
47
|
+
"https://param-predict.example.com",
|
|
48
|
+
),
|
|
49
|
+
({"VELLUM_API_URL": "https://env-api.example.com"}, None, "https://env-api.example.com"),
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
"VELLUM_API_URL": "https://env-api.example.com",
|
|
53
|
+
"VELLUM_PREDICT_API_URL": "https://env-predict.example.com",
|
|
54
|
+
},
|
|
55
|
+
None,
|
|
56
|
+
"https://env-predict.example.com",
|
|
57
|
+
),
|
|
58
|
+
],
|
|
59
|
+
ids=[
|
|
60
|
+
"env_var_predict_api_url_is_used",
|
|
61
|
+
"param_predict_api_url_overrides_env_var",
|
|
62
|
+
"env_var_api_url_fallback_for_predict",
|
|
63
|
+
"env_var_predict_api_url_takes_precedence_over_api_url",
|
|
64
|
+
],
|
|
65
|
+
)
|
|
66
|
+
def test_create_vellum_client__predict_url_env_var_resolution(
|
|
67
|
+
self, env_vars, predict_api_url, expected_predict_url
|
|
68
|
+
):
|
|
69
|
+
"""Tests that predict_api_url respects environment variables with correct precedence."""
|
|
70
|
+
|
|
71
|
+
# GIVEN specific environment variables are set
|
|
72
|
+
with patch.dict("os.environ", env_vars, clear=True):
|
|
73
|
+
# WHEN creating a vellum client with the given parameters
|
|
74
|
+
client = create_vellum_client(
|
|
75
|
+
api_key="test-api-key",
|
|
76
|
+
predict_api_url=predict_api_url,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# THEN the predict URL should be resolved correctly
|
|
80
|
+
assert client._client_wrapper._environment.predict == expected_predict_url
|
|
81
|
+
|
|
82
|
+
def test_create_vellum_environment__predict_api_url_does_not_affect_other_urls(self):
|
|
83
|
+
"""Tests that predict_api_url only affects the predict endpoint, not default or documents."""
|
|
84
|
+
|
|
85
|
+
# GIVEN no environment variables are set
|
|
86
|
+
with patch.dict("os.environ", {}, clear=True):
|
|
87
|
+
# WHEN creating a vellum environment with a custom predict_api_url
|
|
88
|
+
environment = create_vellum_environment(predict_api_url="https://custom-predict.example.com")
|
|
89
|
+
|
|
90
|
+
# THEN the predict URL should be customized
|
|
91
|
+
assert environment.predict == "https://custom-predict.example.com"
|
|
92
|
+
|
|
93
|
+
# AND the default and documents URLs should remain unchanged
|
|
94
|
+
assert environment.default == "https://api.vellum.ai"
|
|
95
|
+
assert environment.documents == "https://documents.vellum.ai"
|
vellum/utils/uuid.py
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
from typing import Union
|
|
2
|
+
from typing import Any, Literal, Union, overload
|
|
3
|
+
from typing_extensions import TypeGuard
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
@overload
|
|
7
|
+
def is_valid_uuid(val: None) -> Literal[False]: ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@overload
|
|
11
|
+
def is_valid_uuid(val: str) -> TypeGuard[str]: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@overload
|
|
15
|
+
def is_valid_uuid(val: uuid.UUID) -> TypeGuard[uuid.UUID]: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@overload
|
|
19
|
+
def is_valid_uuid(val: Any) -> Literal[False]: ...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_valid_uuid(val: Any) -> TypeGuard[Union[str, uuid.UUID]]:
|
|
6
23
|
try:
|
|
7
24
|
uuid.UUID(str(val))
|
|
8
25
|
return True
|
vellum/utils/vellum_client.py
CHANGED
|
@@ -9,22 +9,29 @@ def create_vellum_client(
|
|
|
9
9
|
api_key: Optional[str] = None,
|
|
10
10
|
api_url: Optional[str] = None,
|
|
11
11
|
api_version: Optional[ApiVersionEnum] = None,
|
|
12
|
+
predict_api_url: Optional[str] = None,
|
|
12
13
|
) -> Vellum:
|
|
13
14
|
if api_key is None:
|
|
14
15
|
api_key = os.getenv("VELLUM_API_KEY", default="")
|
|
15
16
|
|
|
16
17
|
return Vellum(
|
|
17
18
|
api_key=api_key,
|
|
18
|
-
environment=create_vellum_environment(api_url),
|
|
19
|
+
environment=create_vellum_environment(api_url, predict_api_url=predict_api_url),
|
|
19
20
|
api_version=api_version,
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def create_vellum_environment(
|
|
24
|
+
def create_vellum_environment(
|
|
25
|
+
api_url: Optional[str] = None,
|
|
26
|
+
predict_api_url: Optional[str] = None,
|
|
27
|
+
) -> VellumEnvironment:
|
|
28
|
+
predict = predict_api_url or _resolve_env(
|
|
29
|
+
[api_url, "VELLUM_PREDICT_API_URL", "VELLUM_API_URL"], "https://predict.vellum.ai"
|
|
30
|
+
)
|
|
24
31
|
return VellumEnvironment(
|
|
25
32
|
default=_resolve_env([api_url, "VELLUM_DEFAULT_API_URL", "VELLUM_API_URL"], "https://api.vellum.ai"),
|
|
26
33
|
documents=_resolve_env([api_url, "VELLUM_DOCUMENTS_API_URL", "VELLUM_API_URL"], "https://documents.vellum.ai"),
|
|
27
|
-
predict=
|
|
34
|
+
predict=predict,
|
|
28
35
|
)
|
|
29
36
|
|
|
30
37
|
|
vellum/workflows/__init__.py
CHANGED
|
@@ -26,15 +26,17 @@ from .nodes import (
|
|
|
26
26
|
WebSearchNode,
|
|
27
27
|
)
|
|
28
28
|
from .nodes.displayable.tool_calling_node import ToolCallingNode
|
|
29
|
+
from .nodes.mocks import MockNodeExecution
|
|
29
30
|
from .ports import Port
|
|
30
31
|
from .references.environment_variable import EnvironmentVariableReference
|
|
31
32
|
from .references.lazy import LazyReference
|
|
32
33
|
from .runner import WorkflowRunner
|
|
33
34
|
from .sandbox import WorkflowSandboxRunner
|
|
34
35
|
from .state.base import BaseState
|
|
35
|
-
from .triggers import IntegrationTrigger, ScheduleTrigger
|
|
36
|
+
from .triggers import ChatMessageTrigger, IntegrationTrigger, ScheduleTrigger
|
|
36
37
|
from .types.core import Json, MergeBehavior
|
|
37
38
|
from .types.definition import DeploymentDefinition, MCPServer, VellumIntegrationToolDefinition
|
|
39
|
+
from .utils.functions import tool, use_tool_inputs
|
|
38
40
|
from .workflows import BaseWorkflow
|
|
39
41
|
|
|
40
42
|
__all__ = [
|
|
@@ -48,6 +50,7 @@ __all__ = [
|
|
|
48
50
|
"BaseSearchNode",
|
|
49
51
|
"BaseState",
|
|
50
52
|
"BaseWorkflow",
|
|
53
|
+
"ChatMessageTrigger",
|
|
51
54
|
"CodeExecutionNode",
|
|
52
55
|
"ConditionalNode",
|
|
53
56
|
"DatasetRow",
|
|
@@ -64,6 +67,7 @@ __all__ = [
|
|
|
64
67
|
"MapNode",
|
|
65
68
|
"MCPServer",
|
|
66
69
|
"MergeBehavior",
|
|
70
|
+
"MockNodeExecution",
|
|
67
71
|
"NodeException",
|
|
68
72
|
"NoteNode",
|
|
69
73
|
"Port",
|
|
@@ -73,8 +77,10 @@ __all__ = [
|
|
|
73
77
|
"SearchNode",
|
|
74
78
|
"SubworkflowDeploymentNode",
|
|
75
79
|
"TemplatingNode",
|
|
80
|
+
"tool",
|
|
76
81
|
"ToolCallingNode",
|
|
77
82
|
"TryNode",
|
|
83
|
+
"use_tool_inputs",
|
|
78
84
|
"VellumIntegrationToolDefinition",
|
|
79
85
|
"WebSearchNode",
|
|
80
86
|
"WorkflowErrorCode",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Generic, Optional, Tuple, Type, TypeVar, Union, cast, overload
|
|
2
2
|
|
|
3
|
+
from pydantic import GetCoreSchemaHandler, ValidationInfo
|
|
4
|
+
from pydantic_core import core_schema
|
|
5
|
+
|
|
3
6
|
if TYPE_CHECKING:
|
|
4
7
|
from vellum.workflows.expressions.accessor import AccessorExpression
|
|
5
8
|
from vellum.workflows.expressions.add import AddExpression
|
|
@@ -65,6 +68,15 @@ class BaseDescriptor(Generic[_T]):
|
|
|
65
68
|
def types(self) -> Tuple[Type[_T], ...]:
|
|
66
69
|
return self._types
|
|
67
70
|
|
|
71
|
+
@property
|
|
72
|
+
def normalized_type(self) -> Type:
|
|
73
|
+
all_types = self._types
|
|
74
|
+
if len(all_types) == 1:
|
|
75
|
+
return all_types[0]
|
|
76
|
+
else:
|
|
77
|
+
# Union should be considered a type, but mypy doesn't agree
|
|
78
|
+
return Union[all_types] # type: ignore[return-value]
|
|
79
|
+
|
|
68
80
|
@property
|
|
69
81
|
def instance(self) -> Optional[_T]:
|
|
70
82
|
return self._instance
|
|
@@ -179,6 +191,17 @@ class BaseDescriptor(Generic[_T]):
|
|
|
179
191
|
|
|
180
192
|
return LessThanExpression(lhs=self, rhs=other)
|
|
181
193
|
|
|
194
|
+
@overload
|
|
195
|
+
def __lt__(self, other: "BaseDescriptor[_O]") -> "LessThanExpression[_T, _O]": ... # type: ignore[misc]
|
|
196
|
+
|
|
197
|
+
@overload
|
|
198
|
+
def __lt__(self, other: _O) -> "LessThanExpression[_T, _O]": ...
|
|
199
|
+
|
|
200
|
+
def __lt__(self, other: "Union[BaseDescriptor[_O], _O]") -> "LessThanExpression[_T, _O]":
|
|
201
|
+
from vellum.workflows.expressions.less_than import LessThanExpression
|
|
202
|
+
|
|
203
|
+
return LessThanExpression(lhs=self, rhs=other)
|
|
204
|
+
|
|
182
205
|
@overload
|
|
183
206
|
def greater_than(self, other: "BaseDescriptor[_O]") -> "GreaterThanExpression[_T, _O]": ...
|
|
184
207
|
|
|
@@ -190,6 +213,17 @@ class BaseDescriptor(Generic[_T]):
|
|
|
190
213
|
|
|
191
214
|
return GreaterThanExpression(lhs=self, rhs=other)
|
|
192
215
|
|
|
216
|
+
@overload
|
|
217
|
+
def __gt__(self, other: "BaseDescriptor[_O]") -> "GreaterThanExpression[_T, _O]": ... # type: ignore[misc]
|
|
218
|
+
|
|
219
|
+
@overload
|
|
220
|
+
def __gt__(self, other: _O) -> "GreaterThanExpression[_T, _O]": ...
|
|
221
|
+
|
|
222
|
+
def __gt__(self, other: "Union[BaseDescriptor[_O], _O]") -> "GreaterThanExpression[_T, _O]":
|
|
223
|
+
from vellum.workflows.expressions.greater_than import GreaterThanExpression
|
|
224
|
+
|
|
225
|
+
return GreaterThanExpression(lhs=self, rhs=other)
|
|
226
|
+
|
|
193
227
|
@overload
|
|
194
228
|
def less_than_or_equal_to(self, other: "BaseDescriptor[_O]") -> "LessThanOrEqualToExpression[_T, _O]": ...
|
|
195
229
|
|
|
@@ -201,6 +235,17 @@ class BaseDescriptor(Generic[_T]):
|
|
|
201
235
|
|
|
202
236
|
return LessThanOrEqualToExpression(lhs=self, rhs=other)
|
|
203
237
|
|
|
238
|
+
@overload
|
|
239
|
+
def __le__(self, other: "BaseDescriptor[_O]") -> "LessThanOrEqualToExpression[_T, _O]": ... # type: ignore[misc]
|
|
240
|
+
|
|
241
|
+
@overload
|
|
242
|
+
def __le__(self, other: _O) -> "LessThanOrEqualToExpression[_T, _O]": ...
|
|
243
|
+
|
|
244
|
+
def __le__(self, other: "Union[BaseDescriptor[_O], _O]") -> "LessThanOrEqualToExpression[_T, _O]":
|
|
245
|
+
from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
|
|
246
|
+
|
|
247
|
+
return LessThanOrEqualToExpression(lhs=self, rhs=other)
|
|
248
|
+
|
|
204
249
|
@overload
|
|
205
250
|
def greater_than_or_equal_to(self, other: "BaseDescriptor[_O]") -> "GreaterThanOrEqualToExpression[_T, _O]": ...
|
|
206
251
|
|
|
@@ -214,6 +259,17 @@ class BaseDescriptor(Generic[_T]):
|
|
|
214
259
|
|
|
215
260
|
return GreaterThanOrEqualToExpression(lhs=self, rhs=other)
|
|
216
261
|
|
|
262
|
+
@overload
|
|
263
|
+
def __ge__(self, other: "BaseDescriptor[_O]") -> "GreaterThanOrEqualToExpression[_T, _O]": ... # type: ignore[misc]
|
|
264
|
+
|
|
265
|
+
@overload
|
|
266
|
+
def __ge__(self, other: _O) -> "GreaterThanOrEqualToExpression[_T, _O]": ...
|
|
267
|
+
|
|
268
|
+
def __ge__(self, other: "Union[BaseDescriptor[_O], _O]") -> "GreaterThanOrEqualToExpression[_T, _O]":
|
|
269
|
+
from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
|
|
270
|
+
|
|
271
|
+
return GreaterThanOrEqualToExpression(lhs=self, rhs=other)
|
|
272
|
+
|
|
217
273
|
@overload
|
|
218
274
|
def contains(self, other: "BaseDescriptor[_O]") -> "ContainsExpression[_T, _O]": ...
|
|
219
275
|
|
|
@@ -439,3 +495,33 @@ class BaseDescriptor(Generic[_T]):
|
|
|
439
495
|
from vellum.workflows.expressions.minus import MinusExpression
|
|
440
496
|
|
|
441
497
|
return MinusExpression(lhs=self, rhs=other)
|
|
498
|
+
|
|
499
|
+
@classmethod
|
|
500
|
+
def __get_pydantic_core_schema__(
|
|
501
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
|
502
|
+
) -> core_schema.CoreSchema:
|
|
503
|
+
def validate(value: Any, info: ValidationInfo) -> Any:
|
|
504
|
+
if isinstance(value, cls):
|
|
505
|
+
return value
|
|
506
|
+
|
|
507
|
+
if not isinstance(value, dict):
|
|
508
|
+
raise TypeError(f"Value must be an instance of {cls.__name__} or a dict")
|
|
509
|
+
|
|
510
|
+
context = info.context
|
|
511
|
+
if not isinstance(context, dict):
|
|
512
|
+
raise TypeError(f"Unexpected type for context: {type(context)}")
|
|
513
|
+
|
|
514
|
+
validator = context.get("descriptor_validator")
|
|
515
|
+
if validator and callable(validator):
|
|
516
|
+
return validator(value)
|
|
517
|
+
|
|
518
|
+
from vellum.workflows.references.constant import ConstantValueReference
|
|
519
|
+
|
|
520
|
+
return ConstantValueReference(value)
|
|
521
|
+
|
|
522
|
+
return core_schema.union_schema(
|
|
523
|
+
[
|
|
524
|
+
core_schema.is_instance_schema(cls),
|
|
525
|
+
core_schema.with_info_after_validator_function(validate, core_schema.dict_schema()),
|
|
526
|
+
]
|
|
527
|
+
)
|
|
@@ -132,6 +132,11 @@ class DummyNode(BaseNode[FixtureState]):
|
|
|
132
132
|
(FixtureState.alpha + FixtureState.beta, 3),
|
|
133
133
|
(FixtureState.gamma + FixtureState.delta, "helloel"),
|
|
134
134
|
(FixtureState.theta + FixtureState.theta, ["baz", "baz"]),
|
|
135
|
+
# Test comparison operators
|
|
136
|
+
(FixtureState.alpha < FixtureState.beta, True),
|
|
137
|
+
(FixtureState.alpha > FixtureState.beta, False),
|
|
138
|
+
(FixtureState.alpha <= FixtureState.beta, True),
|
|
139
|
+
(FixtureState.alpha >= FixtureState.beta, False),
|
|
135
140
|
],
|
|
136
141
|
ids=[
|
|
137
142
|
"or",
|
|
@@ -194,6 +199,10 @@ class DummyNode(BaseNode[FixtureState]):
|
|
|
194
199
|
"add_integers",
|
|
195
200
|
"add_strings",
|
|
196
201
|
"add_lists",
|
|
202
|
+
"lt_operator",
|
|
203
|
+
"gt_operator",
|
|
204
|
+
"le_operator",
|
|
205
|
+
"ge_operator",
|
|
197
206
|
],
|
|
198
207
|
)
|
|
199
208
|
def test_resolve_value__happy_path(descriptor, expected_value):
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode, workflow_error_to_vellum_error
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_workflow_error_to_vellum_error__preserves_raw_data():
|
|
5
|
+
"""
|
|
6
|
+
Tests that raw_data is preserved when converting WorkflowError to VellumError.
|
|
7
|
+
"""
|
|
8
|
+
# GIVEN a WorkflowError with raw_data containing integration details
|
|
9
|
+
raw_data = {
|
|
10
|
+
"integration": {
|
|
11
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
12
|
+
"provider": "COMPOSIO",
|
|
13
|
+
"name": "GITHUB",
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
workflow_error = WorkflowError(
|
|
17
|
+
message="Integration credentials unavailable",
|
|
18
|
+
code=WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE,
|
|
19
|
+
raw_data=raw_data,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# WHEN we convert to VellumError
|
|
23
|
+
vellum_error = workflow_error_to_vellum_error(workflow_error)
|
|
24
|
+
|
|
25
|
+
# THEN the raw_data should be preserved
|
|
26
|
+
assert vellum_error.raw_data == raw_data
|
|
27
|
+
|
|
28
|
+
# AND the integration details should be accessible
|
|
29
|
+
raw_data_from_error = vellum_error.raw_data
|
|
30
|
+
assert raw_data_from_error is not None
|
|
31
|
+
integration = raw_data_from_error.get("integration")
|
|
32
|
+
assert isinstance(integration, dict)
|
|
33
|
+
assert integration["name"] == "GITHUB"
|
|
34
|
+
assert integration["provider"] == "COMPOSIO"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_workflow_error_to_vellum_error__handles_none_raw_data():
|
|
38
|
+
"""
|
|
39
|
+
Tests that None raw_data is handled correctly when converting WorkflowError to VellumError.
|
|
40
|
+
"""
|
|
41
|
+
# GIVEN a WorkflowError with no raw_data
|
|
42
|
+
workflow_error = WorkflowError(
|
|
43
|
+
message="Some error",
|
|
44
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
|
45
|
+
raw_data=None,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# WHEN we convert to VellumError
|
|
49
|
+
vellum_error = workflow_error_to_vellum_error(workflow_error)
|
|
50
|
+
|
|
51
|
+
# THEN the raw_data should be None
|
|
52
|
+
assert vellum_error.raw_data is None
|
vellum/workflows/errors/types.py
CHANGED
vellum/workflows/events/node.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from typing import TYPE_CHECKING, Any, Dict, Generic, List, Literal, Optional, Set, Type, Union
|
|
2
3
|
|
|
3
4
|
from pydantic import SerializationInfo, field_serializer, model_serializer
|
|
4
5
|
|
|
5
6
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
|
7
|
+
from vellum.client.types import SeverityEnum
|
|
8
|
+
from vellum.utils.json_encoder import VellumJsonEncoder
|
|
6
9
|
from vellum.workflows.errors import WorkflowError
|
|
7
10
|
from vellum.workflows.expressions.accessor import AccessorExpression
|
|
8
11
|
from vellum.workflows.outputs.base import BaseOutput
|
|
@@ -136,6 +139,15 @@ class NodeExecutionFulfilledEvent(_BaseNodeEvent, Generic[OutputsType]):
|
|
|
136
139
|
and serialized["body"]["invoked_ports"] is None
|
|
137
140
|
):
|
|
138
141
|
del serialized["body"]["invoked_ports"]
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
self._event_max_size is not None
|
|
145
|
+
and len(json.dumps(serialized, cls=VellumJsonEncoder)) > self._event_max_size
|
|
146
|
+
and "body" in serialized
|
|
147
|
+
and isinstance(serialized["body"], dict)
|
|
148
|
+
):
|
|
149
|
+
serialized["body"]["outputs"] = {}
|
|
150
|
+
|
|
139
151
|
return serialized
|
|
140
152
|
|
|
141
153
|
|
|
@@ -171,6 +183,17 @@ class NodeExecutionResumedEvent(_BaseNodeEvent):
|
|
|
171
183
|
body: NodeExecutionResumedBody
|
|
172
184
|
|
|
173
185
|
|
|
186
|
+
class NodeExecutionLogBody(_BaseNodeExecutionBody):
|
|
187
|
+
attributes: Optional[Dict[str, Any]] = None
|
|
188
|
+
severity: SeverityEnum
|
|
189
|
+
message: str
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class NodeExecutionLogEvent(_BaseNodeEvent):
|
|
193
|
+
name: Literal["node.execution.log"] = "node.execution.log"
|
|
194
|
+
body: NodeExecutionLogBody
|
|
195
|
+
|
|
196
|
+
|
|
174
197
|
NodeEvent = Union[
|
|
175
198
|
NodeExecutionInitiatedEvent,
|
|
176
199
|
NodeExecutionStreamingEvent,
|
|
@@ -178,4 +201,5 @@ NodeEvent = Union[
|
|
|
178
201
|
NodeExecutionRejectedEvent,
|
|
179
202
|
NodeExecutionPausedEvent,
|
|
180
203
|
NodeExecutionResumedEvent,
|
|
204
|
+
NodeExecutionLogEvent,
|
|
181
205
|
]
|