vellum-ai 1.8.5__py3-none-any.whl → 1.9.0__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.
- vellum/__init__.py +6 -0
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/types/__init__.py +6 -0
- vellum/client/types/api_actor_type_enum.py +7 -0
- vellum/client/types/api_request_parent_context.py +6 -0
- vellum/client/types/external_parent_context.py +2 -0
- vellum/client/types/integration_name.py +1 -0
- vellum/client/types/integration_trigger_context.py +38 -0
- vellum/client/types/node_execution_fulfilled_event.py +2 -0
- vellum/client/types/node_execution_initiated_event.py +2 -0
- vellum/client/types/node_execution_paused_event.py +2 -0
- vellum/client/types/node_execution_rejected_event.py +2 -0
- vellum/client/types/node_execution_resumed_event.py +2 -0
- vellum/client/types/node_execution_span.py +2 -0
- vellum/client/types/node_execution_streaming_event.py +2 -0
- vellum/client/types/node_parent_context.py +2 -0
- vellum/client/types/parent_context.py +4 -0
- vellum/client/types/prompt_deployment_parent_context.py +2 -0
- vellum/client/types/scheduled_trigger_context.py +38 -0
- vellum/client/types/slim_workflow_execution_read.py +2 -0
- vellum/client/types/span_link.py +2 -0
- vellum/client/types/workflow_deployment_event_executions_response.py +2 -0
- vellum/client/types/workflow_deployment_parent_context.py +2 -0
- vellum/client/types/workflow_event_execution_read.py +2 -0
- vellum/client/types/workflow_execution_detail.py +2 -0
- vellum/client/types/workflow_execution_fulfilled_event.py +2 -0
- vellum/client/types/workflow_execution_initiated_event.py +2 -0
- vellum/client/types/workflow_execution_paused_event.py +2 -0
- vellum/client/types/workflow_execution_rejected_event.py +2 -0
- vellum/client/types/workflow_execution_resumed_event.py +2 -0
- vellum/client/types/workflow_execution_snapshotted_event.py +2 -0
- vellum/client/types/workflow_execution_span.py +2 -0
- vellum/client/types/workflow_execution_streaming_event.py +2 -0
- vellum/client/types/workflow_parent_context.py +2 -0
- vellum/client/types/workflow_sandbox_parent_context.py +2 -0
- vellum/types/api_actor_type_enum.py +3 -0
- vellum/types/integration_trigger_context.py +3 -0
- vellum/types/scheduled_trigger_context.py +3 -0
- vellum/workflows/inputs/dataset_row.py +9 -7
- vellum/workflows/nodes/displayable/final_output_node/node.py +4 -0
- vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +28 -0
- vellum/workflows/nodes/displayable/set_state_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/set_state_node/node.py +71 -0
- vellum/workflows/nodes/displayable/set_state_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/displayable/set_state_node/tests/test_node.py +212 -0
- vellum/workflows/sandbox.py +13 -3
- vellum/workflows/tests/test_dataset_row.py +20 -0
- vellum/workflows/tests/test_sandbox.py +40 -0
- vellum/workflows/tests/triggers/{test_vellum_integration_trigger.py → test_integration_trigger.py} +22 -22
- vellum/workflows/triggers/__init__.py +2 -2
- vellum/workflows/triggers/base.py +22 -4
- vellum/workflows/triggers/integration.py +168 -49
- vellum/workflows/triggers/schedule.py +18 -0
- vellum/workflows/triggers/tests/test_integration.py +49 -20
- vellum/workflows/utils/uuids.py +1 -15
- vellum/workflows/workflows/base.py +44 -0
- {vellum_ai-1.8.5.dist-info → vellum_ai-1.9.0.dist-info}/METADATA +1 -1
- {vellum_ai-1.8.5.dist-info → vellum_ai-1.9.0.dist-info}/RECORD +67 -57
- vellum_ee/workflows/display/tests/workflow_serialization/{test_vellum_integration_trigger_serialization.py → test_integration_trigger_serialization.py} +8 -8
- vellum_ee/workflows/display/utils/expressions.py +2 -3
- vellum_ee/workflows/display/workflows/base_workflow_display.py +9 -9
- vellum_ee/workflows/server/virtual_file_loader.py +74 -2
- vellum_ee/workflows/tests/test_server.py +81 -66
- vellum_ee/workflows/tests/test_virtual_files.py +48 -0
- vellum/workflows/triggers/vellum_integration.py +0 -189
- {vellum_ai-1.8.5.dist-info → vellum_ai-1.9.0.dist-info}/LICENSE +0 -0
- {vellum_ai-1.8.5.dist-info → vellum_ai-1.9.0.dist-info}/WHEEL +0 -0
- {vellum_ai-1.8.5.dist-info → vellum_ai-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,67 +1,186 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
|
-
from typing import ClassVar,
|
|
2
|
+
from typing import Any, ClassVar, Dict
|
|
3
3
|
|
|
4
|
-
from vellum.workflows.
|
|
4
|
+
from vellum.workflows.constants import VellumIntegrationProviderType
|
|
5
|
+
from vellum.workflows.references.trigger import TriggerAttributeReference
|
|
6
|
+
from vellum.workflows.triggers.base import BaseTrigger, BaseTriggerMeta
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
class
|
|
9
|
+
class IntegrationTriggerMeta(BaseTriggerMeta):
|
|
8
10
|
"""
|
|
9
|
-
|
|
11
|
+
Custom metaclass for IntegrationTrigger.
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
This metaclass extends BaseTriggerMeta to automatically convert type annotations
|
|
14
|
+
into TriggerAttributeReference objects during class creation. This enables trigger
|
|
15
|
+
attributes to be referenced in workflow graphs while maintaining type safety.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __new__(mcs, name: str, bases: tuple, namespace: dict, **kwargs: Any) -> "IntegrationTriggerMeta":
|
|
19
|
+
"""Create a new trigger class and set up attribute references."""
|
|
20
|
+
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
|
|
21
|
+
|
|
22
|
+
# Process __annotations__ to create TriggerAttributeReference for each attribute
|
|
23
|
+
# Only process if class has Config and annotations
|
|
24
|
+
has_config = hasattr(cls, "Config") and "Config" in namespace
|
|
25
|
+
if has_config and hasattr(cls, "__annotations__"):
|
|
26
|
+
# Create TriggerAttributeReference for each annotated attribute
|
|
27
|
+
for attr_name, attr_type in cls.__annotations__.items():
|
|
28
|
+
# Skip special attributes and Config
|
|
29
|
+
if attr_name.startswith("_") or attr_name == "Config":
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
# Create reference with proper type
|
|
33
|
+
reference = TriggerAttributeReference(
|
|
34
|
+
name=attr_name, types=(attr_type,), instance=None, trigger_class=cls
|
|
35
|
+
)
|
|
36
|
+
# Set as class attribute so it's directly accessible
|
|
37
|
+
setattr(cls, attr_name, reference)
|
|
38
|
+
|
|
39
|
+
return cls
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class IntegrationTrigger(BaseTrigger, ABC, metaclass=IntegrationTriggerMeta):
|
|
43
|
+
"""
|
|
44
|
+
Base class for Vellum-managed integration triggers.
|
|
45
|
+
|
|
46
|
+
Subclasses define two types of attributes:
|
|
47
|
+
1. **Config class**: Specifies how the trigger is configured (provider, integration_name, slug)
|
|
48
|
+
- These are configuration details users shouldn't need to interact with directly
|
|
49
|
+
2. **Top-level type annotations**: Define the webhook event payload structure (message, user, channel, etc.)
|
|
50
|
+
- These become TriggerAttributeReference that can be referenced in workflow nodes
|
|
15
51
|
|
|
16
52
|
Examples:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
Create a Slack trigger:
|
|
54
|
+
>>> class SlackNewMessageTrigger(IntegrationTrigger):
|
|
55
|
+
... # Event attributes (webhook payload structure)
|
|
56
|
+
... message: str
|
|
57
|
+
... user: str
|
|
58
|
+
... channel: str
|
|
59
|
+
... timestamp: float
|
|
60
|
+
...
|
|
61
|
+
... # Configuration (how trigger is set up)
|
|
62
|
+
... class Config(IntegrationTrigger.Config):
|
|
63
|
+
... provider = VellumIntegrationProviderType.COMPOSIO
|
|
64
|
+
... integration_name = "SLACK"
|
|
65
|
+
... slug = "slack_new_message"
|
|
66
|
+
|
|
67
|
+
Use in workflow graph:
|
|
68
|
+
>>> class MyWorkflow(BaseWorkflow):
|
|
69
|
+
... graph = SlackNewMessageTrigger >> ProcessMessageNode
|
|
70
|
+
|
|
71
|
+
Reference trigger attributes in nodes:
|
|
72
|
+
>>> class ProcessNode(BaseNode):
|
|
73
|
+
... class Outputs(BaseNode.Outputs):
|
|
74
|
+
... text = SlackNewMessageTrigger.message
|
|
75
|
+
... channel = SlackNewMessageTrigger.channel
|
|
76
|
+
|
|
77
|
+
Instantiate for testing:
|
|
78
|
+
>>> trigger = SlackNewMessageTrigger(
|
|
79
|
+
... message="Hello world",
|
|
80
|
+
... channel="C123456",
|
|
81
|
+
... user="U123",
|
|
82
|
+
... timestamp=1234567890.0,
|
|
83
|
+
... )
|
|
84
|
+
>>> trigger.message
|
|
85
|
+
'Hello world'
|
|
37
86
|
"""
|
|
38
87
|
|
|
39
|
-
|
|
40
|
-
|
|
88
|
+
class Config:
|
|
89
|
+
"""
|
|
90
|
+
Configuration for IntegrationTrigger subclasses.
|
|
41
91
|
|
|
42
|
-
|
|
92
|
+
Defines how the trigger connects to the integration provider. These settings
|
|
93
|
+
specify which integration and which specific trigger type to use.
|
|
43
94
|
"""
|
|
44
|
-
Initialize trigger with event data from external system.
|
|
45
95
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
96
|
+
provider: ClassVar[VellumIntegrationProviderType]
|
|
97
|
+
integration_name: ClassVar[str]
|
|
98
|
+
slug: ClassVar[str]
|
|
49
99
|
|
|
50
|
-
|
|
51
|
-
|
|
100
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
101
|
+
"""Validate that subclasses define required Config class with all required fields."""
|
|
102
|
+
super().__init_subclass__(**kwargs)
|
|
103
|
+
|
|
104
|
+
# Skip validation for the base class itself
|
|
105
|
+
if cls.__name__ == "IntegrationTrigger":
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
# Require Config class with required fields
|
|
109
|
+
if not hasattr(cls, "Config") or cls.Config is IntegrationTrigger.Config:
|
|
110
|
+
raise TypeError(
|
|
111
|
+
f"{cls.__name__} must define a nested Config class. "
|
|
112
|
+
f"Example:\n"
|
|
113
|
+
f" class {cls.__name__}(IntegrationTrigger):\n"
|
|
114
|
+
f" message: str\n"
|
|
115
|
+
f" class Config(IntegrationTrigger.Config):\n"
|
|
116
|
+
f" provider = VellumIntegrationProviderType.COMPOSIO\n"
|
|
117
|
+
f" integration_name = 'SLACK'\n"
|
|
118
|
+
f" slug = 'slack_new_message'"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Validate Config class has required fields
|
|
122
|
+
config_cls = cls.Config
|
|
123
|
+
required_fields = ["provider", "integration_name", "slug"]
|
|
124
|
+
for field in required_fields:
|
|
125
|
+
if not hasattr(config_cls, field):
|
|
126
|
+
raise TypeError(
|
|
127
|
+
f"{cls.__name__}.Config must define '{field}'. " f"Required fields: {', '.join(required_fields)}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def __init__(self, **kwargs: Any):
|
|
131
|
+
"""
|
|
132
|
+
Initialize trigger with event data from the integration.
|
|
133
|
+
|
|
134
|
+
The trigger dynamically populates its attributes based on the kwargs
|
|
135
|
+
dictionary keys. Any key in kwargs becomes an accessible attribute.
|
|
52
136
|
|
|
53
137
|
Examples:
|
|
54
|
-
>>> class
|
|
55
|
-
...
|
|
138
|
+
>>> class SlackTrigger(IntegrationTrigger):
|
|
139
|
+
... message: str
|
|
140
|
+
... channel: str
|
|
141
|
+
... user: str
|
|
56
142
|
...
|
|
57
|
-
...
|
|
58
|
-
...
|
|
59
|
-
...
|
|
60
|
-
|
|
61
|
-
>>> trigger =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
143
|
+
... class Config(IntegrationTrigger.Config):
|
|
144
|
+
... provider = VellumIntegrationProviderType.COMPOSIO
|
|
145
|
+
... integration_name = "SLACK"
|
|
146
|
+
... slug = "slack_new_message"
|
|
147
|
+
>>> trigger = SlackTrigger(
|
|
148
|
+
... message="Hello",
|
|
149
|
+
... channel="C123",
|
|
150
|
+
... user="U456"
|
|
151
|
+
... )
|
|
152
|
+
>>> trigger.message
|
|
153
|
+
'Hello'
|
|
154
|
+
>>> trigger.channel
|
|
155
|
+
'C123'
|
|
66
156
|
"""
|
|
67
|
-
|
|
157
|
+
super().__init__(**kwargs)
|
|
158
|
+
|
|
159
|
+
# Dynamically populate instance attributes from kwargs.
|
|
160
|
+
# This allows any key in kwargs to become an accessible attribute:
|
|
161
|
+
# kwargs={"message": "Hi"} → trigger.message == "Hi"
|
|
162
|
+
for key, value in kwargs.items():
|
|
163
|
+
setattr(self, key, value)
|
|
164
|
+
|
|
165
|
+
def to_trigger_attribute_values(self) -> Dict["TriggerAttributeReference[Any]", Any]:
|
|
166
|
+
"""
|
|
167
|
+
Materialize attribute descriptor/value pairs for this trigger instance.
|
|
168
|
+
|
|
169
|
+
For IntegrationTrigger, this includes all dynamic attributes from event_data.
|
|
170
|
+
"""
|
|
171
|
+
attribute_values: Dict["TriggerAttributeReference[Any]", Any] = {}
|
|
172
|
+
|
|
173
|
+
# Unlike the base class which iterates over type(self) (predefined annotations),
|
|
174
|
+
# we iterate over event_data keys since our attributes are discovered dynamically
|
|
175
|
+
# from the actual event data received during workflow execution.
|
|
176
|
+
# The base class approach: for reference in type(self)
|
|
177
|
+
# Our approach: for attr_name in self._event_data.keys()
|
|
178
|
+
for attr_name in self._event_data.keys():
|
|
179
|
+
# Get the class-level reference for this attribute (created by __new__ from annotations)
|
|
180
|
+
# Unknown keys can appear in webhook payloads, so gracefully skip them if the
|
|
181
|
+
# trigger class doesn't expose a corresponding reference.
|
|
182
|
+
reference = getattr(type(self), attr_name, None)
|
|
183
|
+
if isinstance(reference, TriggerAttributeReference):
|
|
184
|
+
attribute_values[reference] = getattr(self, attr_name)
|
|
185
|
+
|
|
186
|
+
return attribute_values
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from vellum.workflows.triggers.base import BaseTrigger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ScheduleTrigger(BaseTrigger):
|
|
8
|
+
"""
|
|
9
|
+
Trigger representing time-based workflow invocation.
|
|
10
|
+
Supports Cron-based schedules (e.g., "0 9 * * MON" for every Monday at 9am)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
current_run_at: datetime
|
|
14
|
+
next_run_at: datetime
|
|
15
|
+
|
|
16
|
+
class Config:
|
|
17
|
+
cron: str
|
|
18
|
+
timezone: Optional[str] = None
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"""Tests for IntegrationTrigger base class."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from vellum.workflows.constants import VellumIntegrationProviderType
|
|
3
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
|
4
7
|
from vellum.workflows.references.trigger import TriggerAttributeReference
|
|
5
8
|
from vellum.workflows.state.base import BaseState
|
|
@@ -9,7 +12,7 @@ from vellum.workflows.triggers.integration import IntegrationTrigger
|
|
|
9
12
|
def test_integration_trigger__can_be_instantiated_as_base():
|
|
10
13
|
"""IntegrationTrigger can be instantiated as a base class."""
|
|
11
14
|
# WHEN we instantiate IntegrationTrigger directly
|
|
12
|
-
trigger = IntegrationTrigger(
|
|
15
|
+
trigger = IntegrationTrigger(test="data")
|
|
13
16
|
|
|
14
17
|
# THEN it creates an instance with event data stored
|
|
15
18
|
assert trigger._event_data == {"test": "data"}
|
|
@@ -20,7 +23,10 @@ def test_integration_trigger__can_be_instantiated():
|
|
|
20
23
|
|
|
21
24
|
# GIVEN IntegrationTrigger with concrete implementation
|
|
22
25
|
class TestTrigger(IntegrationTrigger):
|
|
23
|
-
|
|
26
|
+
class Config(IntegrationTrigger.Config):
|
|
27
|
+
provider = VellumIntegrationProviderType.COMPOSIO
|
|
28
|
+
integration_name = "test"
|
|
29
|
+
slug = "test"
|
|
24
30
|
|
|
25
31
|
# THEN it can be instantiated (even though base is ABC, concrete subclasses work)
|
|
26
32
|
assert TestTrigger is not None
|
|
@@ -33,12 +39,17 @@ def test_integration_trigger__can_be_subclassed():
|
|
|
33
39
|
class TestTrigger(IntegrationTrigger):
|
|
34
40
|
data: str
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
class Config(IntegrationTrigger.Config):
|
|
43
|
+
provider = VellumIntegrationProviderType.COMPOSIO
|
|
44
|
+
integration_name = "test"
|
|
45
|
+
slug = "test"
|
|
46
|
+
|
|
47
|
+
def __init__(self, **kwargs: Any):
|
|
48
|
+
super().__init__(**kwargs)
|
|
49
|
+
self.data = kwargs.get("data", "")
|
|
39
50
|
|
|
40
51
|
# WHEN we create a trigger instance
|
|
41
|
-
result = TestTrigger(
|
|
52
|
+
result = TestTrigger(data="test")
|
|
42
53
|
|
|
43
54
|
# THEN it returns the expected trigger instance with populated attributes
|
|
44
55
|
assert result.data == "test"
|
|
@@ -50,9 +61,14 @@ def test_integration_trigger__attribute_reference():
|
|
|
50
61
|
class TestTrigger(IntegrationTrigger):
|
|
51
62
|
value: str
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
class Config(IntegrationTrigger.Config):
|
|
65
|
+
provider = VellumIntegrationProviderType.COMPOSIO
|
|
66
|
+
integration_name = "test"
|
|
67
|
+
slug = "test"
|
|
68
|
+
|
|
69
|
+
def __init__(self, **kwargs: Any):
|
|
70
|
+
super().__init__(**kwargs)
|
|
71
|
+
self.value = kwargs.get("value", "")
|
|
56
72
|
|
|
57
73
|
reference = TestTrigger.value
|
|
58
74
|
assert isinstance(reference, TriggerAttributeReference)
|
|
@@ -61,7 +77,7 @@ def test_integration_trigger__attribute_reference():
|
|
|
61
77
|
assert reference == TestTrigger.attribute_references()["value"]
|
|
62
78
|
|
|
63
79
|
state = BaseState()
|
|
64
|
-
trigger = TestTrigger(
|
|
80
|
+
trigger = TestTrigger(value="data")
|
|
65
81
|
trigger.bind_to_state(state)
|
|
66
82
|
assert reference.resolve(state) == "data"
|
|
67
83
|
|
|
@@ -73,9 +89,14 @@ def test_integration_trigger__graph_syntax():
|
|
|
73
89
|
class TestTrigger(IntegrationTrigger):
|
|
74
90
|
value: str
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
class Config(IntegrationTrigger.Config):
|
|
93
|
+
provider = VellumIntegrationProviderType.COMPOSIO
|
|
94
|
+
integration_name = "test"
|
|
95
|
+
slug = "test"
|
|
96
|
+
|
|
97
|
+
def __init__(self, **kwargs: Any):
|
|
98
|
+
super().__init__(**kwargs)
|
|
99
|
+
self.value = kwargs.get("value", "")
|
|
79
100
|
|
|
80
101
|
class TestNode(BaseNode):
|
|
81
102
|
pass
|
|
@@ -97,9 +118,14 @@ def test_integration_trigger__multiple_entrypoints():
|
|
|
97
118
|
class TestTrigger(IntegrationTrigger):
|
|
98
119
|
msg: str
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
class Config(IntegrationTrigger.Config):
|
|
122
|
+
provider = VellumIntegrationProviderType.COMPOSIO
|
|
123
|
+
integration_name = "test"
|
|
124
|
+
slug = "test"
|
|
125
|
+
|
|
126
|
+
def __init__(self, **kwargs: Any):
|
|
127
|
+
super().__init__(**kwargs)
|
|
128
|
+
self.msg = kwargs.get("msg", "")
|
|
103
129
|
|
|
104
130
|
class NodeA(BaseNode):
|
|
105
131
|
pass
|
|
@@ -118,9 +144,12 @@ def test_integration_trigger__multiple_entrypoints():
|
|
|
118
144
|
|
|
119
145
|
|
|
120
146
|
def test_integration_trigger__config_attribute():
|
|
121
|
-
"""IntegrationTrigger
|
|
147
|
+
"""IntegrationTrigger requires Config class with provider, integration_name, and slug."""
|
|
122
148
|
|
|
123
149
|
# GIVEN IntegrationTrigger
|
|
124
|
-
# THEN it has a
|
|
125
|
-
assert hasattr(IntegrationTrigger, "
|
|
126
|
-
assert IntegrationTrigger.
|
|
150
|
+
# THEN it has a Config class with type annotations for required fields
|
|
151
|
+
assert hasattr(IntegrationTrigger, "Config")
|
|
152
|
+
assert hasattr(IntegrationTrigger.Config, "__annotations__")
|
|
153
|
+
assert "provider" in IntegrationTrigger.Config.__annotations__
|
|
154
|
+
assert "integration_name" in IntegrationTrigger.Config.__annotations__
|
|
155
|
+
assert "slug" in IntegrationTrigger.Config.__annotations__
|
vellum/workflows/utils/uuids.py
CHANGED
|
@@ -43,20 +43,6 @@ def uuid4_from_hash(input_str: str) -> UUID:
|
|
|
43
43
|
return UUID(bytes=bytes(hash_list))
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def get_trigger_id(trigger_class: "type[BaseTrigger]") -> UUID:
|
|
47
|
-
"""
|
|
48
|
-
Generate a deterministic trigger ID from a trigger class using
|
|
49
|
-
the class's module name and __qualname__ to ensure stability and uniqueness.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
trigger_class: The trigger class to generate an ID for
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
A deterministic UUID based on the trigger class module and qualname
|
|
56
|
-
"""
|
|
57
|
-
return uuid4_from_hash(f"{trigger_class.__module__}.{trigger_class.__qualname__}")
|
|
58
|
-
|
|
59
|
-
|
|
60
46
|
def get_trigger_attribute_id(trigger_class: "type[BaseTrigger]", attribute_name: str) -> UUID:
|
|
61
47
|
"""
|
|
62
48
|
Generate a deterministic trigger attribute ID from a trigger class and attribute name
|
|
@@ -69,5 +55,5 @@ def get_trigger_attribute_id(trigger_class: "type[BaseTrigger]", attribute_name:
|
|
|
69
55
|
Returns:
|
|
70
56
|
A deterministic UUID based on the trigger class module, qualname, and attribute name
|
|
71
57
|
"""
|
|
72
|
-
trigger_id =
|
|
58
|
+
trigger_id = trigger_class.__id__
|
|
73
59
|
return uuid4_from_hash(f"{trigger_id}|{attribute_name}")
|
|
@@ -714,6 +714,50 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
|
|
|
714
714
|
|
|
715
715
|
return state_class(**state)
|
|
716
716
|
|
|
717
|
+
@classmethod
|
|
718
|
+
def deserialize_trigger(cls, trigger_id: Optional[UUID], inputs: dict) -> Union[InputsType, BaseTrigger]:
|
|
719
|
+
"""
|
|
720
|
+
Deserialize a trigger from a trigger_id and inputs dict.
|
|
721
|
+
|
|
722
|
+
If trigger_id is None, returns an instance of the workflow's Inputs class.
|
|
723
|
+
Otherwise, finds a trigger class that matches the trigger_id and creates an instance of that.
|
|
724
|
+
|
|
725
|
+
Parameters
|
|
726
|
+
----------
|
|
727
|
+
trigger_id: Optional[UUID]
|
|
728
|
+
The UUID of the trigger class to instantiate. If None, returns workflow Inputs.
|
|
729
|
+
|
|
730
|
+
inputs: dict
|
|
731
|
+
The inputs to pass to the trigger or Inputs constructor.
|
|
732
|
+
|
|
733
|
+
Returns
|
|
734
|
+
-------
|
|
735
|
+
Union[InputsType, BaseTrigger]
|
|
736
|
+
Either an instance of the workflow's Inputs class (if trigger_id is None)
|
|
737
|
+
or an instance of the matching trigger class.
|
|
738
|
+
|
|
739
|
+
Raises
|
|
740
|
+
------
|
|
741
|
+
WorkflowInitializationException
|
|
742
|
+
If trigger_id is provided but no matching trigger class is found in the workflow.
|
|
743
|
+
"""
|
|
744
|
+
if trigger_id is None:
|
|
745
|
+
inputs_class = cls.get_inputs_class()
|
|
746
|
+
return inputs_class(**inputs)
|
|
747
|
+
|
|
748
|
+
trigger_classes = []
|
|
749
|
+
for subgraph in cls.get_subgraphs():
|
|
750
|
+
for trigger_class in subgraph.triggers:
|
|
751
|
+
if trigger_class.__id__ == trigger_id:
|
|
752
|
+
return trigger_class(**inputs)
|
|
753
|
+
|
|
754
|
+
trigger_classes.append(trigger_class)
|
|
755
|
+
|
|
756
|
+
raise WorkflowInitializationException(
|
|
757
|
+
message=f"No trigger class found with id {trigger_id} in workflow {cls.__name__}. "
|
|
758
|
+
f"Available trigger classes: {[trigger_class.__name__ for trigger_class in trigger_classes]}"
|
|
759
|
+
)
|
|
760
|
+
|
|
717
761
|
@staticmethod
|
|
718
762
|
def load_from_module(module_path: str) -> Type["BaseWorkflow"]:
|
|
719
763
|
workflow_path = f"{module_path}.workflow"
|