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.

Files changed (45) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +2 -2
  3. vellum/client/types/__init__.py +2 -0
  4. vellum/client/types/auth_type_enum.py +5 -0
  5. vellum/client/types/integration_name.py +4 -0
  6. vellum/client/types/slim_integration_auth_config_read.py +2 -0
  7. vellum/client/types/slim_workflow_execution_read.py +3 -3
  8. vellum/client/types/vellum_error_code_enum.py +1 -0
  9. vellum/client/types/vellum_sdk_error_code_enum.py +1 -0
  10. vellum/client/types/workflow_event_execution_read.py +3 -3
  11. vellum/client/types/workflow_execution_event_error_code.py +1 -0
  12. vellum/client/types/workflow_execution_snapshotted_body.py +1 -0
  13. vellum/types/auth_type_enum.py +3 -0
  14. vellum/workflows/events/tests/test_event.py +1 -0
  15. vellum/workflows/events/workflow.py +3 -0
  16. vellum/workflows/exceptions.py +3 -0
  17. vellum/workflows/integrations/mcp_service.py +7 -0
  18. vellum/workflows/integrations/tests/test_mcp_service.py +48 -0
  19. vellum/workflows/loaders/__init__.py +3 -0
  20. vellum/workflows/loaders/base.py +21 -0
  21. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +3 -0
  22. vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +3 -0
  23. vellum/workflows/tests/triggers/test_vellum_integration_trigger.py +225 -0
  24. vellum/workflows/triggers/__init__.py +2 -1
  25. vellum/workflows/triggers/vellum_integration.py +383 -0
  26. vellum/workflows/types/__init__.py +3 -0
  27. vellum/workflows/types/tests/test_utils.py +11 -0
  28. vellum/workflows/types/trigger_exec_config.py +63 -0
  29. vellum/workflows/types/utils.py +22 -0
  30. vellum/workflows/utils/names.py +20 -0
  31. vellum/workflows/workflows/base.py +13 -1
  32. {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/METADATA +1 -1
  33. {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/RECORD +45 -37
  34. vellum_cli/pull.py +6 -5
  35. vellum_cli/push.py +35 -2
  36. vellum_cli/tests/test_push.py +122 -0
  37. vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +65 -0
  38. vellum_ee/workflows/display/utils/events.py +6 -3
  39. vellum_ee/workflows/display/utils/tests/test_events.py +29 -0
  40. vellum_ee/workflows/server/virtual_file_loader.py +15 -4
  41. vellum_ee/workflows/tests/test_serialize_module.py +48 -0
  42. vellum_ee/workflows/tests/test_server.py +105 -0
  43. {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/LICENSE +0 -0
  44. {vellum_ai-1.7.10.dist-info → vellum_ai-1.7.12.dist-info}/WHEEL +0 -0
  45. {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(importlib.abc.MetaPathFinder, importlib.abc.Loader):
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