fiddler-client 3.5.0.dev2__py3-none-any.whl → 3.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiddler/VERSION +1 -1
- fiddler/__init__.py +0 -2
- fiddler/entities/job.py +45 -10
- fiddler/entities/xai.py +19 -81
- fiddler/exceptions.py +12 -3
- fiddler/libs/http_client.py +33 -21
- fiddler/schemas/xai.py +0 -6
- fiddler/tests/apis/test_xai.py +0 -108
- {fiddler_client-3.5.0.dev2.dist-info → fiddler_client-3.6.0.dist-info}/METADATA +1 -1
- {fiddler_client-3.5.0.dev2.dist-info → fiddler_client-3.6.0.dist-info}/RECORD +13 -13
- {fiddler_client-3.5.0.dev2.dist-info → fiddler_client-3.6.0.dist-info}/WHEEL +1 -1
- {fiddler_client-3.5.0.dev2.dist-info → fiddler_client-3.6.0.dist-info}/LICENSE.txt +0 -0
- {fiddler_client-3.5.0.dev2.dist-info → fiddler_client-3.6.0.dist-info}/top_level.txt +0 -0
fiddler/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.6.0
|
fiddler/__init__.py
CHANGED
|
@@ -57,7 +57,6 @@ from fiddler.schemas.xai import ( # noqa
|
|
|
57
57
|
DatasetDataSource,
|
|
58
58
|
EventIdDataSource,
|
|
59
59
|
RowDataSource,
|
|
60
|
-
SqlSliceQueryDataSource,
|
|
61
60
|
)
|
|
62
61
|
from fiddler.schemas.xai_params import XaiParams # noqa
|
|
63
62
|
from fiddler.utils.helpers import group_by # noqa
|
|
@@ -110,7 +109,6 @@ __all__ = [
|
|
|
110
109
|
'ModelTaskParams',
|
|
111
110
|
'Multivariate',
|
|
112
111
|
'RowDataSource',
|
|
113
|
-
'SqlSliceQueryDataSource',
|
|
114
112
|
'TextEmbedding',
|
|
115
113
|
'VectorFeature',
|
|
116
114
|
'XaiParams',
|
fiddler/entities/job.py
CHANGED
|
@@ -4,6 +4,8 @@ import time
|
|
|
4
4
|
from typing import Iterator
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
|
+
import requests
|
|
8
|
+
|
|
7
9
|
from fiddler.configs import JOB_POLL_INTERVAL, JOB_WAIT_TIMEOUT
|
|
8
10
|
from fiddler.constants.job import JobStatus
|
|
9
11
|
from fiddler.decorators import handle_api_error
|
|
@@ -112,24 +114,57 @@ class Job(BaseEntity): # pylint: disable=too-many-instance-attributes
|
|
|
112
114
|
:return: Iterator of job objects
|
|
113
115
|
"""
|
|
114
116
|
assert self.id is not None
|
|
117
|
+
deadline = time.monotonic() + timeout
|
|
115
118
|
|
|
116
|
-
start_time = time.time()
|
|
117
119
|
while True:
|
|
120
|
+
|
|
121
|
+
if time.monotonic() > deadline:
|
|
122
|
+
raise TimeoutError(f'Deadline exceeded while watching job {self.id}')
|
|
123
|
+
|
|
118
124
|
try:
|
|
119
|
-
|
|
125
|
+
|
|
126
|
+
# This can raise requests.HTTPError to represent non-2xx
|
|
127
|
+
# responses.
|
|
128
|
+
response = self._client().get(
|
|
129
|
+
url=self._get_url(id_=self.id),
|
|
130
|
+
# Short-ish TCP connect timeout, to stay responsive in
|
|
131
|
+
# terms of logging. The HTTP response latency for GET
|
|
132
|
+
# /jobs/<id> is expected to be less than couple of seconds
|
|
133
|
+
# (i.e., 30 s includes lots of leeway).
|
|
134
|
+
timeout=(5, 30),
|
|
135
|
+
# Future: have centralized, automatic retrying in our
|
|
136
|
+
# client logic which we want to disable here because we
|
|
137
|
+
# effectively are in a retry loop, e.g. via passing
|
|
138
|
+
# `retry_strategy="off"`.
|
|
139
|
+
)
|
|
120
140
|
self._refresh_from_response(response)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
|
|
142
|
+
except requests.exceptions.HTTPError as exc:
|
|
143
|
+
# Note(JP): got a non-2xx HTTP response. The main purpose of
|
|
144
|
+
# this handler is to keep going after having received a 5xx
|
|
145
|
+
# response. In this case of GETting job status even some 404s
|
|
146
|
+
# might be worth retrying. That is, it's fine to give up only
|
|
147
|
+
# after reaching the deadline even if we collect some 4xx
|
|
148
|
+
# responses along the way. Noteworthy: Receiving a 5xx response
|
|
149
|
+
# here is not an error, it's an expected scenario to be
|
|
150
|
+
# accounted for.
|
|
151
|
+
logger.info(
|
|
152
|
+
'watch: ignore unexpected response %s (URL: %s, response body prefix: %s...)',
|
|
153
|
+
exc.response,
|
|
154
|
+
exc.request.url,
|
|
155
|
+
exc.response.text[:120],
|
|
125
156
|
)
|
|
126
157
|
continue
|
|
127
158
|
|
|
128
|
-
|
|
159
|
+
except requests.exceptions.RequestException:
|
|
160
|
+
# This error is in the hierarchy _above_ `HTTPError`, and in
|
|
161
|
+
# this setup catches all errors that are _not_ a bad response:
|
|
162
|
+
# DNS error, TCP connect timeout, err during sending request,
|
|
163
|
+
# err during receiving response. Rely on the error detail to
|
|
164
|
+
# have already been logged.
|
|
165
|
+
continue
|
|
129
166
|
|
|
130
|
-
|
|
131
|
-
if (current_time - start_time) > timeout:
|
|
132
|
-
raise TimeoutError(f'Timed out while watching job {self.id}')
|
|
167
|
+
yield self
|
|
133
168
|
|
|
134
169
|
if self.status in [
|
|
135
170
|
JobStatus.SUCCESS,
|
fiddler/entities/xai.py
CHANGED
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
from collections import namedtuple
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
|
-
from io import BytesIO
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from typing import Any, Callable
|
|
9
8
|
from uuid import UUID
|
|
@@ -18,15 +17,10 @@ from fiddler.constants.xai import (
|
|
|
18
17
|
)
|
|
19
18
|
from fiddler.decorators import handle_api_error
|
|
20
19
|
from fiddler.entities.job import Job
|
|
20
|
+
from fiddler.exceptions import Unsupported
|
|
21
21
|
from fiddler.schemas.job import JobCompactResp
|
|
22
|
-
from fiddler.schemas.xai import
|
|
23
|
-
DatasetDataSource,
|
|
24
|
-
EventIdDataSource,
|
|
25
|
-
RowDataSource,
|
|
26
|
-
SqlSliceQueryDataSource,
|
|
27
|
-
)
|
|
22
|
+
from fiddler.schemas.xai import DatasetDataSource, EventIdDataSource, RowDataSource
|
|
28
23
|
from fiddler.utils.decorators import check_version
|
|
29
|
-
from fiddler.utils.helpers import try_series_retype
|
|
30
24
|
from fiddler.utils.logger import get_logger
|
|
31
25
|
|
|
32
26
|
logger = get_logger(__name__)
|
|
@@ -44,7 +38,7 @@ class XaiMixin:
|
|
|
44
38
|
def explain( # pylint: disable=too-many-arguments
|
|
45
39
|
self,
|
|
46
40
|
input_data_source: RowDataSource | EventIdDataSource,
|
|
47
|
-
ref_data_source: DatasetDataSource |
|
|
41
|
+
ref_data_source: DatasetDataSource | None = None,
|
|
48
42
|
method: ExplainMethod | str = ExplainMethod.FIDDLER_SHAP,
|
|
49
43
|
num_permutations: int | None = None,
|
|
50
44
|
ci_level: float | None = None,
|
|
@@ -55,8 +49,8 @@ class XaiMixin:
|
|
|
55
49
|
|
|
56
50
|
:param input_data_source: DataSource for the input data to compute explanation
|
|
57
51
|
on (RowDataSource, EventIdDataSource)
|
|
58
|
-
:param ref_data_source:
|
|
59
|
-
on
|
|
52
|
+
:param ref_data_source: Dataset data source for the reference data to compute explanation
|
|
53
|
+
on.
|
|
60
54
|
Only used for non-text models and the following methods:
|
|
61
55
|
'SHAP', 'FIDDLER_SHAP', 'PERMUTE', 'MEAN_RESET'
|
|
62
56
|
:param method: Explanation method name. Could be your custom
|
|
@@ -102,7 +96,7 @@ class XaiMixin:
|
|
|
102
96
|
sample: bool = False,
|
|
103
97
|
max_rows: int | None = None,
|
|
104
98
|
columns: list[str] | None = None,
|
|
105
|
-
) ->
|
|
99
|
+
) -> None:
|
|
106
100
|
"""
|
|
107
101
|
Fetch data with slice query.
|
|
108
102
|
|
|
@@ -111,35 +105,12 @@ class XaiMixin:
|
|
|
111
105
|
columns to select overriding columns selected in the query.
|
|
112
106
|
:param max_rows: Number of maximum rows to fetch
|
|
113
107
|
:param sample: Whether rows should be sample or not from the database
|
|
114
|
-
|
|
115
108
|
:return: Dataframe of the query output
|
|
116
109
|
"""
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
payload: dict[str, Any] = {
|
|
120
|
-
'model_id': self.id,
|
|
121
|
-
'query': query,
|
|
122
|
-
'sample': sample,
|
|
123
|
-
}
|
|
124
|
-
if max_rows:
|
|
125
|
-
payload['max_rows'] = max_rows
|
|
126
|
-
if columns:
|
|
127
|
-
payload['columns'] = columns
|
|
128
|
-
|
|
129
|
-
response = self._client().post(
|
|
130
|
-
url='/v3/slice-query/fetch',
|
|
131
|
-
data=payload,
|
|
110
|
+
raise Unsupported(
|
|
111
|
+
'This method is not supported since version 3.4. Please use `model.download_data` instead.'
|
|
132
112
|
)
|
|
133
113
|
|
|
134
|
-
response_dict = response.json()['data']
|
|
135
|
-
|
|
136
|
-
column_names = response_dict['metadata']['columns']
|
|
137
|
-
dtype_strings = response_dict['metadata']['dtypes']
|
|
138
|
-
df = pd.DataFrame(response_dict['rows'], columns=column_names)
|
|
139
|
-
for column_name, dtype in zip(column_names, dtype_strings):
|
|
140
|
-
df[column_name] = try_series_retype(df[column_name], dtype)
|
|
141
|
-
return df
|
|
142
|
-
|
|
143
114
|
@handle_api_error
|
|
144
115
|
def download_data( # pylint: disable=too-many-arguments
|
|
145
116
|
self,
|
|
@@ -250,25 +221,9 @@ class XaiMixin:
|
|
|
250
221
|
:param max_rows: Number of maximum rows to fetch
|
|
251
222
|
:param sample: Whether rows should be sample or not from the database
|
|
252
223
|
"""
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
os.makedirs(output_dir)
|
|
257
|
-
payload: dict[str, Any] = {
|
|
258
|
-
'model_id': self.id,
|
|
259
|
-
'query': query,
|
|
260
|
-
'sample': sample,
|
|
261
|
-
}
|
|
262
|
-
if max_rows:
|
|
263
|
-
payload['max_rows'] = max_rows
|
|
264
|
-
if columns:
|
|
265
|
-
payload['columns'] = columns
|
|
266
|
-
|
|
267
|
-
file_path = os.path.join(output_dir, 'output.parquet')
|
|
268
|
-
with self._client().post(url='/v3/slice-query/download', data=payload) as resp:
|
|
269
|
-
# Download parquet file
|
|
270
|
-
df = pd.read_parquet(BytesIO(resp.content))
|
|
271
|
-
df.to_parquet(file_path)
|
|
224
|
+
raise Unsupported(
|
|
225
|
+
'This method is not supported since version 3.4. Please use `model.download_data` instead.'
|
|
226
|
+
)
|
|
272
227
|
|
|
273
228
|
@handle_api_error
|
|
274
229
|
def get_mutual_info(
|
|
@@ -277,7 +232,7 @@ class XaiMixin:
|
|
|
277
232
|
column_name: str,
|
|
278
233
|
num_samples: int | None = None,
|
|
279
234
|
normalized: bool = False,
|
|
280
|
-
) ->
|
|
235
|
+
) -> None:
|
|
281
236
|
"""
|
|
282
237
|
Get mutual information.
|
|
283
238
|
|
|
@@ -290,27 +245,10 @@ class XaiMixin:
|
|
|
290
245
|
all the variables in the dataset.
|
|
291
246
|
:param num_samples: Number of samples to select for computation
|
|
292
247
|
:param normalized: If set to True, it will compute Normalized Mutual Information
|
|
293
|
-
|
|
294
248
|
:return: a dictionary of mutual information w.r.t the given feature
|
|
295
249
|
for each column given
|
|
296
250
|
"""
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
payload: dict[str, Any] = {
|
|
300
|
-
'model_id': self.id,
|
|
301
|
-
'query': query,
|
|
302
|
-
'column_name': column_name,
|
|
303
|
-
'normalized': normalized,
|
|
304
|
-
}
|
|
305
|
-
if num_samples:
|
|
306
|
-
payload['num_samples'] = num_samples
|
|
307
|
-
|
|
308
|
-
response = self._client().post(
|
|
309
|
-
url='/v3/analytics/mutual-info',
|
|
310
|
-
data=payload,
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
return response.json()['data']
|
|
251
|
+
raise Unsupported('This method is not supported since version 3.4.')
|
|
314
252
|
|
|
315
253
|
@handle_api_error
|
|
316
254
|
def predict(
|
|
@@ -344,7 +282,7 @@ class XaiMixin:
|
|
|
344
282
|
@handle_api_error
|
|
345
283
|
def get_feature_impact( # pylint: disable=too-many-arguments
|
|
346
284
|
self,
|
|
347
|
-
data_source: DatasetDataSource
|
|
285
|
+
data_source: DatasetDataSource,
|
|
348
286
|
num_iterations: int | None = None,
|
|
349
287
|
num_refs: int | None = None,
|
|
350
288
|
ci_level: float | None = None,
|
|
@@ -354,8 +292,8 @@ class XaiMixin:
|
|
|
354
292
|
"""
|
|
355
293
|
Get global feature impact for a model over a dataset or a slice.
|
|
356
294
|
|
|
357
|
-
:param data_source:
|
|
358
|
-
impact on
|
|
295
|
+
:param data_source: Dataset data Source for the input dataset to compute feature
|
|
296
|
+
impact on
|
|
359
297
|
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
360
298
|
:param num_refs: The number of reference points used in the explanation
|
|
361
299
|
:param ci_level: The confidence level (between 0 and 1)
|
|
@@ -398,7 +336,7 @@ class XaiMixin:
|
|
|
398
336
|
@handle_api_error
|
|
399
337
|
def get_feature_importance( # pylint: disable=too-many-arguments
|
|
400
338
|
self,
|
|
401
|
-
data_source: DatasetDataSource
|
|
339
|
+
data_source: DatasetDataSource,
|
|
402
340
|
num_iterations: int | None = None,
|
|
403
341
|
num_refs: int | None = None,
|
|
404
342
|
ci_level: float | None = None,
|
|
@@ -406,8 +344,8 @@ class XaiMixin:
|
|
|
406
344
|
"""
|
|
407
345
|
Get global feature importance for a model over a dataset or a slice.
|
|
408
346
|
|
|
409
|
-
:param data_source:
|
|
410
|
-
importance on
|
|
347
|
+
:param data_source: Dataset data aSource for the input dataset to compute feature
|
|
348
|
+
importance on
|
|
411
349
|
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
412
350
|
:param num_refs: The number of reference points used in the explanation
|
|
413
351
|
:param ci_level: The confidence level (between 0 and 1)
|
fiddler/exceptions.py
CHANGED
|
@@ -53,17 +53,26 @@ class Unsupported(BaseError):
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class HttpError(BaseError):
|
|
56
|
-
"""Base class for all HTTP errors
|
|
56
|
+
"""Base class for all HTTP errors
|
|
57
|
+
|
|
58
|
+
Deprecated. Not thrown anymore.
|
|
59
|
+
"""
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
class ConnTimeout(HttpError):
|
|
60
|
-
"""Connection timeout error
|
|
63
|
+
"""Connection timeout error
|
|
64
|
+
|
|
65
|
+
Deprecated. Not thrown anymore.
|
|
66
|
+
"""
|
|
61
67
|
|
|
62
68
|
message = 'Request timed out while trying to reach endpoint'
|
|
63
69
|
|
|
64
70
|
|
|
65
71
|
class ConnError(HttpError):
|
|
66
|
-
"""Connection error
|
|
72
|
+
"""Connection error
|
|
73
|
+
|
|
74
|
+
Deprecated. Not thrown anymore.
|
|
75
|
+
"""
|
|
67
76
|
|
|
68
77
|
message = 'Unable to reach the given endpoint'
|
|
69
78
|
|
fiddler/libs/http_client.py
CHANGED
|
@@ -10,8 +10,6 @@ from requests.adapters import HTTPAdapter
|
|
|
10
10
|
|
|
11
11
|
from fiddler.constants.common import JSON_CONTENT_TYPE
|
|
12
12
|
from fiddler.exceptions import ( # pylint: disable=redefined-builtin
|
|
13
|
-
ConnError,
|
|
14
|
-
ConnTimeout,
|
|
15
13
|
HttpError,
|
|
16
14
|
)
|
|
17
15
|
from fiddler.libs.json_encoder import RequestClientJSONEncoder
|
|
@@ -49,11 +47,11 @@ class RequestClient:
|
|
|
49
47
|
params: dict | None = None,
|
|
50
48
|
headers: dict | None = None,
|
|
51
49
|
data: dict | bytes | None = None,
|
|
52
|
-
timeout:
|
|
50
|
+
timeout: float | tuple[float, float] | None = None,
|
|
53
51
|
**kwargs: Any,
|
|
54
52
|
) -> requests.Response:
|
|
55
53
|
"""
|
|
56
|
-
|
|
54
|
+
Emit HTTP request.
|
|
57
55
|
|
|
58
56
|
:param method: HTTP method like
|
|
59
57
|
:param url: API endpoint
|
|
@@ -62,7 +60,7 @@ class RequestClient:
|
|
|
62
60
|
:param data: Dict/binary data
|
|
63
61
|
:param timeout: Request timeout in seconds
|
|
64
62
|
"""
|
|
65
|
-
logger.debug('
|
|
63
|
+
logger.debug('next: HTTP %s %s', method, url)
|
|
66
64
|
|
|
67
65
|
full_url = urljoin(self.base_url, url)
|
|
68
66
|
|
|
@@ -81,8 +79,9 @@ class RequestClient:
|
|
|
81
79
|
# verify param in kwargs when REQUESTS_CA_BUNDLE is set.
|
|
82
80
|
# So setting that as default here
|
|
83
81
|
kwargs.setdefault('verify', self.session.verify)
|
|
82
|
+
|
|
84
83
|
try:
|
|
85
|
-
|
|
84
|
+
resp = self.session.request(
|
|
86
85
|
method,
|
|
87
86
|
full_url,
|
|
88
87
|
params=params,
|
|
@@ -92,20 +91,33 @@ class RequestClient:
|
|
|
92
91
|
proxies=self.proxies,
|
|
93
92
|
**kwargs,
|
|
94
93
|
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
except requests.exceptions.RequestException as exc:
|
|
95
|
+
# Note(JP): we did not get a response. An error happened before
|
|
96
|
+
# sending the request, while sending the request, while waiting for
|
|
97
|
+
# response, or while receiving the response. A few examples for
|
|
98
|
+
# common errors handled here:
|
|
99
|
+
# - DNS resolution error
|
|
100
|
+
# - TCP connect() timeout
|
|
101
|
+
# - Timeout while waiting for the other end to start sending the
|
|
102
|
+
# HTTP response (after having sent the request).
|
|
103
|
+
# - RECV timeout between trying to receive two response bytes.
|
|
104
|
+
#
|
|
105
|
+
# TODO: add centralized default retrying here, at least for GET
|
|
106
|
+
# (conservative, I understand we initially may not want to retry
|
|
107
|
+
# other methods).
|
|
108
|
+
#
|
|
109
|
+
# For now, have centralized logging and re-raise to give callers
|
|
110
|
+
# the opportunity to implement a local error handler, but not
|
|
111
|
+
# having to worry about logging the full error message.
|
|
112
|
+
logger.info("http: %s %s failed: %s", method, full_url, exc)
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
# Raise requests.HTTPError for non-2xx responses, caller can inspect
|
|
116
|
+
# response status code from the exception object via
|
|
117
|
+
# e.g. exc.response.status_code.
|
|
118
|
+
resp.raise_for_status()
|
|
119
|
+
|
|
120
|
+
return resp
|
|
109
121
|
|
|
110
122
|
def get(
|
|
111
123
|
self,
|
|
@@ -113,7 +125,7 @@ class RequestClient:
|
|
|
113
125
|
url: str,
|
|
114
126
|
params: dict | None = None,
|
|
115
127
|
headers: dict | None = None,
|
|
116
|
-
timeout:
|
|
128
|
+
timeout: float | tuple[float, float] | None = None,
|
|
117
129
|
**kwargs: dict[str, Any],
|
|
118
130
|
) -> requests.Response:
|
|
119
131
|
"""Construct a get request instance."""
|
fiddler/schemas/xai.py
CHANGED
|
@@ -24,9 +24,3 @@ class DatasetDataSource(BaseModel):
|
|
|
24
24
|
env_type: str
|
|
25
25
|
num_samples: Optional[int]
|
|
26
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]
|
fiddler/tests/apis/test_xai.py
CHANGED
|
@@ -219,48 +219,6 @@ EXPLAIN_RESPONSE_200 = {
|
|
|
219
219
|
'kind': 'NORMAL',
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
FETCH_SLICE_RESPONSE_200 = {
|
|
223
|
-
'data': {
|
|
224
|
-
'metadata': {
|
|
225
|
-
'query': "SELECT * FROM test_bank_churn.bank_churn WHERE geography='France' order by balance desc LIMIT 2",
|
|
226
|
-
'is_slice': True,
|
|
227
|
-
'columns': ['Age'],
|
|
228
|
-
'dtypes': ['int'],
|
|
229
|
-
'model': {
|
|
230
|
-
'id': '36bc3613-49d5-46b5-a0e0-a45c3aa4a9d9',
|
|
231
|
-
'name': 'bank_churn',
|
|
232
|
-
},
|
|
233
|
-
'env': {
|
|
234
|
-
'id': '86e59334-2b86-4726-a970-6900db46e437',
|
|
235
|
-
'type': 'PRE_PRODUCTION',
|
|
236
|
-
'name': 'test_bank_churn',
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
'rows': [[57], [37]],
|
|
240
|
-
},
|
|
241
|
-
'api_version': '3.0',
|
|
242
|
-
'kind': 'NORMAL',
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
MUTUAL_INFO_RESPONSE_200 = {
|
|
246
|
-
'data': {
|
|
247
|
-
'CreditScore': 0.009774993816146854,
|
|
248
|
-
'Geography': 1.0387361759590092,
|
|
249
|
-
'Gender': 0.000345600896640319,
|
|
250
|
-
'Age': 0.006747713128151839,
|
|
251
|
-
'Tenure': 0.0017159873190972647,
|
|
252
|
-
'Balance': 0.14278016623398132,
|
|
253
|
-
'NumOfProducts': 0.002155101383831244,
|
|
254
|
-
'HasCrCard': 0.00011152799557884174,
|
|
255
|
-
'IsActiveMember': 0.00026523254634630566,
|
|
256
|
-
'EstimatedSalary': 0.010603971349228138,
|
|
257
|
-
'probability_churned': 0.06073798415644517,
|
|
258
|
-
'Decisions': 2.3682864881247045e-05,
|
|
259
|
-
'Churned': 0.014017045772466472,
|
|
260
|
-
},
|
|
261
|
-
'api_version': '3.0',
|
|
262
|
-
'kind': 'NORMAL',
|
|
263
|
-
}
|
|
264
222
|
|
|
265
223
|
PREDICT_RESPONSE_200 = {
|
|
266
224
|
'data': {'predictions': [{'predicted_quality': 5.759617514660622}]},
|
|
@@ -555,27 +513,6 @@ def test_explain() -> None:
|
|
|
555
513
|
)
|
|
556
514
|
|
|
557
515
|
|
|
558
|
-
@responses.activate
|
|
559
|
-
def test_get_slice() -> None:
|
|
560
|
-
responses.get(
|
|
561
|
-
url=f'{URL}/v3/models/{MODEL_ID}',
|
|
562
|
-
json=MODEL_API_RESPONSE_200,
|
|
563
|
-
)
|
|
564
|
-
model = Model.get(id_=MODEL_ID)
|
|
565
|
-
|
|
566
|
-
responses.post(
|
|
567
|
-
url=f'{URL}/v3/slice-query/fetch',
|
|
568
|
-
json=FETCH_SLICE_RESPONSE_200,
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
slice_df = model.get_slice(
|
|
572
|
-
query="SELECT * FROM test_bank_churn.bank_churn WHERE geography='France' order by balance desc LIMIT 3",
|
|
573
|
-
columns=['Age'],
|
|
574
|
-
max_rows=2,
|
|
575
|
-
)
|
|
576
|
-
expected_df = pd.DataFrame({'Age': [57, 37]})
|
|
577
|
-
pd.testing.assert_frame_equal(expected_df, slice_df)
|
|
578
|
-
|
|
579
516
|
|
|
580
517
|
@responses.activate
|
|
581
518
|
def test_download_data() -> None:
|
|
@@ -607,51 +544,6 @@ def test_download_data() -> None:
|
|
|
607
544
|
shutil.rmtree(str(parquet_output))
|
|
608
545
|
|
|
609
546
|
|
|
610
|
-
@responses.activate
|
|
611
|
-
def test_slice_download() -> None:
|
|
612
|
-
responses.get(
|
|
613
|
-
url=f'{URL}/v3/models/{MODEL_ID}',
|
|
614
|
-
json=MODEL_API_RESPONSE_200,
|
|
615
|
-
)
|
|
616
|
-
model = Model.get(id_=MODEL_ID)
|
|
617
|
-
|
|
618
|
-
parquet_path = os.path.join(OUTPUT_DIR, 'test_slice_download.parquet')
|
|
619
|
-
parquet_output = os.path.join(BASE_TEST_DIR, 'slice_test_dir')
|
|
620
|
-
with open(parquet_path, 'rb') as parquet_file:
|
|
621
|
-
data = io.BufferedReader(parquet_file)
|
|
622
|
-
responses.post(
|
|
623
|
-
url=f'{URL}/v3/slice-query/download',
|
|
624
|
-
body=data,
|
|
625
|
-
)
|
|
626
|
-
expected_df = pd.DataFrame({'Age': [38, 57, 42]})
|
|
627
|
-
model.download_slice(
|
|
628
|
-
output_dir=parquet_output,
|
|
629
|
-
query='SELECT * FROM test_bank_churn.bank_churn WHERE age>=20 order by balance desc',
|
|
630
|
-
columns=['Age'],
|
|
631
|
-
max_rows=3,
|
|
632
|
-
)
|
|
633
|
-
slice_df = pd.read_parquet(parquet_path)
|
|
634
|
-
pd.testing.assert_frame_equal(expected_df, slice_df)
|
|
635
|
-
shutil.rmtree(str(parquet_output))
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
@responses.activate
|
|
639
|
-
def test_mutual_info() -> None:
|
|
640
|
-
responses.get(
|
|
641
|
-
url=f'{URL}/v3/models/{MODEL_ID}',
|
|
642
|
-
json=MODEL_API_RESPONSE_200,
|
|
643
|
-
)
|
|
644
|
-
model = Model.get(id_=MODEL_ID)
|
|
645
|
-
|
|
646
|
-
responses.post(
|
|
647
|
-
url=f'{URL}/v3/analytics/mutual-info',
|
|
648
|
-
json=MUTUAL_INFO_RESPONSE_200,
|
|
649
|
-
)
|
|
650
|
-
mutual_info = model.get_mutual_info(
|
|
651
|
-
query=f'select * from {DATASET_NAME}.{MODEL_NAME}',
|
|
652
|
-
column_name='Geography',
|
|
653
|
-
)
|
|
654
|
-
assert mutual_info == MUTUAL_INFO_RESPONSE_200['data']
|
|
655
547
|
|
|
656
548
|
|
|
657
549
|
@responses.activate
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
fiddler/VERSION,sha256=
|
|
2
|
-
fiddler/__init__.py,sha256=
|
|
1
|
+
fiddler/VERSION,sha256=E4ID4_YEoI-tDE0KKX7-LEhiGYINzdBT5yt9hDUEx6E,6
|
|
2
|
+
fiddler/__init__.py,sha256=3gbyU75uS19XTOqscenO38oPfC7qf9d44Cgt9UYyTJI,3772
|
|
3
3
|
fiddler/configs.py,sha256=ZimSo0Gk7j1BFkjDHdRdycrGZ8oh-7G5TBXYiOh1HvU,217
|
|
4
4
|
fiddler/connection.py,sha256=gm7dShXhNFRTyHKsAEtQ56_lDlArLdxjKJQaQMK5-fE,5775
|
|
5
5
|
fiddler/decorators.py,sha256=TyM5__nxjX8tIT83YAnIWMkWlh5GIsGg6F8wdlHeq-g,1836
|
|
6
|
-
fiddler/exceptions.py,sha256=
|
|
6
|
+
fiddler/exceptions.py,sha256=auMn-F6CILpZAmCzdBWY2XaI6YUPZ-TAB0pjnIUHYX0,2386
|
|
7
7
|
fiddler/version.py,sha256=8fyg3UhFpqghMpxbtYIwNcB1lhCG6yTkPBbDS7IrwxY,140
|
|
8
8
|
fiddler/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
fiddler/constants/alert_rule.py,sha256=cmFq-VKAlQbRKEkTtMmVNdD5R52uMI2zJai6TESFwfs,512
|
|
@@ -24,7 +24,7 @@ fiddler/entities/custom_expression.py,sha256=SdS61JiI0gOxWwHAGxkHh1vqS5XsRtyeNSD
|
|
|
24
24
|
fiddler/entities/dataset.py,sha256=kwD1pZMG6DQGIjejU7A2KUUBRgOi0o1qbbUX-PSlnGg,4399
|
|
25
25
|
fiddler/entities/events.py,sha256=9pzg7J1v03o2DGgHX_uPsCSujwk5iyPEDJPrrBNWJbo,5662
|
|
26
26
|
fiddler/entities/file.py,sha256=dk8ISfjy0--F2eF23Ae8vGiEZmJW6e9LMqNmKfrY2mE,4710
|
|
27
|
-
fiddler/entities/job.py,sha256=
|
|
27
|
+
fiddler/entities/job.py,sha256=ueyjIuG0ZCl9P6yyMfIu9TgAPCKLKASTR8rCW0omuBw,7040
|
|
28
28
|
fiddler/entities/model.py,sha256=giqLWvnqXsqrekJ3sIeHf3vUUYO43PxrGh6I_KsGfH4,19271
|
|
29
29
|
fiddler/entities/model_artifact.py,sha256=Y7o1mFs-nVDz7HI9mVBu9ICwkqGD_QbT3q6vf8KKesk,5520
|
|
30
30
|
fiddler/entities/model_deployment.py,sha256=pzS6xPg4zuhrIzMbF44ptK_ZwV8yN6URF0hDwyVhnwY,3966
|
|
@@ -33,9 +33,9 @@ fiddler/entities/project.py,sha256=G5omjjKHEjvhenMvW-R0bYwrkRVHixIOhTPZb1pS7FI,4
|
|
|
33
33
|
fiddler/entities/surrogate.py,sha256=94RkJmv8uwNhRL8PaliuiRv-R0EXDMKJTimE9boXKwQ,3077
|
|
34
34
|
fiddler/entities/user.py,sha256=ev_Xx9QHAdEHYzNDafzQihXOcD8ME5XkpKgc8MiqCnw,1243
|
|
35
35
|
fiddler/entities/webhook.py,sha256=BhwV-05EaFtOATbnlPWUaGeziRSewBPSNSGsYFbIRfc,4514
|
|
36
|
-
fiddler/entities/xai.py,sha256
|
|
36
|
+
fiddler/entities/xai.py,sha256=-jbQEBFO1NxsBGld_bWV-nmeciex-jK4ett0_AjaO8I,22041
|
|
37
37
|
fiddler/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
fiddler/libs/http_client.py,sha256=
|
|
38
|
+
fiddler/libs/http_client.py,sha256=wQy5wVXDSkW2AZ3azNo4WZjGTRfXi1kf_nknU5_wwHs,6799
|
|
39
39
|
fiddler/libs/json_encoder.py,sha256=brsrWySvtZdaBArIkqKM5WwJUbvZf6ZbKozn1CewjOc,512
|
|
40
40
|
fiddler/libs/semver.py,sha256=khO8HpPceBXbimD4Q7hqQB9BYFHl9G7z7--ycXDQQwo,15979
|
|
41
41
|
fiddler/packtools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -66,7 +66,7 @@ fiddler/schemas/response.py,sha256=kcSt3zmyjLpbjt_t2KOOzinBqgmfJFJS9mvx998hhj8,1
|
|
|
66
66
|
fiddler/schemas/server_info.py,sha256=nrykHzrcIaYX1TcK71RoGSQ6kUIinCfvMU8wxaGtQZQ,500
|
|
67
67
|
fiddler/schemas/user.py,sha256=p_Z-Zjj85HvJw2QGWwP_qqx-lAcNTgzMrVzgkKjstEU,140
|
|
68
68
|
fiddler/schemas/webhook.py,sha256=Utg1wRylr5L0CYc47k7iLQdlVCL0L6S29jVntNwMzbA,464
|
|
69
|
-
fiddler/schemas/xai.py,sha256=
|
|
69
|
+
fiddler/schemas/xai.py,sha256=j16vu4iDhTuD42kKEjukbMFkRFgKfkHzMRVDWTsEFz8,616
|
|
70
70
|
fiddler/schemas/xai_params.py,sha256=VX9dXumBrnCAmxyvsQWagj4kYTaQr9Rv1hGsSimhFjU,316
|
|
71
71
|
fiddler/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
72
|
fiddler/tests/conftest.py,sha256=Dmz9FKAFHYdq1AWmcY7NES9pU4cbY9qKWQiojjaX_0E,840
|
|
@@ -94,7 +94,7 @@ fiddler/tests/apis/test_model_surrogate.py,sha256=h3OPgD1-_n6gTd0YH_hDcBBRjBfXv1
|
|
|
94
94
|
fiddler/tests/apis/test_project.py,sha256=IEYT3imD4dZJg-OT888G4jQJ_AxxHN5_VkCHcmwUnGA,6015
|
|
95
95
|
fiddler/tests/apis/test_segment.py,sha256=hQiXpZEKLJrnlRBLLMsP3jph8H8K7AEUw17sMK6BuF8,7804
|
|
96
96
|
fiddler/tests/apis/test_webhook.py,sha256=RyBw7p2xNp-LFmq6n9mgG-GQqSu2Y3FzsccM8aSyLwc,8180
|
|
97
|
-
fiddler/tests/apis/test_xai.py,sha256=
|
|
97
|
+
fiddler/tests/apis/test_xai.py,sha256=OSExXaA0ajKR7-qvTsbP5uZ72pzbqTVxVn92SDAHLzs,24485
|
|
98
98
|
fiddler/utils/__init__.py,sha256=ooAJR6_tVaF6UKZriz5ynZPj8IeCzaDIPKV23F-e4Fk,53
|
|
99
99
|
fiddler/utils/decorators.py,sha256=bEMhbfxoeyfJRnPGTY3xfBJbGYe9j7W4jbkhke8yuls,1521
|
|
100
100
|
fiddler/utils/helpers.py,sha256=Xl_T_se9SOeMTmVJAOVzzmkekUsmpFMlAb2KvPCWfTw,2908
|
|
@@ -102,8 +102,8 @@ fiddler/utils/logger.py,sha256=FdJ3LkS9dbRjWsw5oJmNNsd7q0XRVEIvx5-TWys7L0k,669
|
|
|
102
102
|
fiddler/utils/model_generator.py,sha256=TwQMQDpy-0gcq6HyL68s37N-sk_nGPq33mmppK6i7hI,2203
|
|
103
103
|
fiddler/utils/validations.py,sha256=i8NtgrpCsitq6BPa5lygpKDh07oaOXu7PAlCIcMvqzY,408
|
|
104
104
|
fiddler/utils/version.py,sha256=iC8Ry7UFl9EJW_xU1WbzO_l4yK6V7eQ6U5exB3xT864,531
|
|
105
|
-
fiddler_client-3.
|
|
106
|
-
fiddler_client-3.
|
|
107
|
-
fiddler_client-3.
|
|
108
|
-
fiddler_client-3.
|
|
109
|
-
fiddler_client-3.
|
|
105
|
+
fiddler_client-3.6.0.dist-info/LICENSE.txt,sha256=w8-LUAb_VLBWSsCNmih0pAqLIicJfGu8OJXpDjkIg_o,559
|
|
106
|
+
fiddler_client-3.6.0.dist-info/METADATA,sha256=SyS0cthchPhthXs7VuDq885vcWD20Z5dbzuauDK2bm4,1586
|
|
107
|
+
fiddler_client-3.6.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
108
|
+
fiddler_client-3.6.0.dist-info/top_level.txt,sha256=ndC6LqvKmrTTs8czRlBOYdYgSqZbHuMf0zHt_HWpzzk,8
|
|
109
|
+
fiddler_client-3.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|