vellum-ai 1.7.10__py3-none-any.whl → 1.7.12__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 +2 -0
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/types/__init__.py +2 -0
- vellum/client/types/auth_type_enum.py +5 -0
- vellum/client/types/integration_name.py +4 -0
- vellum/client/types/slim_integration_auth_config_read.py +2 -0
- vellum/client/types/slim_workflow_execution_read.py +3 -3
- vellum/client/types/vellum_error_code_enum.py +1 -0
- vellum/client/types/vellum_sdk_error_code_enum.py +1 -0
- vellum/client/types/workflow_event_execution_read.py +3 -3
- vellum/client/types/workflow_execution_event_error_code.py +1 -0
- vellum/client/types/workflow_execution_snapshotted_body.py +1 -0
- vellum/types/auth_type_enum.py +3 -0
- vellum/workflows/events/tests/test_event.py +1 -0
- vellum/workflows/events/workflow.py +3 -0
- vellum/workflows/exceptions.py +3 -0
- vellum/workflows/integrations/mcp_service.py +7 -0
- vellum/workflows/integrations/tests/test_mcp_service.py +48 -0
- vellum/workflows/loaders/__init__.py +3 -0
- vellum/workflows/loaders/base.py +21 -0
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +3 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +3 -0
- vellum/workflows/tests/triggers/test_vellum_integration_trigger.py +225 -0
- vellum/workflows/triggers/__init__.py +2 -1
- vellum/workflows/triggers/vellum_integration.py +383 -0
- vellum/workflows/types/__init__.py +3 -0
- vellum/workflows/types/tests/test_utils.py +11 -0
- vellum/workflows/types/trigger_exec_config.py +63 -0
- vellum/workflows/types/utils.py +22 -0
- vellum/workflows/utils/names.py +20 -0
- vellum/workflows/workflows/base.py +13 -1
- {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/METADATA +1 -1
- {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/RECORD +45 -37
- vellum_cli/pull.py +6 -5
- vellum_cli/push.py +35 -2
- vellum_cli/tests/test_push.py +122 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +65 -0
- vellum_ee/workflows/display/utils/events.py +6 -3
- vellum_ee/workflows/display/utils/tests/test_events.py +29 -0
- vellum_ee/workflows/server/virtual_file_loader.py +15 -4
- vellum_ee/workflows/tests/test_serialize_module.py +48 -0
- vellum_ee/workflows/tests/test_server.py +105 -0
- {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/LICENSE +0 -0
- {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/WHEEL +0 -0
- {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/entry_points.txt +0 -0
|
@@ -4,11 +4,14 @@ import re
|
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
|
+
from vellum.workflows.loaders.base import BaseWorkflowFinder
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class VirtualFileLoader(importlib.abc.Loader):
|
|
9
|
-
def __init__(self, files: dict[str, str], namespace: str):
|
|
11
|
+
def __init__(self, files: dict[str, str], namespace: str, source_module: Optional[str] = None):
|
|
10
12
|
self.files = files
|
|
11
13
|
self.namespace = namespace
|
|
14
|
+
self.source_module = source_module
|
|
12
15
|
|
|
13
16
|
def create_module(self, spec: ModuleSpec):
|
|
14
17
|
"""
|
|
@@ -65,9 +68,17 @@ class VirtualFileLoader(importlib.abc.Loader):
|
|
|
65
68
|
return self.files.get(file_key_name)
|
|
66
69
|
|
|
67
70
|
|
|
68
|
-
class VirtualFileFinder(
|
|
69
|
-
def __init__(self, files: dict[str, str], namespace: str):
|
|
70
|
-
self.loader = VirtualFileLoader(files, namespace)
|
|
71
|
+
class VirtualFileFinder(BaseWorkflowFinder):
|
|
72
|
+
def __init__(self, files: dict[str, str], namespace: str, source_module: Optional[str] = None):
|
|
73
|
+
self.loader = VirtualFileLoader(files, namespace, source_module)
|
|
74
|
+
self.source_module = source_module
|
|
75
|
+
self.namespace = namespace
|
|
76
|
+
|
|
77
|
+
def format_error_message(self, error_message: str) -> str:
|
|
78
|
+
"""Format error message by replacing namespace with source_module."""
|
|
79
|
+
if self.source_module and self.namespace in error_message:
|
|
80
|
+
return error_message.replace(self.namespace, self.source_module)
|
|
81
|
+
return error_message
|
|
71
82
|
|
|
72
83
|
def find_spec(self, fullname: str, path, target=None):
|
|
73
84
|
module_info = self.loader._resolve_module(fullname)
|
|
@@ -96,6 +96,54 @@ def test_serialize_module_includes_additional_files():
|
|
|
96
96
|
assert "CONSTANT_VALUE" in additional_files["utils/constants.py"]
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def test_serialize_module_with_pydantic_array():
|
|
100
|
+
"""
|
|
101
|
+
Test that serialize_module correctly serializes arrays of Pydantic models in workflow inputs.
|
|
102
|
+
|
|
103
|
+
This test verifies that when a workflow has inputs containing a List[PydanticModel],
|
|
104
|
+
the serialization properly converts the Pydantic models to JSON format.
|
|
105
|
+
"""
|
|
106
|
+
module_path = "tests.workflows.pydantic_array_serialization"
|
|
107
|
+
|
|
108
|
+
# WHEN we serialize it
|
|
109
|
+
result = BaseWorkflowDisplay.serialize_module(module_path)
|
|
110
|
+
|
|
111
|
+
assert hasattr(result, "exec_config")
|
|
112
|
+
assert hasattr(result, "errors")
|
|
113
|
+
assert isinstance(result.exec_config, dict)
|
|
114
|
+
assert isinstance(result.errors, list)
|
|
115
|
+
|
|
116
|
+
input_variables = result.exec_config["input_variables"]
|
|
117
|
+
assert len(input_variables) == 1
|
|
118
|
+
|
|
119
|
+
items_input = input_variables[0]
|
|
120
|
+
assert items_input["key"] == "items"
|
|
121
|
+
assert items_input["type"] == "JSON"
|
|
122
|
+
# TODO: In the future, this should be a custom type based on an OpenAPI schema (important-comment)
|
|
123
|
+
|
|
124
|
+
assert result.dataset is not None
|
|
125
|
+
assert isinstance(result.dataset, list)
|
|
126
|
+
assert len(result.dataset) == 2
|
|
127
|
+
|
|
128
|
+
first_scenario = result.dataset[0]
|
|
129
|
+
assert first_scenario["label"] == "Scenario 1"
|
|
130
|
+
assert "items" in first_scenario["inputs"]
|
|
131
|
+
items = first_scenario["inputs"]["items"]
|
|
132
|
+
assert isinstance(items, list)
|
|
133
|
+
assert len(items) == 3
|
|
134
|
+
assert items[0]["name"] == "item1"
|
|
135
|
+
assert items[0]["value"] == 10
|
|
136
|
+
assert items[0]["is_active"] is True
|
|
137
|
+
|
|
138
|
+
second_scenario = result.dataset[1]
|
|
139
|
+
assert second_scenario["label"] == "Custom Test"
|
|
140
|
+
assert "items" in second_scenario["inputs"]
|
|
141
|
+
test_items = second_scenario["inputs"]["items"]
|
|
142
|
+
assert len(test_items) == 2
|
|
143
|
+
assert test_items[0]["name"] == "test1"
|
|
144
|
+
assert test_items[0]["value"] == 100
|
|
145
|
+
|
|
146
|
+
|
|
99
147
|
def test_serialize_module__with_invalid_nested_set_graph(temp_module_path):
|
|
100
148
|
"""
|
|
101
149
|
Tests that serialize_module raises a clear user-facing exception for workflows with nested sets in graph attribute.
|
|
@@ -584,6 +584,111 @@ class BrokenNode(BaseNode):
|
|
|
584
584
|
assert "UndefinedClass" in error_message or "not defined" in error_message
|
|
585
585
|
|
|
586
586
|
|
|
587
|
+
def test_load_from_module__module_not_found_error():
|
|
588
|
+
"""
|
|
589
|
+
Tests that a ModuleNotFoundError raises WorkflowInitializationException with user-facing message.
|
|
590
|
+
"""
|
|
591
|
+
# GIVEN a workflow module that imports a non-existent module
|
|
592
|
+
files = {
|
|
593
|
+
"__init__.py": "",
|
|
594
|
+
"workflow.py": """\
|
|
595
|
+
from vellum.workflows import BaseWorkflow
|
|
596
|
+
from .non_existent_module import SomeClass
|
|
597
|
+
|
|
598
|
+
class Workflow(BaseWorkflow):
|
|
599
|
+
graph = None
|
|
600
|
+
""",
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
namespace = str(uuid4())
|
|
604
|
+
|
|
605
|
+
# AND the virtual file loader is registered
|
|
606
|
+
sys.meta_path.append(VirtualFileFinder(files, namespace, source_module="test"))
|
|
607
|
+
|
|
608
|
+
# WHEN we attempt to load the workflow
|
|
609
|
+
# THEN it should raise WorkflowInitializationException
|
|
610
|
+
with pytest.raises(WorkflowInitializationException) as exc_info:
|
|
611
|
+
BaseWorkflow.load_from_module(namespace)
|
|
612
|
+
|
|
613
|
+
# AND the error message should be user-friendly and show source_module instead of namespace
|
|
614
|
+
error_message = str(exc_info.value)
|
|
615
|
+
assert error_message == "Workflow module not found: No module named 'test.non_existent_module'"
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def test_load_from_module__module_not_found_error_with_external_package():
|
|
619
|
+
"""
|
|
620
|
+
Tests that when ModuleNotFoundError occurs for an external package (not containing the namespace),
|
|
621
|
+
the exception includes vellum_on_error_action set to CREATE_CUSTOM_IMAGE in raw_data.
|
|
622
|
+
"""
|
|
623
|
+
|
|
624
|
+
# GIVEN a workflow module that imports a non-existent external package
|
|
625
|
+
files = {
|
|
626
|
+
"__init__.py": "",
|
|
627
|
+
"workflow.py": """\
|
|
628
|
+
from vellum.workflows import BaseWorkflow
|
|
629
|
+
import some_external_package
|
|
630
|
+
|
|
631
|
+
class Workflow(BaseWorkflow):
|
|
632
|
+
graph = None
|
|
633
|
+
""",
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
namespace = str(uuid4())
|
|
637
|
+
|
|
638
|
+
# AND the virtual file loader is registered
|
|
639
|
+
finder = VirtualFileFinder(files, namespace, source_module="test")
|
|
640
|
+
sys.meta_path.append(finder)
|
|
641
|
+
|
|
642
|
+
# WHEN we attempt to load the workflow
|
|
643
|
+
# THEN it should raise WorkflowInitializationException
|
|
644
|
+
with pytest.raises(WorkflowInitializationException) as exc_info:
|
|
645
|
+
BaseWorkflow.load_from_module(namespace)
|
|
646
|
+
|
|
647
|
+
# AND the error message should be user-friendly
|
|
648
|
+
error_message = str(exc_info.value)
|
|
649
|
+
assert "Workflow module not found:" in error_message
|
|
650
|
+
assert "some_external_package" in error_message
|
|
651
|
+
|
|
652
|
+
assert exc_info.value.raw_data is not None
|
|
653
|
+
assert exc_info.value.raw_data["vellum_on_error_action"] == "CREATE_CUSTOM_IMAGE"
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def test_load_from_module__module_not_found_error_with_internal_package():
|
|
657
|
+
"""
|
|
658
|
+
Tests that when ModuleNotFoundError occurs for an internal module (containing the namespace),
|
|
659
|
+
the exception does NOT include vellum_on_error_action in raw_data.
|
|
660
|
+
"""
|
|
661
|
+
|
|
662
|
+
# GIVEN a workflow module that imports a non-existent internal module
|
|
663
|
+
files = {
|
|
664
|
+
"__init__.py": "",
|
|
665
|
+
"workflow.py": """\
|
|
666
|
+
from vellum.workflows import BaseWorkflow
|
|
667
|
+
from .non_existent_module import SomeClass
|
|
668
|
+
|
|
669
|
+
class Workflow(BaseWorkflow):
|
|
670
|
+
graph = None
|
|
671
|
+
""",
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
namespace = str(uuid4())
|
|
675
|
+
|
|
676
|
+
# AND the virtual file loader is registered
|
|
677
|
+
finder = VirtualFileFinder(files, namespace, source_module="test")
|
|
678
|
+
sys.meta_path.append(finder)
|
|
679
|
+
|
|
680
|
+
# WHEN we attempt to load the workflow
|
|
681
|
+
# THEN it should raise WorkflowInitializationException
|
|
682
|
+
with pytest.raises(WorkflowInitializationException) as exc_info:
|
|
683
|
+
BaseWorkflow.load_from_module(namespace)
|
|
684
|
+
|
|
685
|
+
# AND the error message should be user-friendly
|
|
686
|
+
error_message = str(exc_info.value)
|
|
687
|
+
assert "Workflow module not found:" in error_message
|
|
688
|
+
|
|
689
|
+
assert exc_info.value.raw_data is None
|
|
690
|
+
|
|
691
|
+
|
|
587
692
|
def test_serialize_module__tool_calling_node_with_single_tool():
|
|
588
693
|
"""Test that serialize_module works with a tool calling node that has a single tool."""
|
|
589
694
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|