fiddler-client 2.4.0.dev2__py3-none-any.whl → 2.5.0.dev2__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.
- fiddler/__init__.py +2 -0
- fiddler/_version.py +1 -1
- fiddler/api/alert_mixin.py +51 -1
- fiddler/api/webhooks_mixin.py +6 -8
- fiddler/core_objects.py +76 -26
- fiddler/schema/alert.py +9 -2
- fiddler/schemas/custom_features.py +44 -1
- fiddler3/__init__.py +121 -38
- fiddler3/configs.py +3 -0
- fiddler3/connection.py +68 -1
- fiddler3/constants/alert_rule.py +30 -0
- fiddler3/constants/events.py +7 -0
- fiddler3/constants/model.py +9 -0
- fiddler3/entities/__init__.py +0 -7
- fiddler3/entities/alert_record.py +106 -0
- fiddler3/entities/alert_rule.py +323 -0
- fiddler3/entities/base.py +2 -16
- fiddler3/entities/baseline.py +21 -15
- fiddler3/entities/dataset.py +1 -1
- fiddler3/entities/events.py +136 -0
- fiddler3/entities/file.py +162 -0
- fiddler3/entities/model.py +141 -24
- fiddler3/entities/model_artifact.py +52 -224
- fiddler3/entities/model_deployment.py +12 -1
- fiddler3/entities/project.py +2 -2
- fiddler3/entities/{model_surrogate.py → surrogate.py} +27 -29
- fiddler3/entities/webhook.py +128 -0
- fiddler3/entities/xai.py +271 -5
- fiddler3/libs/http_client.py +1 -1
- fiddler3/libs/json_encoder.py +1 -1
- fiddler3/schemas/__init__.py +0 -9
- fiddler3/schemas/alert_record.py +27 -0
- fiddler3/schemas/alert_rule.py +39 -0
- fiddler3/schemas/custom_features.py +68 -12
- fiddler3/schemas/events.py +15 -0
- fiddler3/schemas/file.py +23 -0
- fiddler3/schemas/model.py +1 -6
- fiddler3/schemas/model_deployment.py +5 -4
- fiddler3/schemas/webhook.py +23 -0
- fiddler3/tests/apis/test_alert_record.py +99 -0
- fiddler3/tests/apis/test_alert_rule.py +393 -0
- fiddler3/tests/apis/test_events.py +169 -0
- fiddler3/tests/apis/test_files.py +164 -0
- fiddler3/tests/apis/test_model.py +60 -9
- fiddler3/tests/apis/test_model_artifact.py +21 -0
- fiddler3/tests/apis/test_project.py +3 -3
- fiddler3/tests/apis/test_webhook.py +257 -0
- fiddler3/tests/apis/test_xai.py +233 -1
- fiddler3/tests/conftest.py +2 -2
- fiddler3/tests/constants.py +5 -0
- fiddler3/tests/test_connection.py +24 -1
- fiddler3/tests/test_json_encoder.py +2 -2
- fiddler3/utils/helpers.py +20 -0
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev2.dist-info}/METADATA +15 -5
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev2.dist-info}/RECORD +59 -44
- tests/fiddler/test_alert.py +95 -5
- fiddler3/entities/helpers.py +0 -21
- fiddler3/schemas/model_artifact.py +0 -6
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev2.dist-info}/LICENSE.txt +0 -0
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev2.dist-info}/WHEEL +0 -0
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@enum.unique
|
|
5
|
+
class BinSize(str, enum.Enum):
|
|
6
|
+
HOUR = 'Hour'
|
|
7
|
+
DAY = 'Day'
|
|
8
|
+
WEEK = 'Week'
|
|
9
|
+
MONTH = 'Month'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@enum.unique
|
|
13
|
+
class CompareTo(str, enum.Enum):
|
|
14
|
+
"""Comparison with Absolute(raw_value) or Relative(time_period)"""
|
|
15
|
+
|
|
16
|
+
TIME_PERIOD = 'time_period'
|
|
17
|
+
RAW_VALUE = 'raw_value'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@enum.unique
|
|
21
|
+
class AlertCondition(str, enum.Enum):
|
|
22
|
+
GREATER = 'greater'
|
|
23
|
+
LESSER = 'lesser'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@enum.unique
|
|
27
|
+
class Priority(str, enum.Enum):
|
|
28
|
+
HIGH = 'HIGH'
|
|
29
|
+
MEDIUM = 'MEDIUM'
|
|
30
|
+
LOW = 'LOW'
|
fiddler3/constants/model.py
CHANGED
|
@@ -62,3 +62,12 @@ class ArtifactStatus(str, enum.Enum):
|
|
|
62
62
|
NO_MODEL = 'no_model'
|
|
63
63
|
SURROGATE = 'surrogate'
|
|
64
64
|
USER_UPLOADED = 'user_uploaded'
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@enum.unique
|
|
68
|
+
class CustomFeatureType(str, enum.Enum):
|
|
69
|
+
FROM_COLUMNS = 'FROM_COLUMNS'
|
|
70
|
+
FROM_VECTOR = 'FROM_VECTOR'
|
|
71
|
+
FROM_TEXT_EMBEDDING = 'FROM_TEXT_EMBEDDING'
|
|
72
|
+
FROM_IMAGE_EMBEDDING = 'FROM_IMAGE_EMBEDDING'
|
|
73
|
+
ENRICHMENT = 'ENRICHMENT'
|
fiddler3/entities/__init__.py
CHANGED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"""Initialize all new entities."""
|
|
2
|
-
from fiddler3.entities.baseline import Baseline # noqa
|
|
3
|
-
from fiddler3.entities.dataset import Dataset # noqa
|
|
4
|
-
from fiddler3.entities.job import Job # noqa
|
|
5
|
-
from fiddler3.entities.model import Model # noqa
|
|
6
|
-
from fiddler3.entities.model_deployment import ModelDeployment # noqa
|
|
7
|
-
from fiddler3.entities.project import Project # noqa
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
from typing import Any, Iterator
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from fiddler3.decorators import handle_api_error
|
|
8
|
+
from fiddler3.entities.base import BaseEntity
|
|
9
|
+
from fiddler3.schemas.alert_record import AlertRecordResp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AlertRecord(BaseEntity):
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
"""Construct a alert record instance"""
|
|
15
|
+
|
|
16
|
+
self.alert_run_start_time: int | None = None
|
|
17
|
+
self.alert_time_bucket: int | None = None
|
|
18
|
+
self.alert_value: float | None = None
|
|
19
|
+
self.baseline_time_bucket: int | None = None
|
|
20
|
+
self.baseline_value: float | None = None
|
|
21
|
+
self.is_alert: bool | None = None
|
|
22
|
+
self.severity: str | None = None
|
|
23
|
+
self.failure_reason: str | None = None
|
|
24
|
+
self.message: str | None = None
|
|
25
|
+
self.feature_name: str | None = None
|
|
26
|
+
self.alert_record_main_version: int | None = None
|
|
27
|
+
self.alert_record_sub_version: int | None = None
|
|
28
|
+
|
|
29
|
+
self.id: UUID | None = None
|
|
30
|
+
self.alert_rule_id: UUID | None = None
|
|
31
|
+
self.created_at: datetime | None = None
|
|
32
|
+
self.updated_at: datetime | None = None
|
|
33
|
+
|
|
34
|
+
# Deserialized response object
|
|
35
|
+
self._resp: AlertRecordResp | None = None
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def _from_dict(cls, data: dict) -> AlertRecord:
|
|
39
|
+
"""Build alert record object from the given dictionary"""
|
|
40
|
+
|
|
41
|
+
# Deserialize the response
|
|
42
|
+
resp_obj = AlertRecordResp(**data)
|
|
43
|
+
|
|
44
|
+
# Initialize
|
|
45
|
+
instance = cls()
|
|
46
|
+
|
|
47
|
+
# Add remaining fields
|
|
48
|
+
fields = [
|
|
49
|
+
'id',
|
|
50
|
+
'alert_rule_id',
|
|
51
|
+
'alert_run_start_time',
|
|
52
|
+
'alert_time_bucket',
|
|
53
|
+
'alert_value',
|
|
54
|
+
'baseline_time_bucket',
|
|
55
|
+
'baseline_value',
|
|
56
|
+
'is_alert',
|
|
57
|
+
'severity',
|
|
58
|
+
'failure_reason',
|
|
59
|
+
'message',
|
|
60
|
+
'feature_name',
|
|
61
|
+
'alert_record_main_version',
|
|
62
|
+
'alert_record_sub_version',
|
|
63
|
+
'created_at',
|
|
64
|
+
'updated_at',
|
|
65
|
+
]
|
|
66
|
+
for field in fields:
|
|
67
|
+
setattr(instance, field, getattr(resp_obj, field, None))
|
|
68
|
+
|
|
69
|
+
instance._resp = resp_obj
|
|
70
|
+
|
|
71
|
+
return instance
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _get_url(alert_rule_id: UUID | str, id_: UUID | str | None = None) -> str:
|
|
75
|
+
"""Get alert record resource/item url"""
|
|
76
|
+
url = f'/v2/alert-configs/{alert_rule_id}/records'
|
|
77
|
+
|
|
78
|
+
return url if not id_ else f'{url}/{id_}'
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
@handle_api_error
|
|
82
|
+
def list(
|
|
83
|
+
cls,
|
|
84
|
+
alert_rule_id: UUID | str,
|
|
85
|
+
start_time: datetime | None = None,
|
|
86
|
+
end_time: datetime | None = None,
|
|
87
|
+
ordering: list[str] | None = None,
|
|
88
|
+
) -> Iterator[AlertRecord]:
|
|
89
|
+
"""
|
|
90
|
+
List alert records triggered for an alert rule
|
|
91
|
+
|
|
92
|
+
:param alert_rule_id: unique identifier for alert rule
|
|
93
|
+
:param start_time: Start time to filter trigger alerts :default: 7 days ago
|
|
94
|
+
:param end_time: End time to filter trigger alerts :default: time now
|
|
95
|
+
|
|
96
|
+
:return: paginated list of alert records for the given alert rule
|
|
97
|
+
"""
|
|
98
|
+
params: dict[str, Any] = {
|
|
99
|
+
'start_time': start_time or datetime.now() - timedelta(days=7),
|
|
100
|
+
'end_time': end_time or datetime.now(),
|
|
101
|
+
}
|
|
102
|
+
if ordering:
|
|
103
|
+
params['ordering'] = ordering
|
|
104
|
+
|
|
105
|
+
for record in cls._paginate(url=cls._get_url(alert_rule_id), params=params):
|
|
106
|
+
yield cls._from_dict(data=record)
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any, Iterator
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from fiddler3.constants.alert_rule import AlertCondition, BinSize, CompareTo, Priority
|
|
9
|
+
from fiddler3.decorators import handle_api_error
|
|
10
|
+
from fiddler3.entities.base import BaseEntity
|
|
11
|
+
from fiddler3.entities.baseline import Baseline, BaselineCompactMixin
|
|
12
|
+
from fiddler3.entities.model import Model, ModelCompactMixin
|
|
13
|
+
from fiddler3.entities.project import ProjectCompactMixin
|
|
14
|
+
from fiddler3.schemas.alert_rule import AlertRuleResp, NotificationConfig
|
|
15
|
+
from fiddler3.schemas.filter_query import OperatorType, QueryCondition, QueryRule
|
|
16
|
+
from fiddler3.utils.logger import get_logger
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AlertRule(
|
|
22
|
+
BaseEntity, ModelCompactMixin, ProjectCompactMixin, BaselineCompactMixin
|
|
23
|
+
):
|
|
24
|
+
def __init__( # pylint: disable=too-many-arguments
|
|
25
|
+
self,
|
|
26
|
+
name: str,
|
|
27
|
+
model_id: UUID,
|
|
28
|
+
metric_id: str | UUID,
|
|
29
|
+
priority: Priority | str,
|
|
30
|
+
compare_to: CompareTo | str,
|
|
31
|
+
condition: AlertCondition | str,
|
|
32
|
+
bin_size: BinSize | str,
|
|
33
|
+
critical_threshold: float,
|
|
34
|
+
warning_threshold: float | None = None,
|
|
35
|
+
columns: list[str] | None = None,
|
|
36
|
+
baseline_id: UUID | None = None,
|
|
37
|
+
compare_bin_delta: int | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Construct a alert rule instance."""
|
|
40
|
+
self.name = name
|
|
41
|
+
self.model_id = model_id
|
|
42
|
+
self.metric_id = metric_id
|
|
43
|
+
self.columns = columns
|
|
44
|
+
self.baseline_id = baseline_id
|
|
45
|
+
self.priority = priority
|
|
46
|
+
self.compare_to = compare_to
|
|
47
|
+
self.compare_bin_delta = compare_bin_delta
|
|
48
|
+
self.warning_threshold = warning_threshold
|
|
49
|
+
self.critical_threshold = critical_threshold
|
|
50
|
+
self.condition = condition
|
|
51
|
+
self.bin_size = bin_size
|
|
52
|
+
|
|
53
|
+
self.id: UUID | None = None
|
|
54
|
+
self.project_id: UUID | None = None
|
|
55
|
+
|
|
56
|
+
self.created_at: datetime | None = None
|
|
57
|
+
self.updated_at: datetime | None = None
|
|
58
|
+
|
|
59
|
+
# Deserialized response object
|
|
60
|
+
self._resp: AlertRuleResp | None = None
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _get_url(id_: UUID | str | None = None) -> str:
|
|
64
|
+
"""Get model resource/item url."""
|
|
65
|
+
url = '/v2/alert-configs'
|
|
66
|
+
return url if not id_ else f'{url}/{id_}'
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def _from_dict(cls, data: dict) -> AlertRule:
|
|
70
|
+
"""Build entity object from the given dictionary."""
|
|
71
|
+
|
|
72
|
+
# Deserialize the response
|
|
73
|
+
resp_obj = AlertRuleResp(**data)
|
|
74
|
+
|
|
75
|
+
# Initialize
|
|
76
|
+
instance = cls(
|
|
77
|
+
name=resp_obj.name,
|
|
78
|
+
model_id=resp_obj.model.id,
|
|
79
|
+
metric_id=resp_obj.metric_id,
|
|
80
|
+
priority=resp_obj.priority,
|
|
81
|
+
compare_to=resp_obj.compare_to,
|
|
82
|
+
condition=resp_obj.condition,
|
|
83
|
+
bin_size=resp_obj.bin_size,
|
|
84
|
+
critical_threshold=resp_obj.critical_threshold,
|
|
85
|
+
warning_threshold=resp_obj.warning_threshold,
|
|
86
|
+
columns=resp_obj.columns,
|
|
87
|
+
baseline_id=resp_obj.baseline_id,
|
|
88
|
+
compare_bin_delta=resp_obj.compare_bin_delta,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Add remaining fields
|
|
92
|
+
fields = [
|
|
93
|
+
'id',
|
|
94
|
+
# 'notifications_config',
|
|
95
|
+
'created_at',
|
|
96
|
+
'updated_at',
|
|
97
|
+
]
|
|
98
|
+
for field in fields:
|
|
99
|
+
setattr(instance, field, getattr(resp_obj, field, None))
|
|
100
|
+
|
|
101
|
+
instance.project_id = resp_obj.project.id
|
|
102
|
+
instance._resp = resp_obj
|
|
103
|
+
|
|
104
|
+
return instance
|
|
105
|
+
|
|
106
|
+
def _refresh(self, data: dict) -> None:
|
|
107
|
+
"""Refresh the fields of this instance from the given response dictionary"""
|
|
108
|
+
# Deserialize the response
|
|
109
|
+
resp_obj = AlertRuleResp(**data)
|
|
110
|
+
|
|
111
|
+
# Reset properties
|
|
112
|
+
self.model_id = resp_obj.model.id
|
|
113
|
+
self.project_id = resp_obj.project.id
|
|
114
|
+
|
|
115
|
+
# Add remaining fields
|
|
116
|
+
fields = [
|
|
117
|
+
'id',
|
|
118
|
+
'created_at',
|
|
119
|
+
'updated_at',
|
|
120
|
+
]
|
|
121
|
+
for field in fields:
|
|
122
|
+
setattr(self, field, getattr(resp_obj, field, None))
|
|
123
|
+
|
|
124
|
+
self._resp = resp_obj
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
@handle_api_error
|
|
128
|
+
def get(cls, id_: UUID | str) -> AlertRule:
|
|
129
|
+
"""
|
|
130
|
+
Get the alert rule instance using alert rule id.
|
|
131
|
+
|
|
132
|
+
:param id_: unique uuid format identifier for alert rule
|
|
133
|
+
:return: alert rule instance
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
response = cls._client().get(url=cls._get_url(id_=id_))
|
|
137
|
+
return cls._from_response(response=response)
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
@handle_api_error
|
|
141
|
+
def list( # pylint: disable=too-many-arguments
|
|
142
|
+
cls,
|
|
143
|
+
model_id: UUID | None = None,
|
|
144
|
+
project_id: UUID | None = None,
|
|
145
|
+
metric_id: UUID | str | None = None,
|
|
146
|
+
columns: list[str] | None = None,
|
|
147
|
+
baseline_id: UUID | None = None,
|
|
148
|
+
ordering: list[str] | None = None,
|
|
149
|
+
) -> Iterator[AlertRule]:
|
|
150
|
+
"""Get a list of all alert rules in the organization."""
|
|
151
|
+
rules: list[QueryRule | QueryCondition] = []
|
|
152
|
+
|
|
153
|
+
if model_id:
|
|
154
|
+
rules.append(
|
|
155
|
+
QueryRule(field='model_id', operator=OperatorType.EQUAL, value=model_id)
|
|
156
|
+
)
|
|
157
|
+
if project_id:
|
|
158
|
+
rules.append(
|
|
159
|
+
QueryRule(
|
|
160
|
+
field='project_id', operator=OperatorType.EQUAL, value=project_id
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
if baseline_id:
|
|
164
|
+
rules.append(
|
|
165
|
+
QueryRule(
|
|
166
|
+
field='baseline_id', operator=OperatorType.EQUAL, value=baseline_id
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
if metric_id:
|
|
170
|
+
rules.append(
|
|
171
|
+
QueryRule(
|
|
172
|
+
field='metric_id', operator=OperatorType.EQUAL, value=metric_id
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
if columns:
|
|
176
|
+
for column in columns:
|
|
177
|
+
rules.append(
|
|
178
|
+
QueryRule(field='columns', operator=OperatorType.ANY, value=column)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
_filter = QueryCondition(rules=rules)
|
|
182
|
+
params: dict[str, Any] = {'filter': _filter.json()}
|
|
183
|
+
|
|
184
|
+
if ordering:
|
|
185
|
+
params['ordering'] = ','.join(ordering)
|
|
186
|
+
|
|
187
|
+
params['organization_name'] = cls.get_organization_name()
|
|
188
|
+
for rule in cls._paginate(url=cls._get_url(), params=params):
|
|
189
|
+
yield cls._from_dict(data=rule)
|
|
190
|
+
|
|
191
|
+
@handle_api_error
|
|
192
|
+
def delete(self) -> None:
|
|
193
|
+
"""Delete an alert rule."""
|
|
194
|
+
assert self.id is not None
|
|
195
|
+
|
|
196
|
+
self._client().delete(url=self._get_url(id_=self.id))
|
|
197
|
+
|
|
198
|
+
@handle_api_error
|
|
199
|
+
def create(self) -> AlertRule:
|
|
200
|
+
"""Create a new alert rule."""
|
|
201
|
+
model = Model.get(self.model_id)
|
|
202
|
+
payload: dict[str, Any] = {
|
|
203
|
+
'name': self.name,
|
|
204
|
+
'organization_name': self.organization_name,
|
|
205
|
+
'model_name': model.name,
|
|
206
|
+
'project_name': model.project.name,
|
|
207
|
+
'metric_id': self.metric_id,
|
|
208
|
+
'priority': self.priority,
|
|
209
|
+
'compare_to': self.compare_to,
|
|
210
|
+
'condition': self.condition,
|
|
211
|
+
'bin_size': self.bin_size,
|
|
212
|
+
'critical_threshold': self.critical_threshold,
|
|
213
|
+
'warning_threshold': self.warning_threshold,
|
|
214
|
+
'feature_names': self.columns,
|
|
215
|
+
'compare_bin_delta': self.compare_bin_delta,
|
|
216
|
+
}
|
|
217
|
+
if self.baseline_id:
|
|
218
|
+
baseline = Baseline.get(self.baseline_id)
|
|
219
|
+
payload['baseline_name'] = baseline.name
|
|
220
|
+
|
|
221
|
+
response = self._client().post(
|
|
222
|
+
url=self._get_url(),
|
|
223
|
+
data=payload,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
self._refresh_from_response(response=response)
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
@handle_api_error
|
|
230
|
+
def enable_notifications(self) -> None:
|
|
231
|
+
"""Enable notifications for an alert rule"""
|
|
232
|
+
self._client().patch(
|
|
233
|
+
url=self._get_url(id_=self.id), data={'enable_notification': True}
|
|
234
|
+
)
|
|
235
|
+
logger.info(
|
|
236
|
+
'Notifications have been enabled for alert rule with id: %s', self.id
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
@handle_api_error
|
|
240
|
+
def disable_notifications(self) -> None:
|
|
241
|
+
"""Disable notifications for an alert rule"""
|
|
242
|
+
self._client().patch(
|
|
243
|
+
url=self._get_url(id_=self.id), data={'enable_notification': False}
|
|
244
|
+
)
|
|
245
|
+
logger.info(
|
|
246
|
+
'Notifications have been disabled for alert rule with id: %s', self.id
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
@handle_api_error
|
|
250
|
+
def set_notification_config(
|
|
251
|
+
self,
|
|
252
|
+
emails: builtins.list[str] | None = None,
|
|
253
|
+
pagerduty_services: builtins.list[str] | None = None,
|
|
254
|
+
pagerduty_severity: str | None = None,
|
|
255
|
+
webhooks: builtins.list[UUID] | None = None,
|
|
256
|
+
) -> NotificationConfig:
|
|
257
|
+
"""
|
|
258
|
+
Set notifications for an alert rule
|
|
259
|
+
|
|
260
|
+
:param emails: list of emails
|
|
261
|
+
:param pagerduty_services: list of pagerduty services
|
|
262
|
+
:param pagerduty_severity: severity of pagerduty
|
|
263
|
+
:param webhooks: list of webhooks UUIDs
|
|
264
|
+
|
|
265
|
+
:return: AlertNotifications object
|
|
266
|
+
"""
|
|
267
|
+
notifications = {
|
|
268
|
+
'emails': {
|
|
269
|
+
'email': ','.join(emails) if emails else '',
|
|
270
|
+
},
|
|
271
|
+
'pagerduty': {
|
|
272
|
+
'service': ','.join(pagerduty_services) if pagerduty_services else '',
|
|
273
|
+
'severity': pagerduty_severity,
|
|
274
|
+
},
|
|
275
|
+
'webhooks': [{'uuid': webhook_uuid} for webhook_uuid in webhooks]
|
|
276
|
+
if webhooks
|
|
277
|
+
else None,
|
|
278
|
+
}
|
|
279
|
+
response = self._client().patch(
|
|
280
|
+
url=self._get_url(id_=self.id),
|
|
281
|
+
data={'notifications': notifications},
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return self._get_notifications_from_dict(
|
|
285
|
+
response.json()['data']['notifications']
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
@handle_api_error
|
|
289
|
+
def get_notification_config(self) -> NotificationConfig:
|
|
290
|
+
"""
|
|
291
|
+
Get notifications setting for an alert rule
|
|
292
|
+
|
|
293
|
+
:return: AlertNotifications object
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
response = self._client().get(url=self._get_url(id_=self.id))
|
|
297
|
+
|
|
298
|
+
return self._get_notifications_from_dict(
|
|
299
|
+
response.json()['data']['notifications']
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
@staticmethod
|
|
303
|
+
def _get_notifications_from_dict(notif_dict: dict) -> NotificationConfig:
|
|
304
|
+
emails = notif_dict.get('emails', {}).get('email')
|
|
305
|
+
notifications: dict = {}
|
|
306
|
+
if emails:
|
|
307
|
+
notifications['emails'] = [email.strip() for email in emails.split(',')]
|
|
308
|
+
|
|
309
|
+
pd_services = notif_dict.get('pagerduty', {}).get('service')
|
|
310
|
+
if pd_services:
|
|
311
|
+
notifications['pagerduty_services'] = [
|
|
312
|
+
pd_service.strip() for pd_service in pd_services.split(',')
|
|
313
|
+
]
|
|
314
|
+
notifications['pagerduty_severity'] = notif_dict.get('pagerduty', {}).get(
|
|
315
|
+
'severity'
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
if notif_dict.get('webhooks'):
|
|
319
|
+
notifications['webhooks'] = [
|
|
320
|
+
webhook['uuid'] for webhook in notif_dict.get('webhooks', [])
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
return NotificationConfig(**notifications)
|
fiddler3/entities/base.py
CHANGED
|
@@ -6,8 +6,7 @@ from typing import Iterator, TypeVar
|
|
|
6
6
|
from requests import Response
|
|
7
7
|
|
|
8
8
|
from fiddler3.configs import DEFAULT_PAGE_SIZE
|
|
9
|
-
from fiddler3.connection import
|
|
10
|
-
from fiddler3.libs.http_client import RequestClient
|
|
9
|
+
from fiddler3.connection import ConnectionMixin
|
|
11
10
|
from fiddler3.schemas.response import PaginatedApiResponse
|
|
12
11
|
|
|
13
12
|
BaseEntityType = TypeVar( # pylint: disable=invalid-name
|
|
@@ -15,20 +14,7 @@ BaseEntityType = TypeVar( # pylint: disable=invalid-name
|
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
class BaseEntity(ABC):
|
|
19
|
-
@classmethod
|
|
20
|
-
def _connection(cls) -> Connection:
|
|
21
|
-
"""Fiddler connection instance"""
|
|
22
|
-
from fiddler3 import connection # pylint: disable=import-outside-toplevel
|
|
23
|
-
|
|
24
|
-
assert connection is not None
|
|
25
|
-
return connection
|
|
26
|
-
|
|
27
|
-
@classmethod
|
|
28
|
-
def _client(cls) -> RequestClient:
|
|
29
|
-
"""Request client instance"""
|
|
30
|
-
return cls._connection().client
|
|
31
|
-
|
|
17
|
+
class BaseEntity(ABC, ConnectionMixin):
|
|
32
18
|
@classmethod
|
|
33
19
|
def _from_response(cls: type[BaseEntityType], response: Response) -> BaseEntityType:
|
|
34
20
|
"""Build entity object from the given response"""
|
fiddler3/entities/baseline.py
CHANGED
|
@@ -9,11 +9,11 @@ from fiddler3.constants.dataset import EnvType
|
|
|
9
9
|
from fiddler3.decorators import handle_api_error
|
|
10
10
|
from fiddler3.entities.base import BaseEntity
|
|
11
11
|
from fiddler3.entities.dataset import DatasetCompactMixin
|
|
12
|
-
from fiddler3.entities.helpers import raise_not_found
|
|
13
12
|
from fiddler3.entities.model import ModelCompactMixin
|
|
14
13
|
from fiddler3.entities.project import ProjectCompactMixin
|
|
15
14
|
from fiddler3.schemas.baseline import BaselineResp
|
|
16
15
|
from fiddler3.schemas.filter_query import OperatorType, QueryCondition, QueryRule
|
|
16
|
+
from fiddler3.utils.helpers import raise_not_found
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class Baseline(
|
|
@@ -138,9 +138,9 @@ class Baseline(
|
|
|
138
138
|
"""
|
|
139
139
|
Get the baseline instance using baseline name, model name and project name.
|
|
140
140
|
|
|
141
|
-
:param name:
|
|
142
|
-
:param model_name:
|
|
143
|
-
:param project_name:
|
|
141
|
+
:param name: Baseline name
|
|
142
|
+
:param model_name: Name of the model associated with the baseline
|
|
143
|
+
:param project_name: Name of the project associated with the baseline
|
|
144
144
|
:return: Baseline instance for the provided params
|
|
145
145
|
"""
|
|
146
146
|
|
|
@@ -179,19 +179,25 @@ class Baseline(
|
|
|
179
179
|
@handle_api_error
|
|
180
180
|
def create(self) -> Baseline:
|
|
181
181
|
"""Create a new baseline."""
|
|
182
|
+
payload = {
|
|
183
|
+
'name': self.name,
|
|
184
|
+
'model_id': self.model_id,
|
|
185
|
+
'type': self.type,
|
|
186
|
+
'env_type': self.environment,
|
|
187
|
+
'env_id': self.dataset_id,
|
|
188
|
+
}
|
|
189
|
+
if self.start_time:
|
|
190
|
+
payload['start_time'] = self.start_time
|
|
191
|
+
if self.end_time:
|
|
192
|
+
payload['end_time'] = self.end_time
|
|
193
|
+
if self.offset:
|
|
194
|
+
payload['offset'] = self.offset
|
|
195
|
+
if self.window_size:
|
|
196
|
+
payload['window_size'] = self.window_size
|
|
197
|
+
|
|
182
198
|
response = self._client().post(
|
|
183
199
|
url=self._get_url(),
|
|
184
|
-
data=
|
|
185
|
-
'name': self.name,
|
|
186
|
-
'model_id': self.model_id,
|
|
187
|
-
'type': self.type,
|
|
188
|
-
'env_type': self.environment,
|
|
189
|
-
'start_time': self.start_time,
|
|
190
|
-
'end_time': self.end_time,
|
|
191
|
-
'offset': self.offset,
|
|
192
|
-
'window_size': self.window_size,
|
|
193
|
-
'env_id': self.dataset_id,
|
|
194
|
-
},
|
|
200
|
+
data=payload,
|
|
195
201
|
)
|
|
196
202
|
self._refresh_from_response(response=response)
|
|
197
203
|
return self
|
fiddler3/entities/dataset.py
CHANGED
|
@@ -7,10 +7,10 @@ from uuid import UUID
|
|
|
7
7
|
from fiddler3.constants.dataset import EnvType
|
|
8
8
|
from fiddler3.decorators import handle_api_error
|
|
9
9
|
from fiddler3.entities.base import BaseEntity
|
|
10
|
-
from fiddler3.entities.helpers import raise_not_found
|
|
11
10
|
from fiddler3.entities.project import ProjectCompactMixin
|
|
12
11
|
from fiddler3.schemas.dataset import DatasetResp
|
|
13
12
|
from fiddler3.schemas.filter_query import OperatorType, QueryCondition, QueryRule
|
|
13
|
+
from fiddler3.utils.helpers import raise_not_found
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Dataset(BaseEntity, ProjectCompactMixin):
|