fiddler-client 2.5.0.dev1__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/_version.py +1 -1
- fiddler/core_objects.py +18 -5
- fiddler/schema/alert.py +3 -3
- fiddler/schemas/custom_features.py +31 -1
- fiddler3/__init__.py +121 -3
- fiddler3/configs.py +3 -0
- fiddler3/constants/model.py +9 -0
- fiddler3/entities/__init__.py +0 -10
- fiddler3/entities/alert_rule.py +79 -2
- fiddler3/entities/baseline.py +21 -15
- fiddler3/entities/dataset.py +1 -1
- fiddler3/entities/events.py +68 -52
- fiddler3/entities/{files.py → file.py} +7 -15
- fiddler3/entities/model.py +81 -13
- fiddler3/entities/model_artifact.py +5 -5
- fiddler3/entities/model_deployment.py +12 -1
- fiddler3/entities/project.py +1 -1
- fiddler3/entities/{model_surrogate.py → surrogate.py} +27 -29
- fiddler3/entities/xai.py +8 -9
- fiddler3/libs/http_client.py +1 -1
- fiddler3/schemas/__init__.py +0 -9
- fiddler3/schemas/alert_rule.py +7 -0
- fiddler3/schemas/custom_features.py +34 -12
- fiddler3/schemas/model_deployment.py +5 -4
- fiddler3/schemas/webhook.py +0 -3
- fiddler3/tests/apis/test_alert_rule.py +71 -1
- fiddler3/tests/apis/test_events.py +41 -0
- fiddler3/tests/apis/test_files.py +1 -1
- fiddler3/tests/apis/test_model.py +5 -1
- fiddler3/tests/apis/test_project.py +1 -1
- fiddler3/tests/apis/test_webhook.py +0 -23
- fiddler3/utils/helpers.py +20 -0
- {fiddler_client-2.5.0.dev1.dist-info → fiddler_client-2.5.0.dev2.dist-info}/METADATA +15 -5
- {fiddler_client-2.5.0.dev1.dist-info → fiddler_client-2.5.0.dev2.dist-info}/RECORD +38 -39
- fiddler3/entities/helpers.py +0 -21
- /fiddler3/schemas/{files.py → file.py} +0 -0
- {fiddler_client-2.5.0.dev1.dist-info → fiddler_client-2.5.0.dev2.dist-info}/LICENSE.txt +0 -0
- {fiddler_client-2.5.0.dev1.dist-info → fiddler_client-2.5.0.dev2.dist-info}/WHEEL +0 -0
- {fiddler_client-2.5.0.dev1.dist-info → fiddler_client-2.5.0.dev2.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ import io
|
|
|
4
4
|
import os
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
10
|
from requests import Response
|
|
@@ -14,7 +14,7 @@ from fiddler3.constants.common import MULTI_PART_CHUNK_SIZE
|
|
|
14
14
|
from fiddler3.decorators import handle_api_error
|
|
15
15
|
from fiddler3.entities.base import BaseEntity
|
|
16
16
|
from fiddler3.entities.user import CreatedByMixin, UpdatedByMixin
|
|
17
|
-
from fiddler3.schemas.
|
|
17
|
+
from fiddler3.schemas.file import FileResp
|
|
18
18
|
from fiddler3.utils.logger import get_logger
|
|
19
19
|
|
|
20
20
|
logger = get_logger(__name__)
|
|
@@ -66,16 +66,13 @@ class File(BaseEntity, CreatedByMixin, UpdatedByMixin):
|
|
|
66
66
|
chunk_size: int = MULTI_PART_CHUNK_SIZE,
|
|
67
67
|
) -> File:
|
|
68
68
|
"""Upload file. Single or multipart upload depends on file size"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
69
|
+
if os.path.getsize(self.path) < chunk_size:
|
|
70
|
+
return self.single_upload()
|
|
71
|
+
|
|
72
|
+
return self.multipart_upload(chunk_size=chunk_size)
|
|
73
73
|
|
|
74
74
|
@handle_api_error
|
|
75
|
-
def single_upload(
|
|
76
|
-
self,
|
|
77
|
-
chunk_size: int = MULTI_PART_CHUNK_SIZE, # pylint: disable=unused-argument
|
|
78
|
-
) -> File:
|
|
75
|
+
def single_upload(self) -> File:
|
|
79
76
|
"""Single part file upload"""
|
|
80
77
|
with open(self.path, 'rb') as f:
|
|
81
78
|
multipart_data = MultipartEncoder(fields={'file': (self.name, f)})
|
|
@@ -163,8 +160,3 @@ class File(BaseEntity, CreatedByMixin, UpdatedByMixin):
|
|
|
163
160
|
|
|
164
161
|
logger.info('Multi-part upload complete')
|
|
165
162
|
return response
|
|
166
|
-
|
|
167
|
-
def _get_upload_method(self, chunk_size: int) -> Callable:
|
|
168
|
-
if os.path.getsize(self.path) < chunk_size:
|
|
169
|
-
return self.single_upload
|
|
170
|
-
return self.multipart_upload
|
fiddler3/entities/model.py
CHANGED
|
@@ -11,16 +11,16 @@ from uuid import UUID
|
|
|
11
11
|
|
|
12
12
|
import pandas as pd
|
|
13
13
|
|
|
14
|
+
from fiddler3.constants.dataset import EnvType
|
|
14
15
|
from fiddler3.constants.model import ModelInputType, ModelTask
|
|
15
16
|
from fiddler3.decorators import handle_api_error
|
|
16
17
|
from fiddler3.entities.base import BaseEntity
|
|
17
|
-
from fiddler3.entities.events import
|
|
18
|
-
from fiddler3.entities.helpers import raise_not_found
|
|
18
|
+
from fiddler3.entities.events import EventPublisher
|
|
19
19
|
from fiddler3.entities.job import Job
|
|
20
20
|
from fiddler3.entities.model_artifact import ModelArtifact
|
|
21
21
|
from fiddler3.entities.model_deployment import ModelDeployment
|
|
22
|
-
from fiddler3.entities.model_surrogate import SurrogateMixin
|
|
23
22
|
from fiddler3.entities.project import ProjectCompactMixin
|
|
23
|
+
from fiddler3.entities.surrogate import Surrogate
|
|
24
24
|
from fiddler3.entities.user import CreatedByMixin, UpdatedByMixin
|
|
25
25
|
from fiddler3.entities.xai import XaiMixin
|
|
26
26
|
from fiddler3.schemas.deployment_params import DeploymentParams
|
|
@@ -31,6 +31,7 @@ from fiddler3.schemas.model_schema import ModelSchema
|
|
|
31
31
|
from fiddler3.schemas.model_spec import ModelSpec
|
|
32
32
|
from fiddler3.schemas.model_task_params import ModelTaskParams
|
|
33
33
|
from fiddler3.schemas.xai_params import XaiParams
|
|
34
|
+
from fiddler3.utils.helpers import raise_not_found
|
|
34
35
|
from fiddler3.utils.model_schema_generator import SchemaGeneratorFactory
|
|
35
36
|
|
|
36
37
|
if typing.TYPE_CHECKING:
|
|
@@ -42,16 +43,14 @@ class Model(
|
|
|
42
43
|
CreatedByMixin,
|
|
43
44
|
ProjectCompactMixin,
|
|
44
45
|
UpdatedByMixin,
|
|
45
|
-
SurrogateMixin,
|
|
46
46
|
XaiMixin,
|
|
47
|
-
EventMixin,
|
|
48
47
|
): # pylint: disable=too-many-ancestors
|
|
49
48
|
def __init__( # pylint: disable=too-many-arguments
|
|
50
49
|
self,
|
|
51
50
|
name: str,
|
|
52
51
|
project_id: UUID,
|
|
53
52
|
schema: ModelSchema,
|
|
54
|
-
spec: ModelSpec
|
|
53
|
+
spec: ModelSpec,
|
|
55
54
|
input_type: str = ModelInputType.TABULAR,
|
|
56
55
|
task: str = ModelTask.NOT_SET,
|
|
57
56
|
task_params: ModelTaskParams | None = None,
|
|
@@ -71,7 +70,7 @@ class Model(
|
|
|
71
70
|
self.event_id_col = event_id_col
|
|
72
71
|
self.event_ts_col = event_ts_col
|
|
73
72
|
self.event_ts_format = event_ts_format
|
|
74
|
-
self.spec = spec
|
|
73
|
+
self.spec = spec
|
|
75
74
|
self.task_params = task_params or ModelTaskParams()
|
|
76
75
|
self.xai_params = xai_params or XaiParams()
|
|
77
76
|
|
|
@@ -167,6 +166,18 @@ class Model(
|
|
|
167
166
|
assert self.id is not None
|
|
168
167
|
return ModelArtifact(model_id=self.id)
|
|
169
168
|
|
|
169
|
+
@cached_property
|
|
170
|
+
def _surrogate(self) -> Surrogate:
|
|
171
|
+
"""Model artifact instance"""
|
|
172
|
+
assert self.id is not None
|
|
173
|
+
return Surrogate(model_id=self.id)
|
|
174
|
+
|
|
175
|
+
@cached_property
|
|
176
|
+
def _event_publisher(self) -> EventPublisher:
|
|
177
|
+
"""Event publisher instance"""
|
|
178
|
+
assert self.id is not None
|
|
179
|
+
return EventPublisher(model_id=self.id)
|
|
180
|
+
|
|
170
181
|
@classmethod
|
|
171
182
|
@handle_api_error
|
|
172
183
|
def get(cls, id_: UUID | str) -> Model:
|
|
@@ -196,7 +207,7 @@ class Model(
|
|
|
196
207
|
if response.json()['data']['total'] == 0:
|
|
197
208
|
raise_not_found('Model not found for the given identifier')
|
|
198
209
|
|
|
199
|
-
return cls.
|
|
210
|
+
return cls.get(id_=response.json()['data']['items'][0]['id'])
|
|
200
211
|
|
|
201
212
|
@handle_api_error
|
|
202
213
|
def create(self) -> Model:
|
|
@@ -267,11 +278,9 @@ class Model(
|
|
|
267
278
|
@handle_api_error
|
|
268
279
|
def model_deployment(self) -> ModelDeployment:
|
|
269
280
|
"""Fetch all the deployments for this model"""
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return ModelDeployment.
|
|
273
|
-
data=response.json()['data']
|
|
274
|
-
)
|
|
281
|
+
assert self.id is not None
|
|
282
|
+
|
|
283
|
+
return ModelDeployment.of(model_id=self.id)
|
|
275
284
|
|
|
276
285
|
@classmethod
|
|
277
286
|
@handle_api_error
|
|
@@ -309,6 +318,38 @@ class Model(
|
|
|
309
318
|
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
310
319
|
return Job.get(id_=job_compact.id)
|
|
311
320
|
|
|
321
|
+
@handle_api_error
|
|
322
|
+
def publish(
|
|
323
|
+
self,
|
|
324
|
+
source: builtins.list[dict[str, Any]] | str | Path | pd.DataFrame,
|
|
325
|
+
environment: EnvType = EnvType.PRODUCTION,
|
|
326
|
+
dataset_name: str | None = None,
|
|
327
|
+
update: bool = False,
|
|
328
|
+
) -> builtins.list[UUID] | Job:
|
|
329
|
+
"""
|
|
330
|
+
Publish Pre-production or Production data
|
|
331
|
+
|
|
332
|
+
:param source: one of:
|
|
333
|
+
Path or str path: path for data file.
|
|
334
|
+
list[dict]: list of events (max 1000) PRE_PRODUCTION environment is not
|
|
335
|
+
supported
|
|
336
|
+
dataframe: events dataframe. EnvType.PRE_PRODUCTION not supported.
|
|
337
|
+
:param environment: Either EnvType.PRE_PRODUCTION or EnvType.PRODUCTION
|
|
338
|
+
:param dataset_name: Name of the dataset. Not supported for EnvType.PRODUCTION
|
|
339
|
+
:param update: flag indicating if the events are updates to previously
|
|
340
|
+
published rows
|
|
341
|
+
|
|
342
|
+
:return: list[UUID] for list of dicts or dataframe source and Job object for
|
|
343
|
+
file path source.
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
return self._event_publisher.publish(
|
|
347
|
+
source=source,
|
|
348
|
+
environment=environment,
|
|
349
|
+
dataset_name=dataset_name,
|
|
350
|
+
update=update,
|
|
351
|
+
)
|
|
352
|
+
|
|
312
353
|
def add_artifact(
|
|
313
354
|
self,
|
|
314
355
|
model_dir: str | Path,
|
|
@@ -353,6 +394,33 @@ class Model(
|
|
|
353
394
|
|
|
354
395
|
self._artifact.download(output_dir=output_dir)
|
|
355
396
|
|
|
397
|
+
def add_surrogate(
|
|
398
|
+
self,
|
|
399
|
+
deployment_params: DeploymentParams | None = None,
|
|
400
|
+
) -> Job:
|
|
401
|
+
"""
|
|
402
|
+
Add a new surrogate model
|
|
403
|
+
|
|
404
|
+
:param deployment_params: Model deployment parameters
|
|
405
|
+
:return: Async job
|
|
406
|
+
"""
|
|
407
|
+
job = self._surrogate.add(deployment_params=deployment_params)
|
|
408
|
+
return job
|
|
409
|
+
|
|
410
|
+
@handle_api_error
|
|
411
|
+
def update_surrogate(
|
|
412
|
+
self,
|
|
413
|
+
deployment_params: DeploymentParams | None = None,
|
|
414
|
+
) -> Job:
|
|
415
|
+
"""
|
|
416
|
+
Update an existing surrogate model
|
|
417
|
+
|
|
418
|
+
:param deployment_params: Model deployment parameters
|
|
419
|
+
:return: Async job
|
|
420
|
+
"""
|
|
421
|
+
job = self._surrogate.update(deployment_params=deployment_params)
|
|
422
|
+
return job
|
|
423
|
+
|
|
356
424
|
|
|
357
425
|
@dataclass
|
|
358
426
|
class ModelCompact:
|
|
@@ -9,7 +9,7 @@ from uuid import UUID
|
|
|
9
9
|
|
|
10
10
|
from fiddler3.connection import ConnectionMixin
|
|
11
11
|
from fiddler3.decorators import handle_api_error
|
|
12
|
-
from fiddler3.entities.
|
|
12
|
+
from fiddler3.entities.file import File
|
|
13
13
|
from fiddler3.entities.job import Job
|
|
14
14
|
from fiddler3.schemas.deployment_params import DeploymentParams
|
|
15
15
|
from fiddler3.schemas.job import JobCompactResp
|
|
@@ -28,7 +28,7 @@ class ModelArtifact(ConnectionMixin):
|
|
|
28
28
|
"""
|
|
29
29
|
self.model_id = model_id
|
|
30
30
|
|
|
31
|
-
def
|
|
31
|
+
def _get_http_method(self, update: bool = False) -> Callable:
|
|
32
32
|
"""Get HTTP method based on operation"""
|
|
33
33
|
if update:
|
|
34
34
|
return self._client().put
|
|
@@ -60,7 +60,7 @@ class ModelArtifact(ConnectionMixin):
|
|
|
60
60
|
)
|
|
61
61
|
file_path = shutil.make_archive(
|
|
62
62
|
base_name=str(Path(tmp) / 'files'),
|
|
63
|
-
format='
|
|
63
|
+
format='gztar',
|
|
64
64
|
root_dir=str(model_dir),
|
|
65
65
|
base_dir='.',
|
|
66
66
|
)
|
|
@@ -97,7 +97,7 @@ class ModelArtifact(ConnectionMixin):
|
|
|
97
97
|
update: bool = False,
|
|
98
98
|
) -> Job:
|
|
99
99
|
"""Artifact deploy base method."""
|
|
100
|
-
|
|
100
|
+
http_method = self._get_http_method(update)
|
|
101
101
|
payload = {
|
|
102
102
|
'file_id': file_id,
|
|
103
103
|
'deployment_params': deployment_params.dict(exclude_unset=True)
|
|
@@ -105,7 +105,7 @@ class ModelArtifact(ConnectionMixin):
|
|
|
105
105
|
else {},
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
response =
|
|
108
|
+
response = http_method(
|
|
109
109
|
url=f'/v3/models/{self.model_id}/deploy-artifact',
|
|
110
110
|
data=payload,
|
|
111
111
|
)
|
|
@@ -37,7 +37,7 @@ class ModelDeployment(
|
|
|
37
37
|
self._resp: ModelDeploymentResponse | None = None
|
|
38
38
|
|
|
39
39
|
@staticmethod
|
|
40
|
-
def _get_url(model_id: UUID | str
|
|
40
|
+
def _get_url(model_id: UUID | str) -> str:
|
|
41
41
|
"""Get model deployment url."""
|
|
42
42
|
return f'v3/models/{model_id}/deployment'
|
|
43
43
|
|
|
@@ -110,3 +110,14 @@ class ModelDeployment(
|
|
|
110
110
|
url=self._get_url(model_id=self.model_id), data=body
|
|
111
111
|
)
|
|
112
112
|
self._refresh_from_response(response=response)
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def of(cls, model_id: UUID) -> ModelDeployment:
|
|
116
|
+
"""
|
|
117
|
+
Get model deployment instance of the given model
|
|
118
|
+
|
|
119
|
+
:param model_id: Model identifier
|
|
120
|
+
:return: ModelDeployment instance
|
|
121
|
+
"""
|
|
122
|
+
response = cls._client().get(url=cls._get_url(model_id=model_id))
|
|
123
|
+
return cls._from_dict(data=response.json()['data'])
|
fiddler3/entities/project.py
CHANGED
|
@@ -8,9 +8,9 @@ from uuid import UUID
|
|
|
8
8
|
|
|
9
9
|
from fiddler3.decorators import handle_api_error
|
|
10
10
|
from fiddler3.entities.base import BaseEntity
|
|
11
|
-
from fiddler3.entities.helpers import raise_not_found
|
|
12
11
|
from fiddler3.schemas.filter_query import OperatorType, QueryCondition, QueryRule
|
|
13
12
|
from fiddler3.schemas.project import ProjectResp
|
|
13
|
+
from fiddler3.utils.helpers import raise_not_found
|
|
14
14
|
|
|
15
15
|
if typing.TYPE_CHECKING:
|
|
16
16
|
from fiddler3.entities.model import ModelCompact
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
|
+
from fiddler3.connection import ConnectionMixin
|
|
6
7
|
from fiddler3.decorators import handle_api_error
|
|
7
8
|
from fiddler3.entities.job import Job
|
|
8
9
|
from fiddler3.schemas.deployment_params import ArtifactType, DeploymentParams
|
|
@@ -12,28 +13,26 @@ from fiddler3.utils.logger import get_logger
|
|
|
12
13
|
logger = get_logger(__name__)
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
class Surrogate(ConnectionMixin):
|
|
17
|
+
def __init__(self, model_id: UUID) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Surrogate model methods
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'This method is available only for model object generated from '
|
|
23
|
-
'API response.'
|
|
24
|
-
)
|
|
21
|
+
:param model_id: Model identifier
|
|
22
|
+
"""
|
|
23
|
+
self.model_id = model_id
|
|
25
24
|
|
|
26
|
-
def
|
|
25
|
+
def _deploy(
|
|
27
26
|
self,
|
|
28
27
|
deployment_params: DeploymentParams | None = None,
|
|
29
28
|
update: bool = False,
|
|
30
29
|
) -> Job:
|
|
31
30
|
"""
|
|
32
|
-
|
|
31
|
+
Deploy surrogate model to an existing model
|
|
33
32
|
|
|
34
33
|
:param deployment_params: Model deployment parameters
|
|
35
34
|
:param update: Set True for re-generating surrogate model, otherwise False
|
|
36
|
-
:return: Async job
|
|
35
|
+
:return: Async job
|
|
37
36
|
"""
|
|
38
37
|
payload: dict[str, Any] = {}
|
|
39
38
|
|
|
@@ -43,22 +42,27 @@ class SurrogateMixin:
|
|
|
43
42
|
{'deployment_params': deployment_params.dict(exclude_unset=True)}
|
|
44
43
|
)
|
|
45
44
|
|
|
46
|
-
url = f'/v3/models/{self.
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
url = f'/v3/models/{self.model_id}/deploy-surrogate'
|
|
46
|
+
|
|
47
|
+
if update:
|
|
48
|
+
http_method = self._client().put
|
|
49
|
+
else:
|
|
50
|
+
http_method = self._client().post
|
|
51
|
+
|
|
52
|
+
response = http_method(url=url, data=payload)
|
|
49
53
|
|
|
50
54
|
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
51
55
|
|
|
52
56
|
logger.info(
|
|
53
57
|
'Model[%s] - Submitted job (%s) for deploying a surrogate model',
|
|
54
|
-
self.
|
|
58
|
+
self.model_id,
|
|
55
59
|
job_compact.id,
|
|
56
60
|
)
|
|
57
61
|
|
|
58
62
|
return Job.get(id_=job_compact.id)
|
|
59
63
|
|
|
60
64
|
@handle_api_error
|
|
61
|
-
def
|
|
65
|
+
def add(
|
|
62
66
|
self,
|
|
63
67
|
deployment_params: DeploymentParams | None = None,
|
|
64
68
|
) -> Job:
|
|
@@ -66,16 +70,13 @@ class SurrogateMixin:
|
|
|
66
70
|
Add a new surrogate.
|
|
67
71
|
|
|
68
72
|
:param deployment_params: Model deployment parameters
|
|
69
|
-
:return: Async job
|
|
73
|
+
:return: Async job
|
|
70
74
|
"""
|
|
71
|
-
self.
|
|
72
|
-
job = self._deploy_surrogate_model(
|
|
73
|
-
deployment_params=deployment_params, update=False
|
|
74
|
-
)
|
|
75
|
+
job = self._deploy(deployment_params=deployment_params, update=False)
|
|
75
76
|
return job
|
|
76
77
|
|
|
77
78
|
@handle_api_error
|
|
78
|
-
def
|
|
79
|
+
def update(
|
|
79
80
|
self,
|
|
80
81
|
deployment_params: DeploymentParams | None = None,
|
|
81
82
|
) -> Job:
|
|
@@ -83,10 +84,7 @@ class SurrogateMixin:
|
|
|
83
84
|
Update an existing surrogate.
|
|
84
85
|
|
|
85
86
|
:param deployment_params: Model deployment parameters
|
|
86
|
-
:return: Async job
|
|
87
|
+
:return: Async job
|
|
87
88
|
"""
|
|
88
|
-
self.
|
|
89
|
-
job = self._deploy_surrogate_model(
|
|
90
|
-
deployment_params=deployment_params, update=True
|
|
91
|
-
)
|
|
89
|
+
job = self._deploy(deployment_params=deployment_params, update=True)
|
|
92
90
|
return job
|
fiddler3/entities/xai.py
CHANGED
|
@@ -74,7 +74,7 @@ class XaiMixin:
|
|
|
74
74
|
'explanation_type': method,
|
|
75
75
|
}
|
|
76
76
|
if ref_data_source:
|
|
77
|
-
payload['ref_data_source'] = ref_data_source.dict()
|
|
77
|
+
payload['ref_data_source'] = ref_data_source.dict(exclude_none=True)
|
|
78
78
|
if num_permutations:
|
|
79
79
|
payload['num_permutations'] = num_permutations
|
|
80
80
|
if ci_level:
|
|
@@ -114,7 +114,7 @@ class XaiMixin:
|
|
|
114
114
|
|
|
115
115
|
payload: dict[str, Any] = {
|
|
116
116
|
'model_id': self.id,
|
|
117
|
-
'data_source': data_source.dict(),
|
|
117
|
+
'data_source': data_source.dict(exclude_none=True),
|
|
118
118
|
'protected_features': protected_features,
|
|
119
119
|
'positive_outcome': positive_outcome,
|
|
120
120
|
}
|
|
@@ -193,8 +193,8 @@ class XaiMixin:
|
|
|
193
193
|
"""
|
|
194
194
|
self._check_id_attributes()
|
|
195
195
|
output_dir = Path(output_dir)
|
|
196
|
-
if output_dir.exists():
|
|
197
|
-
|
|
196
|
+
if not output_dir.exists():
|
|
197
|
+
os.makedirs(output_dir)
|
|
198
198
|
payload: dict[str, Any] = {
|
|
199
199
|
'model_id': self.id,
|
|
200
200
|
'query': query,
|
|
@@ -205,7 +205,6 @@ class XaiMixin:
|
|
|
205
205
|
if columns:
|
|
206
206
|
payload['columns'] = columns
|
|
207
207
|
|
|
208
|
-
os.makedirs(output_dir)
|
|
209
208
|
file_path = os.path.join(output_dir, 'output.parquet')
|
|
210
209
|
with self._client().post(url='/v3/slice-query/download', data=payload) as resp:
|
|
211
210
|
# Download parquet file
|
|
@@ -314,7 +313,7 @@ class XaiMixin:
|
|
|
314
313
|
|
|
315
314
|
payload: dict[str, Any] = {
|
|
316
315
|
'model_id': self.id,
|
|
317
|
-
'data_source': data_source.dict(),
|
|
316
|
+
'data_source': data_source.dict(exclude_none=True),
|
|
318
317
|
}
|
|
319
318
|
|
|
320
319
|
if num_refs:
|
|
@@ -431,7 +430,7 @@ class XaiMixin:
|
|
|
431
430
|
|
|
432
431
|
response = method(
|
|
433
432
|
url='/v3/analytics/precompute-feature-impact',
|
|
434
|
-
|
|
433
|
+
data=payload,
|
|
435
434
|
)
|
|
436
435
|
|
|
437
436
|
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
@@ -487,7 +486,7 @@ class XaiMixin:
|
|
|
487
486
|
|
|
488
487
|
response = method(
|
|
489
488
|
url='/v3/analytics/precompute-feature-importance',
|
|
490
|
-
|
|
489
|
+
data=payload,
|
|
491
490
|
)
|
|
492
491
|
|
|
493
492
|
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
@@ -556,7 +555,7 @@ class XaiMixin:
|
|
|
556
555
|
|
|
557
556
|
response = method(
|
|
558
557
|
url='/v3/analytics/precompute-predictions',
|
|
559
|
-
|
|
558
|
+
data=payload,
|
|
560
559
|
)
|
|
561
560
|
|
|
562
561
|
job_compact = JobCompactResp(**response.json()['data']['job'])
|
fiddler3/libs/http_client.py
CHANGED
|
@@ -78,7 +78,7 @@ class RequestClient:
|
|
|
78
78
|
request_headers.update(headers)
|
|
79
79
|
|
|
80
80
|
content_type = request_headers.get('Content-Type')
|
|
81
|
-
if data and content_type == JSON_CONTENT_TYPE:
|
|
81
|
+
if data is not None and content_type == JSON_CONTENT_TYPE:
|
|
82
82
|
data = simplejson.dumps(data, ignore_nan=True, cls=RequestClientJSONEncoder) # type: ignore
|
|
83
83
|
|
|
84
84
|
kwargs.setdefault('allow_redirects', True)
|
fiddler3/schemas/__init__.py
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"""Initialize all schemas."""
|
|
2
|
-
from fiddler3.schemas.custom_features import * # noqa
|
|
3
|
-
from fiddler3.schemas.dataset import EnvType # noqa
|
|
4
|
-
from fiddler3.schemas.deployment_params import DeploymentParams # noqa
|
|
5
|
-
from fiddler3.schemas.model_schema import ModelSchema # noqa
|
|
6
|
-
from fiddler3.schemas.model_spec import ModelSpec # noqa
|
|
7
|
-
from fiddler3.schemas.model_task_params import ModelTaskParams # noqa
|
|
8
|
-
from fiddler3.schemas.xai import * # noqa
|
|
9
|
-
from fiddler3.schemas.xai_params import XaiParams # noqa
|
fiddler3/schemas/alert_rule.py
CHANGED
|
@@ -30,3 +30,10 @@ class AlertRuleResp(BaseModel):
|
|
|
30
30
|
|
|
31
31
|
created_at: datetime
|
|
32
32
|
updated_at: datetime = Field(alias='last_updated')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NotificationConfig(BaseModel):
|
|
36
|
+
emails: Optional[List[str]]
|
|
37
|
+
pagerduty_services: Optional[List[str]]
|
|
38
|
+
pagerduty_severity: Optional[str]
|
|
39
|
+
webhooks: Optional[List[UUID]]
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
1
|
from typing import Any, Dict, List, Optional, Type, TypeVar
|
|
3
2
|
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
|
|
6
|
-
CustomFeatureTypeVar = TypeVar('CustomFeatureTypeVar', bound='CustomFeature')
|
|
7
|
-
DEFAULT_NUM_CLUSTERS = 5
|
|
8
|
-
DEFAULT_NUM_TAGS = 5
|
|
3
|
+
from pydantic import BaseModel, validator
|
|
9
4
|
|
|
5
|
+
from fiddler3.configs import DEFAULT_NUM_CLUSTERS, DEFAULT_NUM_TAGS
|
|
6
|
+
from fiddler3.constants.model import CustomFeatureType
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
FROM_COLUMNS = 'FROM_COLUMNS'
|
|
13
|
-
FROM_VECTOR = 'FROM_VECTOR'
|
|
14
|
-
FROM_TEXT_EMBEDDING = 'FROM_TEXT_EMBEDDING'
|
|
15
|
-
FROM_IMAGE_EMBEDDING = 'FROM_IMAGE_EMBEDDING'
|
|
16
|
-
ENRICHMENT = 'ENRICHMENT'
|
|
8
|
+
CustomFeatureTypeVar = TypeVar('CustomFeatureTypeVar', bound='CustomFeature')
|
|
17
9
|
|
|
18
10
|
|
|
19
11
|
class CustomFeature(BaseModel):
|
|
@@ -22,6 +14,12 @@ class CustomFeature(BaseModel):
|
|
|
22
14
|
n_clusters: Optional[int] = DEFAULT_NUM_CLUSTERS
|
|
23
15
|
centroids: Optional[List] = None
|
|
24
16
|
|
|
17
|
+
@validator('n_clusters')
|
|
18
|
+
def validate_n_clusters(cls, value: int) -> int: # noqa: N805
|
|
19
|
+
if value < 0:
|
|
20
|
+
raise ValueError('n_clusters must be greater than 0')
|
|
21
|
+
return value
|
|
22
|
+
|
|
25
23
|
class Config:
|
|
26
24
|
allow_mutation = False
|
|
27
25
|
|
|
@@ -80,6 +78,12 @@ class Multivariate(CustomFeature):
|
|
|
80
78
|
columns: List[str]
|
|
81
79
|
monitor_components: bool = False
|
|
82
80
|
|
|
81
|
+
@validator('columns')
|
|
82
|
+
def validate_columns(cls, value: List[str]) -> List[str]: # noqa: N805
|
|
83
|
+
if len(value) < 2:
|
|
84
|
+
raise ValueError('Multivariate columns must be greater than 1')
|
|
85
|
+
return value
|
|
86
|
+
|
|
83
87
|
|
|
84
88
|
class VectorFeature(CustomFeature):
|
|
85
89
|
type: CustomFeatureType = CustomFeatureType.FROM_VECTOR
|
|
@@ -91,10 +95,28 @@ class TextEmbedding(VectorFeature):
|
|
|
91
95
|
type: CustomFeatureType = CustomFeatureType.FROM_TEXT_EMBEDDING
|
|
92
96
|
n_tags: Optional[int] = DEFAULT_NUM_TAGS
|
|
93
97
|
|
|
98
|
+
@validator('source_column')
|
|
99
|
+
def validate_source_column(cls, value: str) -> str: # noqa: N805
|
|
100
|
+
if value is None:
|
|
101
|
+
raise ValueError('source_column must be specified')
|
|
102
|
+
return value
|
|
103
|
+
|
|
104
|
+
@validator('n_tags')
|
|
105
|
+
def validate_n_tags(cls, value: int) -> int: # noqa: N805
|
|
106
|
+
if value < 0:
|
|
107
|
+
raise ValueError('n_tags must be greater than 0')
|
|
108
|
+
return value
|
|
109
|
+
|
|
94
110
|
|
|
95
111
|
class ImageEmbedding(VectorFeature):
|
|
96
112
|
type: CustomFeatureType = CustomFeatureType.FROM_IMAGE_EMBEDDING
|
|
97
113
|
|
|
114
|
+
@validator('source_column')
|
|
115
|
+
def validate_source_column(cls, value: str) -> str: # noqa: N805
|
|
116
|
+
if value is None:
|
|
117
|
+
raise ValueError('source_column must be specified')
|
|
118
|
+
return value
|
|
119
|
+
|
|
98
120
|
|
|
99
121
|
class Enrichment(CustomFeature):
|
|
100
122
|
"""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
2
3
|
from uuid import UUID
|
|
3
4
|
|
|
4
5
|
from fiddler3.schemas.base import BaseModel
|
|
@@ -16,10 +17,10 @@ class ModelDeploymentResponse(BaseModel):
|
|
|
16
17
|
artifact_type: str
|
|
17
18
|
deployment_type: str
|
|
18
19
|
active: bool
|
|
19
|
-
image_uri: str
|
|
20
|
-
replicas: int
|
|
21
|
-
cpu: int
|
|
22
|
-
memory: int
|
|
20
|
+
image_uri: Optional[str]
|
|
21
|
+
replicas: Optional[int]
|
|
22
|
+
cpu: Optional[int]
|
|
23
|
+
memory: Optional[int]
|
|
23
24
|
created_at: datetime
|
|
24
25
|
updated_at: datetime
|
|
25
26
|
created_by: UserCompactResp
|
fiddler3/schemas/webhook.py
CHANGED
|
@@ -5,7 +5,6 @@ from uuid import UUID
|
|
|
5
5
|
from pydantic.fields import Field
|
|
6
6
|
|
|
7
7
|
from fiddler3.schemas.base import BaseModel
|
|
8
|
-
from fiddler3.schemas.user import UserCompactResp
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
@enum.unique
|
|
@@ -22,5 +21,3 @@ class WebhookResp(BaseModel):
|
|
|
22
21
|
provider: WebhookProvider
|
|
23
22
|
created_at: datetime
|
|
24
23
|
updated_at: datetime
|
|
25
|
-
created_by: UserCompactResp
|
|
26
|
-
updated_by: UserCompactResp
|