fiddler-client 2.3.0.dev2__py3-none-any.whl → 2.4.0.dev1__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 +1 -1
- fiddler/api/api.py +2 -0
- fiddler/api/webhooks_mixin.py +9 -7
- fiddler/core_objects.py +98 -47
- fiddler/schemas/custom_features.py +25 -19
- fiddler3/constants/baseline.py +19 -0
- fiddler3/constants/common.py +12 -0
- fiddler3/constants/dataset.py +7 -0
- fiddler3/constants/model_deployment.py +15 -0
- fiddler3/constants/xai.py +11 -0
- fiddler3/entities/__init__.py +5 -1
- fiddler3/entities/base.py +22 -18
- fiddler3/entities/baseline.py +228 -0
- fiddler3/entities/dataset.py +149 -0
- fiddler3/entities/job.py +9 -8
- fiddler3/entities/model.py +160 -29
- fiddler3/entities/model_artifact.py +350 -0
- fiddler3/entities/model_deployment.py +112 -0
- fiddler3/entities/model_surrogate.py +92 -0
- fiddler3/entities/organization.py +38 -0
- fiddler3/entities/project.py +69 -8
- fiddler3/entities/user.py +45 -0
- fiddler3/entities/xai.py +309 -0
- fiddler3/libs/http_client.py +24 -13
- fiddler3/libs/json_encoder.py +12 -0
- fiddler3/schemas/__init__.py +4 -0
- fiddler3/schemas/baseline.py +37 -0
- fiddler3/schemas/custom_features.py +23 -18
- fiddler3/schemas/dataset.py +27 -0
- fiddler3/schemas/deployment_params.py +25 -0
- fiddler3/schemas/filter_query.py +54 -0
- fiddler3/schemas/job.py +2 -2
- fiddler3/schemas/model.py +9 -9
- fiddler3/schemas/model_artifact.py +6 -0
- fiddler3/schemas/model_deployment.py +26 -0
- fiddler3/schemas/organization.py +9 -1
- fiddler3/schemas/project.py +4 -4
- fiddler3/schemas/server_info.py +2 -2
- fiddler3/schemas/user.py +1 -1
- fiddler3/schemas/xai.py +32 -0
- fiddler3/tests/apis/test_baseline.py +292 -0
- fiddler3/tests/apis/test_dataset.py +173 -0
- fiddler3/tests/apis/test_mixin.py +77 -0
- fiddler3/tests/apis/test_model.py +102 -7
- fiddler3/tests/apis/test_model_artifact.py +175 -0
- fiddler3/tests/apis/test_model_deployment.py +98 -0
- fiddler3/tests/apis/test_model_surrogate.py +159 -0
- fiddler3/tests/apis/test_project.py +36 -20
- fiddler3/tests/apis/test_xai.py +690 -0
- fiddler3/tests/constants.py +10 -0
- fiddler3/tests/test_json_encoder.py +16 -0
- fiddler3/tests/test_logger.py +12 -0
- fiddler3/tests/test_utils.py +19 -0
- fiddler3/utils/__init__.py +1 -0
- fiddler3/utils/helpers.py +43 -0
- fiddler3/utils/logger.py +15 -0
- fiddler3/utils/validations.py +11 -0
- {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/METADATA +7 -5
- {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/RECORD +65 -33
- tests/fiddler/test_segments.py +227 -0
- {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/LICENSE.txt +0 -0
- {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/WHEEL +0 -0
- {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/top_level.txt +0 -0
fiddler3/schemas/model.py
CHANGED
|
@@ -8,18 +8,18 @@ from fiddler3.schemas.base import BaseModel
|
|
|
8
8
|
from fiddler3.schemas.model_schema import Column, ModelSchema
|
|
9
9
|
from fiddler3.schemas.model_spec import ModelSpec
|
|
10
10
|
from fiddler3.schemas.model_task_params import ModelTaskParams
|
|
11
|
-
from fiddler3.schemas.organization import
|
|
12
|
-
from fiddler3.schemas.project import
|
|
13
|
-
from fiddler3.schemas.user import
|
|
11
|
+
from fiddler3.schemas.organization import OrganizationCompactResp
|
|
12
|
+
from fiddler3.schemas.project import ProjectCompactResp
|
|
13
|
+
from fiddler3.schemas.user import UserCompactResp
|
|
14
14
|
from fiddler3.schemas.xai_params import XaiParams
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class ModelCompactResp(BaseModel):
|
|
18
18
|
id: UUID
|
|
19
19
|
name: str
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class
|
|
22
|
+
class ModelResp(BaseModel):
|
|
23
23
|
id: UUID
|
|
24
24
|
name: str
|
|
25
25
|
input_type: str
|
|
@@ -36,10 +36,10 @@ class ModelResponse(BaseModel):
|
|
|
36
36
|
artifact_files: List[Dict]
|
|
37
37
|
created_at: datetime
|
|
38
38
|
updated_at: datetime
|
|
39
|
-
created_by:
|
|
40
|
-
updated_by:
|
|
41
|
-
organization:
|
|
42
|
-
project:
|
|
39
|
+
created_by: UserCompactResp
|
|
40
|
+
updated_by: UserCompactResp
|
|
41
|
+
organization: OrganizationCompactResp
|
|
42
|
+
project: ProjectCompactResp
|
|
43
43
|
input_cols: List[Column]
|
|
44
44
|
output_cols: List[Column]
|
|
45
45
|
target_cols: List[Column]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from fiddler3.schemas.base import BaseModel
|
|
5
|
+
from fiddler3.schemas.model import ModelCompactResp
|
|
6
|
+
from fiddler3.schemas.organization import OrganizationCompactResp
|
|
7
|
+
from fiddler3.schemas.project import ProjectCompactResp
|
|
8
|
+
from fiddler3.schemas.user import UserCompactResp
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ModelDeploymentResponse(BaseModel):
|
|
12
|
+
id: UUID
|
|
13
|
+
model: ModelCompactResp
|
|
14
|
+
project: ProjectCompactResp
|
|
15
|
+
organization: OrganizationCompactResp
|
|
16
|
+
artifact_type: str
|
|
17
|
+
deployment_type: str
|
|
18
|
+
active: bool
|
|
19
|
+
image_uri: str
|
|
20
|
+
replicas: int
|
|
21
|
+
cpu: int
|
|
22
|
+
memory: int
|
|
23
|
+
created_at: datetime
|
|
24
|
+
updated_at: datetime
|
|
25
|
+
created_by: UserCompactResp
|
|
26
|
+
updated_by: UserCompactResp
|
fiddler3/schemas/organization.py
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from uuid import UUID
|
|
2
3
|
|
|
3
4
|
from fiddler3.schemas.base import BaseModel
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
class
|
|
7
|
+
class OrganizationCompactResp(BaseModel):
|
|
7
8
|
id: UUID
|
|
8
9
|
name: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OrganizationResp(BaseModel):
|
|
13
|
+
id: UUID
|
|
14
|
+
name: str
|
|
15
|
+
created_at: datetime
|
|
16
|
+
updated_at: datetime
|
fiddler3/schemas/project.py
CHANGED
|
@@ -2,18 +2,18 @@ from datetime import datetime
|
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
|
|
4
4
|
from fiddler3.schemas.base import BaseModel
|
|
5
|
-
from fiddler3.schemas.organization import
|
|
5
|
+
from fiddler3.schemas.organization import OrganizationCompactResp
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class ProjectCompactResp(BaseModel):
|
|
9
9
|
id: UUID
|
|
10
10
|
name: str
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class ProjectResp(BaseModel):
|
|
14
14
|
id: UUID
|
|
15
15
|
name: str
|
|
16
16
|
created_at: datetime
|
|
17
17
|
updated_at: datetime
|
|
18
18
|
|
|
19
|
-
organization:
|
|
19
|
+
organization: OrganizationCompactResp
|
fiddler3/schemas/server_info.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Dict, Generator
|
|
|
2
2
|
|
|
3
3
|
from fiddler3.libs.semver import VersionInfo
|
|
4
4
|
from fiddler3.schemas.base import BaseModel
|
|
5
|
-
from fiddler3.schemas.organization import
|
|
5
|
+
from fiddler3.schemas.organization import OrganizationCompactResp
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Version(VersionInfo):
|
|
@@ -15,4 +15,4 @@ class Version(VersionInfo):
|
|
|
15
15
|
class ServerInfo(BaseModel):
|
|
16
16
|
feature_flags: Dict
|
|
17
17
|
server_version: Version
|
|
18
|
-
organization:
|
|
18
|
+
organization: OrganizationCompactResp
|
fiddler3/schemas/user.py
CHANGED
fiddler3/schemas/xai.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Dict, Optional, Union
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from fiddler3.constants.dataset import EnvType
|
|
7
|
+
from fiddler3.schemas.base import BaseModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RowDataSource(BaseModel):
|
|
11
|
+
source_type = 'ROW'
|
|
12
|
+
row: Dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EventIdDataSource(BaseModel):
|
|
16
|
+
source_type = 'EVENT_ID'
|
|
17
|
+
event_id: str
|
|
18
|
+
env_id: Optional[Union[str, UUID]] = Field(alias='dataset_id')
|
|
19
|
+
env_type: EnvType
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DatasetDataSource(BaseModel):
|
|
23
|
+
source_type = 'ENVIRONMENT'
|
|
24
|
+
env_type: str
|
|
25
|
+
num_samples: Optional[int]
|
|
26
|
+
env_id: Optional[Union[str, UUID]] = Field(alias='dataset_id')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SqlSliceQueryDataSource(BaseModel):
|
|
30
|
+
source_type = 'SQL_SLICE_QUERY'
|
|
31
|
+
query: str
|
|
32
|
+
num_samples: Optional[int]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import responses
|
|
6
|
+
from responses import matchers
|
|
7
|
+
|
|
8
|
+
from fiddler3.constants.baseline import BaselineType
|
|
9
|
+
from fiddler3.entities.baseline import Baseline
|
|
10
|
+
from fiddler3.exceptions import Conflict, NotFound
|
|
11
|
+
from fiddler3.tests.constants import (
|
|
12
|
+
BASELINE_ID,
|
|
13
|
+
BASELINE_NAME,
|
|
14
|
+
DATASET_ID,
|
|
15
|
+
DATASET_NAME,
|
|
16
|
+
MODEL_ID,
|
|
17
|
+
MODEL_NAME,
|
|
18
|
+
ORG_ID,
|
|
19
|
+
ORG_NAME,
|
|
20
|
+
PROJECT_ID,
|
|
21
|
+
PROJECT_NAME,
|
|
22
|
+
URL,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
API_RESPONSE_200 = {
|
|
26
|
+
'data': {
|
|
27
|
+
'id': BASELINE_ID,
|
|
28
|
+
'name': BASELINE_NAME,
|
|
29
|
+
'type': 'STATIC',
|
|
30
|
+
'start_time': None,
|
|
31
|
+
'end_time': None,
|
|
32
|
+
'offset': None,
|
|
33
|
+
'window_size': None,
|
|
34
|
+
'row_count': 20000,
|
|
35
|
+
'model': {
|
|
36
|
+
'id': MODEL_ID,
|
|
37
|
+
'name': MODEL_NAME,
|
|
38
|
+
},
|
|
39
|
+
'project': {'id': PROJECT_ID, 'name': PROJECT_NAME},
|
|
40
|
+
'organization': {
|
|
41
|
+
'id': ORG_ID,
|
|
42
|
+
'name': ORG_NAME,
|
|
43
|
+
},
|
|
44
|
+
'environment': {
|
|
45
|
+
'id': DATASET_ID,
|
|
46
|
+
'name': DATASET_NAME,
|
|
47
|
+
'type': 'PRE_PRODUCTION',
|
|
48
|
+
},
|
|
49
|
+
'created_at': '2023-10-05T18:46:40.526590+00:00',
|
|
50
|
+
'updated_at': '2023-10-05T18:46:40.526590+00:00',
|
|
51
|
+
},
|
|
52
|
+
'api_version': '3.0',
|
|
53
|
+
'kind': 'NORMAL',
|
|
54
|
+
}
|
|
55
|
+
API_RESPONSE_FROM_NAME = {
|
|
56
|
+
'data': {
|
|
57
|
+
'page_size': 100,
|
|
58
|
+
'total': 1,
|
|
59
|
+
'item_count': 1,
|
|
60
|
+
'page_count': 1,
|
|
61
|
+
'page_index': 1,
|
|
62
|
+
'offset': 0,
|
|
63
|
+
'items': [API_RESPONSE_200['data']],
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
LIST_API_RESPONSE = {
|
|
68
|
+
'data': {
|
|
69
|
+
'page_size': 100,
|
|
70
|
+
'total': 2,
|
|
71
|
+
'item_count': 2,
|
|
72
|
+
'page_count': 1,
|
|
73
|
+
'page_index': 1,
|
|
74
|
+
'offset': 0,
|
|
75
|
+
'items': [
|
|
76
|
+
API_RESPONSE_200['data'],
|
|
77
|
+
{
|
|
78
|
+
'id': 'af05646f-0cef-4638-84c9-0d195df2575f',
|
|
79
|
+
'name': 'test_baseline_2',
|
|
80
|
+
'type': 'STATIC',
|
|
81
|
+
'start_time': None,
|
|
82
|
+
'end_time': None,
|
|
83
|
+
'offset': None,
|
|
84
|
+
'window_size': None,
|
|
85
|
+
'row_count': 20000,
|
|
86
|
+
'model': {
|
|
87
|
+
'id': MODEL_ID,
|
|
88
|
+
'name': MODEL_NAME,
|
|
89
|
+
},
|
|
90
|
+
'project': {'id': PROJECT_ID, 'name': PROJECT_NAME},
|
|
91
|
+
'organization': {
|
|
92
|
+
'id': ORG_ID,
|
|
93
|
+
'name': ORG_NAME,
|
|
94
|
+
},
|
|
95
|
+
'environment': {
|
|
96
|
+
'id': DATASET_ID,
|
|
97
|
+
'name': DATASET_NAME,
|
|
98
|
+
'type': 'PRE_PRODUCTION',
|
|
99
|
+
},
|
|
100
|
+
'created_at': '2023-10-05T18:46:40.526590+00:00',
|
|
101
|
+
'updated_at': '2023-10-05T18:46:40.526590+00:00',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
API_RESPONSE_404 = {
|
|
108
|
+
'error': {
|
|
109
|
+
'code': 404,
|
|
110
|
+
'message': 'Baseline not found for the given identifier',
|
|
111
|
+
'errors': [
|
|
112
|
+
{
|
|
113
|
+
'reason': 'ObjectNotFound',
|
|
114
|
+
'message': 'Baseline not found for the given identifier',
|
|
115
|
+
'help': '',
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
API_RESPONSE_409 = {
|
|
122
|
+
'error': {
|
|
123
|
+
'code': 409,
|
|
124
|
+
'message': 'Baseline already exists',
|
|
125
|
+
'errors': [
|
|
126
|
+
{
|
|
127
|
+
'reason': 'Conflict',
|
|
128
|
+
'message': 'Baseline already exists',
|
|
129
|
+
'help': '',
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@responses.activate
|
|
137
|
+
def test_get_baseline_success() -> None:
|
|
138
|
+
responses.get(
|
|
139
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
140
|
+
json=API_RESPONSE_200,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
baseline = Baseline.get(id_=BASELINE_ID)
|
|
144
|
+
|
|
145
|
+
assert isinstance(baseline, Baseline)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@responses.activate
|
|
149
|
+
def test_get_baseline_not_found() -> None:
|
|
150
|
+
responses.get(
|
|
151
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
152
|
+
json=API_RESPONSE_404,
|
|
153
|
+
status=HTTPStatus.NOT_FOUND,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
with pytest.raises(NotFound):
|
|
157
|
+
Baseline.get(id_=BASELINE_ID)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@responses.activate
|
|
161
|
+
def test_baseline_from_name() -> None:
|
|
162
|
+
params = {
|
|
163
|
+
'filter': '{"condition": "AND", "rules": [{"field": "name", "operator": "equal", "value": "test_baseline"}, {"field": "project_name", "operator": "equal", "value": "bank_churn"}, {"field": "model_name", "operator": "equal", "value": "bank_churn"}]}'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
responses.get(
|
|
167
|
+
url=f'{URL}/v3/baselines',
|
|
168
|
+
json=API_RESPONSE_FROM_NAME,
|
|
169
|
+
match=[matchers.query_param_matcher(params)],
|
|
170
|
+
)
|
|
171
|
+
baseline = Baseline.from_name(
|
|
172
|
+
name=BASELINE_NAME,
|
|
173
|
+
model_name=MODEL_NAME,
|
|
174
|
+
project_name=PROJECT_NAME,
|
|
175
|
+
)
|
|
176
|
+
assert isinstance(baseline, Baseline)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@responses.activate
|
|
180
|
+
def test_baseline_from_name_not_found() -> None:
|
|
181
|
+
resp = API_RESPONSE_FROM_NAME.copy()
|
|
182
|
+
resp['data']['total'] = 0
|
|
183
|
+
resp['data']['item_count'] = 0
|
|
184
|
+
resp['data']['items'] = []
|
|
185
|
+
|
|
186
|
+
params = {
|
|
187
|
+
'filter': '{"condition": "AND", "rules": [{"field": "name", "operator": "equal", "value": "test_baseline"}, {"field": "project_name", "operator": "equal", "value": "bank_churn"}, {"field": "model_name", "operator": "equal", "value": "bank_churn"}]}'
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
responses.get(
|
|
191
|
+
url=f'{URL}/v3/baselines',
|
|
192
|
+
json=resp,
|
|
193
|
+
match=[matchers.query_param_matcher(params)],
|
|
194
|
+
)
|
|
195
|
+
with pytest.raises(NotFound):
|
|
196
|
+
Baseline.from_name(
|
|
197
|
+
name=BASELINE_NAME,
|
|
198
|
+
model_name=MODEL_NAME,
|
|
199
|
+
project_name=PROJECT_NAME,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@responses.activate
|
|
204
|
+
def test_baseline_list_success() -> None:
|
|
205
|
+
responses.get(
|
|
206
|
+
url=f'{URL}/v3/baselines',
|
|
207
|
+
json=LIST_API_RESPONSE,
|
|
208
|
+
)
|
|
209
|
+
for baseline in Baseline.list():
|
|
210
|
+
assert isinstance(baseline, Baseline)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@responses.activate
|
|
214
|
+
def test_baseline_list_empty() -> None:
|
|
215
|
+
resp = API_RESPONSE_FROM_NAME.copy()
|
|
216
|
+
resp['data']['total'] = 0
|
|
217
|
+
resp['data']['item_count'] = 0
|
|
218
|
+
resp['data']['items'] = []
|
|
219
|
+
|
|
220
|
+
responses.get(
|
|
221
|
+
url=f'{URL}/v3/baselines',
|
|
222
|
+
json=resp,
|
|
223
|
+
)
|
|
224
|
+
assert len(list(Baseline.list())) == 0
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@responses.activate
|
|
228
|
+
def test_add_baseline_success() -> None:
|
|
229
|
+
responses.post(
|
|
230
|
+
url=f'{URL}/v3/baselines',
|
|
231
|
+
json=API_RESPONSE_200,
|
|
232
|
+
)
|
|
233
|
+
baseline = Baseline(
|
|
234
|
+
name=BASELINE_NAME,
|
|
235
|
+
model_id=MODEL_ID,
|
|
236
|
+
environment='PRE_PRODUCTION',
|
|
237
|
+
dataset_id=DATASET_ID,
|
|
238
|
+
type_=BaselineType.STATIC,
|
|
239
|
+
).create()
|
|
240
|
+
|
|
241
|
+
assert isinstance(baseline, Baseline)
|
|
242
|
+
assert baseline.id == UUID(BASELINE_ID)
|
|
243
|
+
assert baseline.name == BASELINE_NAME
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@responses.activate
|
|
247
|
+
def test_add_baseline_conflict() -> None:
|
|
248
|
+
responses.post(
|
|
249
|
+
url=f'{URL}/v3/baselines', json=API_RESPONSE_409, status=HTTPStatus.CONFLICT
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
with pytest.raises(Conflict):
|
|
253
|
+
Baseline(
|
|
254
|
+
name=BASELINE_NAME,
|
|
255
|
+
model_id=MODEL_ID,
|
|
256
|
+
environment='PRE_PRODUCTION',
|
|
257
|
+
dataset_id=DATASET_ID,
|
|
258
|
+
type_=BaselineType.STATIC,
|
|
259
|
+
).create()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@responses.activate
|
|
263
|
+
def test_delete_baseline() -> None:
|
|
264
|
+
responses.get(
|
|
265
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
266
|
+
json=API_RESPONSE_200,
|
|
267
|
+
)
|
|
268
|
+
baseline = Baseline.get(id_=BASELINE_ID)
|
|
269
|
+
|
|
270
|
+
responses.delete(
|
|
271
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
baseline.delete()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@responses.activate
|
|
278
|
+
def test_delete_baseline_not_found() -> None:
|
|
279
|
+
responses.get(
|
|
280
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
281
|
+
json=API_RESPONSE_200,
|
|
282
|
+
)
|
|
283
|
+
baseline = Baseline.get(id_=BASELINE_ID)
|
|
284
|
+
|
|
285
|
+
responses.delete(
|
|
286
|
+
url=f'{URL}/v3/baselines/{BASELINE_ID}',
|
|
287
|
+
json=API_RESPONSE_404,
|
|
288
|
+
status=HTTPStatus.NOT_FOUND,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
with pytest.raises(NotFound):
|
|
292
|
+
baseline.delete()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import responses
|
|
5
|
+
from responses import matchers
|
|
6
|
+
|
|
7
|
+
from fiddler3.entities.dataset import Dataset
|
|
8
|
+
from fiddler3.exceptions import NotFound
|
|
9
|
+
from fiddler3.tests.constants import (
|
|
10
|
+
DATASET_ID,
|
|
11
|
+
DATASET_NAME,
|
|
12
|
+
MODEL_ID,
|
|
13
|
+
MODEL_NAME,
|
|
14
|
+
ORG_ID,
|
|
15
|
+
ORG_NAME,
|
|
16
|
+
PROJECT_ID,
|
|
17
|
+
PROJECT_NAME,
|
|
18
|
+
URL,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
API_RESPONSE_200 = {
|
|
22
|
+
'data': {
|
|
23
|
+
'id': DATASET_ID,
|
|
24
|
+
'name': DATASET_NAME,
|
|
25
|
+
'type': 'PRE_PRODUCTION',
|
|
26
|
+
'row_count': 10000,
|
|
27
|
+
'model': {'id': MODEL_ID, 'name': MODEL_NAME},
|
|
28
|
+
'project': {'id': PROJECT_ID, 'name': PROJECT_NAME},
|
|
29
|
+
'organization': {'id': ORG_ID, 'name': ORG_NAME},
|
|
30
|
+
'created_at': '2023-09-19T13:27:03.684345+00:00',
|
|
31
|
+
'updated_at': '2023-09-19T13:27:03.684345+00:00',
|
|
32
|
+
},
|
|
33
|
+
'api_version': '3.0',
|
|
34
|
+
'kind': 'NORMAL',
|
|
35
|
+
}
|
|
36
|
+
API_RESPONSE_FROM_NAME = {
|
|
37
|
+
'data': {
|
|
38
|
+
'page_size': 100,
|
|
39
|
+
'total': 1,
|
|
40
|
+
'item_count': 1,
|
|
41
|
+
'page_count': 1,
|
|
42
|
+
'page_index': 1,
|
|
43
|
+
'offset': 0,
|
|
44
|
+
'items': [API_RESPONSE_200['data']],
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
LIST_API_RESPONSE = {
|
|
49
|
+
'data': {
|
|
50
|
+
'page_size': 100,
|
|
51
|
+
'total': 2,
|
|
52
|
+
'item_count': 2,
|
|
53
|
+
'page_count': 1,
|
|
54
|
+
'page_index': 1,
|
|
55
|
+
'offset': 0,
|
|
56
|
+
'items': [
|
|
57
|
+
API_RESPONSE_200['data'],
|
|
58
|
+
{
|
|
59
|
+
'id': '2531bfd9-2ca2-4a7b-bb5a-136c8da09ca1',
|
|
60
|
+
'name': 'dataset2',
|
|
61
|
+
'type': 'PRE_PRODUCTION',
|
|
62
|
+
'row_count': 10000,
|
|
63
|
+
'model': {'id': MODEL_ID, 'name': MODEL_NAME},
|
|
64
|
+
'project': {'id': PROJECT_ID, 'name': PROJECT_NAME},
|
|
65
|
+
'organization': {'id': ORG_ID, 'name': ORG_NAME},
|
|
66
|
+
'created_at': '2023-09-19T13:27:03.684345+00:00',
|
|
67
|
+
'updated_at': '2023-09-19T13:27:03.684345+00:00',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
API_RESPONSE_404 = {
|
|
74
|
+
'error': {
|
|
75
|
+
'code': 404,
|
|
76
|
+
'message': 'Environment not found for the given identifier',
|
|
77
|
+
'errors': [
|
|
78
|
+
{
|
|
79
|
+
'reason': 'ObjectNotFound',
|
|
80
|
+
'message': 'Environment not found for the given identifier',
|
|
81
|
+
'help': '',
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@responses.activate
|
|
89
|
+
def test_get_dataset_success() -> None:
|
|
90
|
+
responses.get(
|
|
91
|
+
url=f'{URL}/v3/environments/{DATASET_ID}',
|
|
92
|
+
json=API_RESPONSE_200,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
dataset = Dataset.get(id_=DATASET_ID)
|
|
96
|
+
assert isinstance(dataset, Dataset)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@responses.activate
|
|
100
|
+
def test_get_dataset_not_found() -> None:
|
|
101
|
+
responses.get(
|
|
102
|
+
url=f'{URL}/v3/environments/{DATASET_ID}',
|
|
103
|
+
json=API_RESPONSE_404,
|
|
104
|
+
status=HTTPStatus.NOT_FOUND,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
with pytest.raises(NotFound):
|
|
108
|
+
Dataset.get(id_=DATASET_ID)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@responses.activate
|
|
112
|
+
def test_dataset_from_name() -> None:
|
|
113
|
+
params = {
|
|
114
|
+
'filter': '{"condition": "AND", "rules": [{"field": "name", "operator": "equal", "value": "dataset3"}, {"field": "project_name", "operator": "equal", "value": "bank_churn"}, {"field": "model_name", "operator": "equal", "value": "bank_churn"}]}'
|
|
115
|
+
}
|
|
116
|
+
responses.get(
|
|
117
|
+
url=f'{URL}/v3/environments',
|
|
118
|
+
json=API_RESPONSE_FROM_NAME,
|
|
119
|
+
match=[matchers.query_param_matcher(params)],
|
|
120
|
+
)
|
|
121
|
+
dataset = Dataset.from_name(
|
|
122
|
+
name=DATASET_NAME,
|
|
123
|
+
model_name=MODEL_NAME,
|
|
124
|
+
project_name=PROJECT_NAME,
|
|
125
|
+
)
|
|
126
|
+
assert isinstance(dataset, Dataset)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@responses.activate
|
|
130
|
+
def test_dataset_from_name_not_found() -> None:
|
|
131
|
+
resp = API_RESPONSE_FROM_NAME.copy()
|
|
132
|
+
resp['data']['total'] = 0
|
|
133
|
+
resp['data']['item_count'] = 0
|
|
134
|
+
resp['data']['items'] = []
|
|
135
|
+
|
|
136
|
+
params = {
|
|
137
|
+
'filter': '{"condition": "AND", "rules": [{"field": "name", "operator": "equal", "value": "dataset3"}, {"field": "project_name", "operator": "equal", "value": "bank_churn"}, {"field": "model_name", "operator": "equal", "value": "bank_churn"}]}'
|
|
138
|
+
}
|
|
139
|
+
responses.get(
|
|
140
|
+
url=f'{URL}/v3/environments',
|
|
141
|
+
json=resp,
|
|
142
|
+
match=[matchers.query_param_matcher(params)],
|
|
143
|
+
)
|
|
144
|
+
with pytest.raises(NotFound):
|
|
145
|
+
Dataset.from_name(
|
|
146
|
+
name=DATASET_NAME,
|
|
147
|
+
model_name=MODEL_NAME,
|
|
148
|
+
project_name=PROJECT_NAME,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@responses.activate
|
|
153
|
+
def test_dataset_list_success() -> None:
|
|
154
|
+
responses.get(
|
|
155
|
+
url=f'{URL}/v3/environments',
|
|
156
|
+
json=LIST_API_RESPONSE,
|
|
157
|
+
)
|
|
158
|
+
for dataset in Dataset.list():
|
|
159
|
+
assert isinstance(dataset, Dataset)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@responses.activate
|
|
163
|
+
def test_dataset_list_empty() -> None:
|
|
164
|
+
resp = API_RESPONSE_FROM_NAME.copy()
|
|
165
|
+
resp['data']['total'] = 0
|
|
166
|
+
resp['data']['item_count'] = 0
|
|
167
|
+
resp['data']['items'] = []
|
|
168
|
+
|
|
169
|
+
responses.get(
|
|
170
|
+
url=f'{URL}/v3/environments',
|
|
171
|
+
json=resp,
|
|
172
|
+
)
|
|
173
|
+
assert len(list(Dataset.list())) == 0
|