zenml-nightly 0.58.2.dev20240624__py3-none-any.whl → 0.58.2.dev20240625__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.
Files changed (48) hide show
  1. zenml/VERSION +1 -1
  2. zenml/actions/base_action.py +177 -174
  3. zenml/actions/pipeline_run/pipeline_run_action.py +28 -23
  4. zenml/client.py +226 -55
  5. zenml/config/compiler.py +10 -9
  6. zenml/config/docker_settings.py +25 -9
  7. zenml/constants.py +1 -1
  8. zenml/event_hub/base_event_hub.py +5 -5
  9. zenml/event_hub/event_hub.py +15 -6
  10. zenml/event_sources/base_event.py +0 -11
  11. zenml/event_sources/base_event_source.py +7 -0
  12. zenml/event_sources/webhooks/base_webhook_event_source.py +1 -4
  13. zenml/exceptions.py +4 -0
  14. zenml/hooks/hook_validators.py +2 -3
  15. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +3 -3
  16. zenml/integrations/mlflow/__init__.py +1 -1
  17. zenml/models/__init__.py +17 -0
  18. zenml/models/v2/core/action.py +276 -0
  19. zenml/models/v2/core/trigger.py +182 -141
  20. zenml/new/pipelines/pipeline.py +13 -3
  21. zenml/new/pipelines/pipeline_decorator.py +1 -2
  22. zenml/new/pipelines/run_utils.py +1 -12
  23. zenml/new/steps/step_decorator.py +2 -3
  24. zenml/pipelines/base_pipeline.py +0 -2
  25. zenml/pipelines/pipeline_decorator.py +1 -2
  26. zenml/steps/base_step.py +1 -2
  27. zenml/steps/step_decorator.py +1 -2
  28. zenml/types.py +10 -1
  29. zenml/utils/pipeline_docker_image_builder.py +20 -5
  30. zenml/zen_server/rbac/models.py +1 -0
  31. zenml/zen_server/rbac/utils.py +22 -1
  32. zenml/zen_server/routers/actions_endpoints.py +324 -0
  33. zenml/zen_server/routers/triggers_endpoints.py +30 -158
  34. zenml/zen_server/zen_server_api.py +2 -0
  35. zenml/zen_stores/migrations/versions/25155145c545_separate_actions_and_triggers.py +228 -0
  36. zenml/zen_stores/rest_zen_store.py +103 -4
  37. zenml/zen_stores/schemas/__init__.py +2 -0
  38. zenml/zen_stores/schemas/action_schemas.py +192 -0
  39. zenml/zen_stores/schemas/trigger_schemas.py +43 -50
  40. zenml/zen_stores/schemas/user_schemas.py +10 -2
  41. zenml/zen_stores/schemas/workspace_schemas.py +5 -0
  42. zenml/zen_stores/sql_zen_store.py +240 -30
  43. zenml/zen_stores/zen_store_interface.py +85 -0
  44. {zenml_nightly-0.58.2.dev20240624.dist-info → zenml_nightly-0.58.2.dev20240625.dist-info}/METADATA +1 -1
  45. {zenml_nightly-0.58.2.dev20240624.dist-info → zenml_nightly-0.58.2.dev20240625.dist-info}/RECORD +48 -44
  46. {zenml_nightly-0.58.2.dev20240624.dist-info → zenml_nightly-0.58.2.dev20240625.dist-info}/LICENSE +0 -0
  47. {zenml_nightly-0.58.2.dev20240624.dist-info → zenml_nightly-0.58.2.dev20240625.dist-info}/WHEEL +0 -0
  48. {zenml_nightly-0.58.2.dev20240624.dist-info → zenml_nightly-0.58.2.dev20240625.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,192 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """SQL Model Implementations for Actions."""
15
+
16
+ import base64
17
+ import json
18
+ from datetime import datetime
19
+ from typing import TYPE_CHECKING, Any, List, Optional
20
+ from uuid import UUID
21
+
22
+ from pydantic.json import pydantic_encoder
23
+ from sqlalchemy import TEXT, Column
24
+ from sqlmodel import Field, Relationship
25
+
26
+ from zenml.models import (
27
+ ActionRequest,
28
+ ActionResponse,
29
+ ActionResponseBody,
30
+ ActionResponseMetadata,
31
+ ActionResponseResources,
32
+ ActionUpdate,
33
+ )
34
+ from zenml.zen_stores.schemas.base_schemas import NamedSchema
35
+ from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
36
+ from zenml.zen_stores.schemas.user_schemas import UserSchema
37
+ from zenml.zen_stores.schemas.workspace_schemas import WorkspaceSchema
38
+
39
+ if TYPE_CHECKING:
40
+ from zenml.zen_stores.schemas import TriggerSchema
41
+
42
+
43
+ class ActionSchema(NamedSchema, table=True):
44
+ """SQL Model for actions."""
45
+
46
+ __tablename__ = "action"
47
+
48
+ workspace_id: UUID = build_foreign_key_field(
49
+ source=__tablename__,
50
+ target=WorkspaceSchema.__tablename__,
51
+ source_column="workspace_id",
52
+ target_column="id",
53
+ ondelete="CASCADE",
54
+ nullable=False,
55
+ )
56
+ workspace: "WorkspaceSchema" = Relationship(back_populates="actions")
57
+
58
+ user_id: Optional[UUID] = build_foreign_key_field(
59
+ source=__tablename__,
60
+ target=UserSchema.__tablename__,
61
+ source_column="user_id",
62
+ target_column="id",
63
+ ondelete="SET NULL",
64
+ nullable=True,
65
+ )
66
+ user: Optional["UserSchema"] = Relationship(
67
+ back_populates="actions",
68
+ sa_relationship_kwargs={"foreign_keys": "[ActionSchema.user_id]"},
69
+ )
70
+
71
+ triggers: List["TriggerSchema"] = Relationship(back_populates="action")
72
+
73
+ service_account_id: UUID = build_foreign_key_field(
74
+ source=__tablename__,
75
+ target=UserSchema.__tablename__,
76
+ source_column="service_account_id",
77
+ target_column="id",
78
+ ondelete="CASCADE",
79
+ nullable=False,
80
+ )
81
+ service_account: UserSchema = Relationship(
82
+ back_populates="auth_actions",
83
+ sa_relationship_kwargs={
84
+ "foreign_keys": "[ActionSchema.service_account_id]"
85
+ },
86
+ )
87
+ auth_window: int
88
+
89
+ flavor: str = Field(nullable=False)
90
+ plugin_subtype: str = Field(nullable=False)
91
+ description: Optional[str] = Field(sa_column=Column(TEXT, nullable=True))
92
+
93
+ configuration: bytes
94
+
95
+ @classmethod
96
+ def from_request(cls, request: "ActionRequest") -> "ActionSchema":
97
+ """Convert a `ActionRequest` to a `ActionSchema`.
98
+
99
+ Args:
100
+ request: The request model to convert.
101
+
102
+ Returns:
103
+ The converted schema.
104
+ """
105
+ return cls(
106
+ name=request.name,
107
+ workspace_id=request.workspace,
108
+ user_id=request.user,
109
+ configuration=base64.b64encode(
110
+ json.dumps(
111
+ request.configuration, default=pydantic_encoder
112
+ ).encode("utf-8"),
113
+ ),
114
+ flavor=request.flavor,
115
+ plugin_subtype=request.plugin_subtype,
116
+ description=request.description,
117
+ service_account_id=request.service_account_id,
118
+ auth_window=request.auth_window,
119
+ )
120
+
121
+ def update(self, action_update: "ActionUpdate") -> "ActionSchema":
122
+ """Updates a action schema with a action update model.
123
+
124
+ Args:
125
+ action_update: `ActionUpdate` to update the action with.
126
+
127
+ Returns:
128
+ The updated ActionSchema.
129
+ """
130
+ for field, value in action_update.dict(
131
+ exclude_unset=True,
132
+ exclude_none=True,
133
+ ).items():
134
+ if field == "configuration":
135
+ self.configuration = base64.b64encode(
136
+ json.dumps(
137
+ action_update.configuration, default=pydantic_encoder
138
+ ).encode("utf-8")
139
+ )
140
+ else:
141
+ setattr(self, field, value)
142
+
143
+ self.updated = datetime.utcnow()
144
+ return self
145
+
146
+ def to_model(
147
+ self,
148
+ include_metadata: bool = False,
149
+ include_resources: bool = False,
150
+ **kwargs: Any,
151
+ ) -> "ActionResponse":
152
+ """Converts the action schema to a model.
153
+
154
+ Args:
155
+ include_metadata: Flag deciding whether to include the output model(s)
156
+ metadata fields in the response.
157
+ include_resources: Flag deciding whether to include the output model(s)
158
+ metadata fields in the response.
159
+ **kwargs: Keyword arguments to allow schema specific logic
160
+
161
+ Returns:
162
+ The converted model.
163
+ """
164
+ body = ActionResponseBody(
165
+ user=self.user.to_model() if self.user else None,
166
+ created=self.created,
167
+ updated=self.updated,
168
+ flavor=self.flavor,
169
+ plugin_subtype=self.plugin_subtype,
170
+ )
171
+ metadata = None
172
+ if include_metadata:
173
+ metadata = ActionResponseMetadata(
174
+ workspace=self.workspace.to_model(),
175
+ configuration=json.loads(
176
+ base64.b64decode(self.configuration).decode()
177
+ ),
178
+ description=self.description,
179
+ auth_window=self.auth_window,
180
+ )
181
+ resources = None
182
+ if include_resources:
183
+ resources = ActionResponseResources(
184
+ service_account=self.service_account.to_model(),
185
+ )
186
+ return ActionResponse(
187
+ id=self.id,
188
+ name=self.name,
189
+ body=body,
190
+ metadata=metadata,
191
+ resources=resources,
192
+ )
@@ -22,13 +22,14 @@ from uuid import UUID
22
22
  from sqlalchemy import TEXT, Column
23
23
  from sqlmodel import Field, Relationship
24
24
 
25
- from zenml import TriggerExecutionResponseResources
25
+ from zenml.config.schedule import Schedule
26
26
  from zenml.models import (
27
27
  Page,
28
28
  TriggerExecutionRequest,
29
29
  TriggerExecutionResponse,
30
30
  TriggerExecutionResponseBody,
31
31
  TriggerExecutionResponseMetadata,
32
+ TriggerExecutionResponseResources,
32
33
  TriggerRequest,
33
34
  TriggerResponse,
34
35
  TriggerResponseBody,
@@ -37,6 +38,7 @@ from zenml.models import (
37
38
  TriggerUpdate,
38
39
  )
39
40
  from zenml.utils.json_utils import pydantic_encoder
41
+ from zenml.zen_stores.schemas.action_schemas import ActionSchema
40
42
  from zenml.zen_stores.schemas.base_schemas import BaseSchema, NamedSchema
41
43
  from zenml.zen_stores.schemas.event_source_schemas import EventSourceSchema
42
44
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
@@ -78,36 +80,33 @@ class TriggerSchema(NamedSchema, table=True):
78
80
  target=EventSourceSchema.__tablename__,
79
81
  source_column="event_source_id",
80
82
  target_column="id",
81
- ondelete="CASCADE", # TODO: this should be set null and the trigger should be deactivated
82
- nullable=False,
83
+ # This won't happen because the SQL zen store prevents an event source
84
+ # from being deleted if it has associated triggers
85
+ ondelete="SET NULL",
86
+ nullable=True,
83
87
  )
84
- event_source: "EventSourceSchema" = Relationship(back_populates="triggers")
85
-
86
- executions: List["TriggerExecutionSchema"] = Relationship(
87
- back_populates="trigger"
88
+ event_source: Optional["EventSourceSchema"] = Relationship(
89
+ back_populates="triggers"
88
90
  )
89
91
 
90
- service_account_id: UUID = build_foreign_key_field(
92
+ action_id: UUID = build_foreign_key_field(
91
93
  source=__tablename__,
92
- target=UserSchema.__tablename__,
93
- source_column="service_account_id",
94
+ target=ActionSchema.__tablename__,
95
+ source_column="action_id",
94
96
  target_column="id",
95
- ondelete="CASCADE", # TODO: this should be set null and the trigger should be deactivated
97
+ # This won't happen because the SQL zen store prevents an action
98
+ # from being deleted if it has associated triggers
99
+ ondelete="CASCADE",
96
100
  nullable=False,
97
101
  )
98
- service_account: UserSchema = Relationship(
99
- back_populates="auth_triggers",
100
- sa_relationship_kwargs={
101
- "foreign_keys": "[TriggerSchema.service_account_id]"
102
- },
102
+ action: "ActionSchema" = Relationship(back_populates="triggers")
103
+
104
+ executions: List["TriggerExecutionSchema"] = Relationship(
105
+ back_populates="trigger"
103
106
  )
104
- auth_window: int
105
107
 
106
108
  event_filter: bytes
107
-
108
- action: bytes
109
- action_flavor: str # <- "builtin"
110
- action_subtype: str # <- "PipelineRun"
109
+ schedule: Optional[bytes] = Field(nullable=True)
111
110
 
112
111
  description: str = Field(sa_column=Column(TEXT, nullable=True))
113
112
  is_active: bool = Field(nullable=False)
@@ -131,12 +130,6 @@ class TriggerSchema(NamedSchema, table=True):
131
130
  trigger_update.event_filter, default=pydantic_encoder
132
131
  ).encode("utf-8")
133
132
  )
134
- elif field == "action":
135
- self.action = base64.b64encode(
136
- json.dumps(
137
- trigger_update.action, default=pydantic_encoder
138
- ).encode("utf-8")
139
- )
140
133
  else:
141
134
  setattr(self, field, value)
142
135
 
@@ -152,31 +145,21 @@ class TriggerSchema(NamedSchema, table=True):
152
145
 
153
146
  Returns:
154
147
  The converted schema.
155
-
156
- Raises:
157
- ValueError: If `auth_window` is not set.
158
148
  """
159
- if request.auth_window is None:
160
- raise ValueError("auth_window must be set")
161
149
  return cls(
162
150
  name=request.name,
163
151
  workspace_id=request.workspace,
164
152
  user_id=request.user,
165
- action=base64.b64encode(
166
- json.dumps(request.action, default=pydantic_encoder).encode(
167
- "utf-8"
168
- ),
169
- ),
170
- action_flavor=request.action_flavor,
171
- action_subtype=request.action_subtype,
153
+ action_id=request.action_id,
172
154
  event_source_id=request.event_source_id,
173
- service_account_id=request.service_account_id,
174
- auth_window=request.auth_window,
175
155
  event_filter=base64.b64encode(
176
156
  json.dumps(
177
157
  request.event_filter, default=pydantic_encoder
178
158
  ).encode("utf-8")
179
159
  ),
160
+ schedule=base64.b64encode(request.schedule.json().encode("utf-8"))
161
+ if request.schedule
162
+ else None,
180
163
  description=request.description,
181
164
  is_active=True, # Makes no sense for it to be created inactive
182
165
  )
@@ -205,9 +188,14 @@ class TriggerSchema(NamedSchema, table=True):
205
188
  user=self.user.to_model() if self.user else None,
206
189
  created=self.created,
207
190
  updated=self.updated,
208
- action_flavor=self.action_flavor,
209
- action_subtype=self.action_subtype,
210
- event_source_flavor=self.event_source.flavor,
191
+ action_flavor=self.action.flavor,
192
+ action_subtype=self.action.plugin_subtype,
193
+ event_source_flavor=self.event_source.flavor
194
+ if self.event_source
195
+ else None,
196
+ event_source_subtype=self.event_source.plugin_subtype
197
+ if self.event_source
198
+ else None,
211
199
  is_active=self.is_active,
212
200
  )
213
201
  metadata = None
@@ -217,9 +205,12 @@ class TriggerSchema(NamedSchema, table=True):
217
205
  event_filter=json.loads(
218
206
  base64.b64decode(self.event_filter).decode()
219
207
  ),
220
- action=json.loads(base64.b64decode(self.action).decode()),
208
+ schedule=Schedule.parse_raw(
209
+ base64.b64decode(self.schedule).decode()
210
+ )
211
+ if self.schedule
212
+ else None,
221
213
  description=self.description,
222
- auth_window=self.auth_window,
223
214
  )
224
215
  resources = None
225
216
  if include_resources:
@@ -228,13 +219,15 @@ class TriggerSchema(NamedSchema, table=True):
228
219
  get_page_from_list(
229
220
  items_list=self.executions,
230
221
  response_model=TriggerExecutionResponse,
231
- include_resources=include_resources,
232
- include_metadata=include_metadata,
222
+ include_resources=False,
223
+ include_metadata=False,
233
224
  ),
234
225
  )
235
226
  resources = TriggerResponseResources(
236
- event_source=self.event_source.to_model(),
237
- service_account=self.service_account.to_model(),
227
+ action=self.action.to_model(),
228
+ event_source=self.event_source.to_model()
229
+ if self.event_source
230
+ else None,
238
231
  executions=executions,
239
232
  )
240
233
  return TriggerResponse(
@@ -37,6 +37,7 @@ from zenml.zen_stores.schemas.base_schemas import NamedSchema
37
37
 
38
38
  if TYPE_CHECKING:
39
39
  from zenml.zen_stores.schemas import (
40
+ ActionSchema,
40
41
  APIKeySchema,
41
42
  ArtifactVersionSchema,
42
43
  CodeRepositorySchema,
@@ -87,6 +88,13 @@ class UserSchema(NamedSchema, table=True):
87
88
  back_populates="user",
88
89
  )
89
90
  flavors: List["FlavorSchema"] = Relationship(back_populates="user")
91
+ actions: List["ActionSchema"] = Relationship(
92
+ back_populates="user",
93
+ sa_relationship_kwargs={
94
+ "cascade": "delete",
95
+ "primaryjoin": "UserSchema.id==ActionSchema.user_id",
96
+ },
97
+ )
90
98
  event_sources: List["EventSourceSchema"] = Relationship(
91
99
  back_populates="user"
92
100
  )
@@ -114,11 +122,11 @@ class UserSchema(NamedSchema, table=True):
114
122
  "primaryjoin": "UserSchema.id==TriggerSchema.user_id",
115
123
  },
116
124
  )
117
- auth_triggers: List["TriggerSchema"] = Relationship(
125
+ auth_actions: List["ActionSchema"] = Relationship(
118
126
  back_populates="service_account",
119
127
  sa_relationship_kwargs={
120
128
  "cascade": "delete",
121
- "primaryjoin": "UserSchema.id==TriggerSchema.service_account_id",
129
+ "primaryjoin": "UserSchema.id==ActionSchema.service_account_id",
122
130
  },
123
131
  )
124
132
  deployments: List["PipelineDeploymentSchema"] = Relationship(
@@ -29,6 +29,7 @@ from zenml.zen_stores.schemas.base_schemas import NamedSchema
29
29
 
30
30
  if TYPE_CHECKING:
31
31
  from zenml.zen_stores.schemas import (
32
+ ActionSchema,
32
33
  ArtifactVersionSchema,
33
34
  CodeRepositorySchema,
34
35
  EventSourceSchema,
@@ -104,6 +105,10 @@ class WorkspaceSchema(NamedSchema, table=True):
104
105
  back_populates="workspace",
105
106
  sa_relationship_kwargs={"cascade": "delete"},
106
107
  )
108
+ actions: List["ActionSchema"] = Relationship(
109
+ back_populates="workspace",
110
+ sa_relationship_kwargs={"cascade": "delete"},
111
+ )
107
112
  triggers: List["TriggerSchema"] = Relationship(
108
113
  back_populates="workspace",
109
114
  sa_relationship_kwargs={"cascade": "delete"},