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.
Files changed (65) hide show
  1. fiddler/__init__.py +2 -0
  2. fiddler/_version.py +1 -1
  3. fiddler/api/alert_mixin.py +1 -1
  4. fiddler/api/api.py +2 -0
  5. fiddler/api/webhooks_mixin.py +9 -7
  6. fiddler/core_objects.py +98 -47
  7. fiddler/schemas/custom_features.py +25 -19
  8. fiddler3/constants/baseline.py +19 -0
  9. fiddler3/constants/common.py +12 -0
  10. fiddler3/constants/dataset.py +7 -0
  11. fiddler3/constants/model_deployment.py +15 -0
  12. fiddler3/constants/xai.py +11 -0
  13. fiddler3/entities/__init__.py +5 -1
  14. fiddler3/entities/base.py +22 -18
  15. fiddler3/entities/baseline.py +228 -0
  16. fiddler3/entities/dataset.py +149 -0
  17. fiddler3/entities/job.py +9 -8
  18. fiddler3/entities/model.py +160 -29
  19. fiddler3/entities/model_artifact.py +350 -0
  20. fiddler3/entities/model_deployment.py +112 -0
  21. fiddler3/entities/model_surrogate.py +92 -0
  22. fiddler3/entities/organization.py +38 -0
  23. fiddler3/entities/project.py +69 -8
  24. fiddler3/entities/user.py +45 -0
  25. fiddler3/entities/xai.py +309 -0
  26. fiddler3/libs/http_client.py +24 -13
  27. fiddler3/libs/json_encoder.py +12 -0
  28. fiddler3/schemas/__init__.py +4 -0
  29. fiddler3/schemas/baseline.py +37 -0
  30. fiddler3/schemas/custom_features.py +23 -18
  31. fiddler3/schemas/dataset.py +27 -0
  32. fiddler3/schemas/deployment_params.py +25 -0
  33. fiddler3/schemas/filter_query.py +54 -0
  34. fiddler3/schemas/job.py +2 -2
  35. fiddler3/schemas/model.py +9 -9
  36. fiddler3/schemas/model_artifact.py +6 -0
  37. fiddler3/schemas/model_deployment.py +26 -0
  38. fiddler3/schemas/organization.py +9 -1
  39. fiddler3/schemas/project.py +4 -4
  40. fiddler3/schemas/server_info.py +2 -2
  41. fiddler3/schemas/user.py +1 -1
  42. fiddler3/schemas/xai.py +32 -0
  43. fiddler3/tests/apis/test_baseline.py +292 -0
  44. fiddler3/tests/apis/test_dataset.py +173 -0
  45. fiddler3/tests/apis/test_mixin.py +77 -0
  46. fiddler3/tests/apis/test_model.py +102 -7
  47. fiddler3/tests/apis/test_model_artifact.py +175 -0
  48. fiddler3/tests/apis/test_model_deployment.py +98 -0
  49. fiddler3/tests/apis/test_model_surrogate.py +159 -0
  50. fiddler3/tests/apis/test_project.py +36 -20
  51. fiddler3/tests/apis/test_xai.py +690 -0
  52. fiddler3/tests/constants.py +10 -0
  53. fiddler3/tests/test_json_encoder.py +16 -0
  54. fiddler3/tests/test_logger.py +12 -0
  55. fiddler3/tests/test_utils.py +19 -0
  56. fiddler3/utils/__init__.py +1 -0
  57. fiddler3/utils/helpers.py +43 -0
  58. fiddler3/utils/logger.py +15 -0
  59. fiddler3/utils/validations.py +11 -0
  60. {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/METADATA +7 -5
  61. {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/RECORD +65 -33
  62. tests/fiddler/test_segments.py +227 -0
  63. {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/LICENSE.txt +0 -0
  64. {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/WHEEL +0 -0
  65. {fiddler_client-2.3.0.dev2.dist-info → fiddler_client-2.4.0.dev1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,309 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from collections import namedtuple
5
+ from io import BytesIO
6
+ from pathlib import Path
7
+ from typing import Any, Callable
8
+ from uuid import UUID
9
+
10
+ import pandas as pd
11
+
12
+ from fiddler3.constants.xai import ExplainMethod
13
+ from fiddler3.decorators import handle_api_error
14
+ from fiddler3.schemas.xai import (
15
+ DatasetDataSource,
16
+ EventIdDataSource,
17
+ RowDataSource,
18
+ SqlSliceQueryDataSource,
19
+ )
20
+ from fiddler3.utils.helpers import try_series_retype
21
+
22
+
23
+ class XaiMixin:
24
+ id: UUID | None
25
+ _client: Callable
26
+
27
+ @handle_api_error
28
+ def explain( # pylint: disable=too-many-arguments
29
+ self,
30
+ input_data_source: RowDataSource | EventIdDataSource,
31
+ ref_data_source: DatasetDataSource | SqlSliceQueryDataSource | None = None,
32
+ method: ExplainMethod | str = ExplainMethod.FIDDLER_SHAP,
33
+ num_permutations: int | None = None,
34
+ ci_level: float | None = None,
35
+ top_n_class: int | None = None,
36
+ ) -> tuple:
37
+ """
38
+ Get explanation for a single observation.
39
+
40
+ :param input_data_source: DataSource for the input data to compute explanation
41
+ on (RowDataSource, EventIdDataSource)
42
+ :param ref_data_source: DataSource for the reference data to compute explanation
43
+ on (DatasetDataSource, SqlSliceQueryDataSource).
44
+ Only used for non-text models and the following methods:
45
+ 'SHAP', 'FIDDLER_SHAP', 'PERMUTE', 'MEAN_RESET'
46
+ :param method: Explanation method name. Could be your custom
47
+ explanation method or one of the following method:
48
+ 'SHAP', 'FIDDLER_SHAP', 'IG', 'PERMUTE', 'MEAN_RESET', 'ZERO_RESET'
49
+ :param num_permutations: For Fiddler SHAP, that corresponds to the number of
50
+ coalitions to sample to estimate the Shapley values of each single-reference
51
+ game. For the permutation algorithms, this corresponds to the number
52
+ of permutations from the dataset to use for the computation.
53
+ :param ci_level: The confidence level (between 0 and 1) to use for the
54
+ confidence intervals in Fiddler SHAP. Not used for other methods.
55
+ :param top_n_class: For multiclass classification models only, specifying if
56
+ only the n top classes are computed or all classes (when parameter is None)
57
+
58
+ :return: A named tuple with the explanation results.
59
+ """
60
+ self._check_id_attributes()
61
+ payload: dict[str, Any] = {
62
+ 'model_id': self.id,
63
+ 'input_data_source': input_data_source.dict(),
64
+ 'explanation_type': method,
65
+ }
66
+ if ref_data_source:
67
+ payload['ref_data_source'] = ref_data_source.dict()
68
+ if num_permutations:
69
+ payload['num_permutations'] = num_permutations
70
+ if ci_level:
71
+ payload['ci_level'] = ci_level
72
+ if top_n_class:
73
+ payload['top_n_class'] = top_n_class
74
+
75
+ response = self._client().post(
76
+ url='v3/explain',
77
+ data=payload,
78
+ )
79
+
80
+ return namedtuple('Explain', response.json()['data'])(**response.json()['data'])
81
+
82
+ @handle_api_error
83
+ def get_fairness(
84
+ self,
85
+ data_source: DatasetDataSource | SqlSliceQueryDataSource,
86
+ protected_features: list[str],
87
+ positive_outcome: str | int | float | bool,
88
+ score_threshold: float | None = None,
89
+ ) -> tuple:
90
+ """
91
+ Get fairness analysis on a dataset or a slice.
92
+
93
+ :param data_source: DataSource for the input dataset to compute
94
+ fairness on (DatasetDataSource or SqlSliceQueryDataSource)
95
+ :param protected_features: list of protected attribute names to compute
96
+ Fairness analysis on
97
+ :param positive_outcome: name of the positive outcome to compute
98
+ Fairness analysis on
99
+ :param score_threshold: Binary threshold value (between 0 and 1). Default to 0.5
100
+
101
+ :return: A named tuple with the fairness results.
102
+ """
103
+ self._check_id_attributes()
104
+
105
+ payload: dict[str, Any] = {
106
+ 'model_id': self.id,
107
+ 'data_source': data_source.dict(),
108
+ 'protected_features': protected_features,
109
+ 'positive_outcome': positive_outcome,
110
+ }
111
+ if score_threshold:
112
+ payload['score_threshold'] = score_threshold
113
+
114
+ response = self._client().post(url='/v3/analytics/fairness', data=payload)
115
+
116
+ return namedtuple('Fairness', response.json()['data'])(
117
+ **response.json()['data']
118
+ )
119
+
120
+ @handle_api_error
121
+ def get_slice(
122
+ self,
123
+ query: str,
124
+ sample: bool = False,
125
+ max_rows: int | None = None,
126
+ columns: list[str] | None = None,
127
+ ) -> pd.DataFrame:
128
+ """
129
+ Fetch data with slice query.
130
+
131
+ :param query: An SQL query that begins with the keyword 'SELECT'
132
+ :param columns: Allows caller to explicitly specify list of
133
+ columns to select overriding columns selected in the query.
134
+ :param max_rows: Number of maximum rows to fetch
135
+ :param sample: Whether rows should be sample or not from the database
136
+
137
+ :return: Dataframe of the query output
138
+ """
139
+ self._check_id_attributes()
140
+
141
+ payload: dict[str, Any] = {
142
+ 'model_id': self.id,
143
+ 'query': query,
144
+ 'sample': sample,
145
+ }
146
+ if max_rows:
147
+ payload['max_rows'] = max_rows
148
+ if columns:
149
+ payload['columns'] = columns
150
+
151
+ response = self._client().post(
152
+ url='/v3/slice-query/fetch',
153
+ data=payload,
154
+ )
155
+
156
+ response_dict = response.json()['data']
157
+
158
+ column_names = response_dict['metadata']['columns']
159
+ dtype_strings = response_dict['metadata']['dtypes']
160
+ df = pd.DataFrame(response_dict['rows'], columns=column_names)
161
+ for column_name, dtype in zip(column_names, dtype_strings):
162
+ df[column_name] = try_series_retype(df[column_name], dtype)
163
+ return df
164
+
165
+ @handle_api_error
166
+ def download_slice( # pylint: disable=too-many-arguments
167
+ self,
168
+ output_dir: Path | str,
169
+ query: str,
170
+ sample: bool = False,
171
+ max_rows: int | None = None,
172
+ columns: list[str] | None = None,
173
+ ) -> None:
174
+ """
175
+ Download data with slice query to parquet file.
176
+
177
+ :param output_dir: Path to download the file
178
+ :param query: An SQL query that begins with the keyword 'SELECT'
179
+ :param columns: Allows caller to explicitly specify list of
180
+ columns to select overriding columns selected in the query.
181
+ :param max_rows: Number of maximum rows to fetch
182
+ :param sample: Whether rows should be sample or not from the database
183
+ """
184
+ self._check_id_attributes()
185
+ output_dir = Path(output_dir)
186
+ if output_dir.exists():
187
+ raise ValueError(f'Output dir already exists {output_dir}')
188
+ payload: dict[str, Any] = {
189
+ 'model_id': self.id,
190
+ 'query': query,
191
+ 'sample': sample,
192
+ }
193
+ if max_rows:
194
+ payload['max_rows'] = max_rows
195
+ if columns:
196
+ payload['columns'] = columns
197
+
198
+ os.makedirs(output_dir)
199
+ file_path = os.path.join(output_dir, 'output.parquet')
200
+ with self._client().post(url='/v3/slice-query/download', data=payload) as resp:
201
+ # Download parquet file
202
+ df = pd.read_parquet(BytesIO(resp.content))
203
+ df.to_parquet(file_path)
204
+
205
+ @handle_api_error
206
+ def get_mutual_info(
207
+ self,
208
+ query: str,
209
+ column_name: str,
210
+ num_samples: int | None = None,
211
+ normalized: bool = False,
212
+ ) -> dict:
213
+ """
214
+ Get mutual information.
215
+
216
+ The Mutual information measures the dependency between
217
+ two random variables. It's a non-negative value. If two random variables are
218
+ independent MI is equal to zero. Higher MI values means higher dependency.
219
+
220
+ :param query: slice query to compute Mutual information on
221
+ :param column_name: column name to compute mutual information with respect to
222
+ all the variables in the dataset.
223
+ :param num_samples: Number of samples to select for computation
224
+ :param normalized: If set to True, it will compute Normalized Mutual Information
225
+
226
+ :return: a dictionary of mutual information w.r.t the given feature
227
+ for each column given
228
+ """
229
+ self._check_id_attributes()
230
+
231
+ payload: dict[str, Any] = {
232
+ 'model_id': self.id,
233
+ 'query': query,
234
+ 'column_name': column_name,
235
+ 'normalized': normalized,
236
+ }
237
+ if num_samples:
238
+ payload['num_samples'] = num_samples
239
+
240
+ response = self._client().post(
241
+ url='/v3/analytics/mutual-info',
242
+ data=payload,
243
+ )
244
+
245
+ return response.json()['data']
246
+
247
+ @handle_api_error
248
+ def predict(
249
+ self,
250
+ df: pd.DataFrame,
251
+ chunk_size: int | None = None,
252
+ ) -> pd.DataFrame:
253
+ """
254
+ Run model on an input dataframe.
255
+
256
+ :param df: Feature dataframe
257
+ :param chunk_size: Chunk size for fetching predictions
258
+
259
+ :return: Dataframe of the predictions
260
+ """
261
+ self._check_id_attributes()
262
+
263
+ payload: dict[str, Any] = {
264
+ 'model_id': self.id,
265
+ 'data': df.to_dict('records'),
266
+ }
267
+ if chunk_size:
268
+ payload['chunk_size'] = chunk_size
269
+
270
+ response = self._client().post(
271
+ url='/v3/predict',
272
+ data=payload,
273
+ )
274
+ return pd.DataFrame(response.json()['data']['predictions'])
275
+
276
+ @handle_api_error
277
+ def get_precomputed_feature_importance(self) -> tuple:
278
+ """Get precomputed feature importance for a model"""
279
+
280
+ self._check_id_attributes()
281
+ response = self._client().post(
282
+ url='/v3/analytics/feature-importance/precomputed',
283
+ data={'model_id': self.id},
284
+ )
285
+
286
+ return namedtuple('FeatureImportance', response.json()['data'])(
287
+ **response.json()['data']
288
+ )
289
+
290
+ @handle_api_error
291
+ def get_precomputed_feature_impact(self) -> tuple:
292
+ """Get precomputed feature impact for a model"""
293
+
294
+ self._check_id_attributes()
295
+ response = self._client().post(
296
+ url='/v3/analytics/feature-impact/precomputed',
297
+ data={'model_id': self.id},
298
+ )
299
+
300
+ return namedtuple('FeatureImpact', response.json()['data'])(
301
+ **response.json()['data']
302
+ )
303
+
304
+ def _check_id_attributes(self) -> None:
305
+ if not self.id:
306
+ raise AttributeError(
307
+ 'This method is available only for model object generated from '
308
+ 'API response.'
309
+ )
@@ -10,7 +10,12 @@ from requests.adapters import HTTPAdapter
10
10
  from requests_toolbelt.multipart.encoder import MultipartEncoder
11
11
 
12
12
  from fiddler3.constants.common import JSON_CONTENT_TYPE
13
- from fiddler3.exceptions import ConnectionError, ConnectionTimeout, HttpError
13
+ from fiddler3.exceptions import ( # pylint: disable=redefined-builtin
14
+ ConnectionError,
15
+ ConnectionTimeout,
16
+ HttpError,
17
+ )
18
+ from fiddler3.libs.json_encoder import RequestClientJSONEncoder
14
19
  from fiddler3.utils.logger import get_logger
15
20
 
16
21
  logger = get_logger(__name__)
@@ -24,6 +29,7 @@ class RequestClient:
24
29
  verify: bool = True,
25
30
  proxies: dict | None = None,
26
31
  ) -> None:
32
+ """Construct a request instance."""
27
33
  self.base_url = base_url
28
34
  self.proxies = proxies
29
35
  self.headers = headers
@@ -43,7 +49,7 @@ class RequestClient:
43
49
  url: str,
44
50
  params: dict | None = None,
45
51
  headers: dict | None = None,
46
- data: dict | None = None,
52
+ data: dict | bytes | None = None,
47
53
  timeout: int | None = None,
48
54
  files_encoders: dict[str, MultipartEncoder] | None = None,
49
55
  **kwargs: Any,
@@ -73,7 +79,7 @@ class RequestClient:
73
79
 
74
80
  content_type = request_headers.get('Content-Type')
75
81
  if data and content_type == JSON_CONTENT_TYPE:
76
- data = simplejson.dumps(data, ignore_nan=True) # type: ignore
82
+ data = simplejson.dumps(data, ignore_nan=True, cls=RequestClientJSONEncoder) # type: ignore
77
83
 
78
84
  kwargs.setdefault('allow_redirects', True)
79
85
  # requests is not able to pass the value of self.session.verify to the
@@ -107,16 +113,16 @@ class RequestClient:
107
113
  **kwargs,
108
114
  )
109
115
 
110
- except requests.Timeout:
111
- logger.error(f'{method} call failed with Timeout error for URL {url}')
112
- raise ConnectionTimeout()
113
- except requests.ConnectionError:
114
- logger.error(f'{method} call failed with ConnectionError for URL {url}')
115
- raise ConnectionError()
116
- except requests.RequestException:
116
+ except requests.Timeout as exc:
117
+ logger.error('%s call failed with Timeout error for URL %s', method, url)
118
+ raise ConnectionTimeout() from exc
119
+ except requests.ConnectionError as exc:
120
+ logger.error('%s call failed with ConnectionError for URL %s', method, url)
121
+ raise ConnectionError() from exc
122
+ except requests.RequestException as exc:
117
123
  # catastrophic error.
118
- logger.error(f'{method} call failed with RequestException for URL {url}')
119
- raise HttpError()
124
+ logger.error('%s call failed with RequestException for %s', method, url)
125
+ raise HttpError() from exc
120
126
 
121
127
  response.raise_for_status()
122
128
  return response
@@ -130,6 +136,7 @@ class RequestClient:
130
136
  timeout: int | None = None,
131
137
  **kwargs: dict[str, Any],
132
138
  ) -> requests.Response:
139
+ """Construct a get request instance."""
133
140
  return self.call(
134
141
  method='GET',
135
142
  url=url,
@@ -148,6 +155,7 @@ class RequestClient:
148
155
  timeout: int | None = None,
149
156
  **kwargs: dict[str, Any],
150
157
  ) -> requests.Response:
158
+ """Construct a delete request instance."""
151
159
  return self.call(
152
160
  method='DELETE',
153
161
  url=url,
@@ -164,10 +172,11 @@ class RequestClient:
164
172
  params: dict | None = None,
165
173
  headers: dict | None = None,
166
174
  timeout: int | None = None,
167
- data: dict | None = None,
175
+ data: dict | bytes | None = None,
168
176
  files_encoders: dict[str, MultipartEncoder] | None = None,
169
177
  **kwargs: dict[str, Any],
170
178
  ) -> requests.Response:
179
+ """Construct a post request instance."""
171
180
  return self.call(
172
181
  method='POST',
173
182
  url=url,
@@ -189,6 +198,7 @@ class RequestClient:
189
198
  data: dict | None = None,
190
199
  **kwargs: dict[str, Any],
191
200
  ) -> requests.Response:
201
+ """Construct a put request instance."""
192
202
  return self.call(
193
203
  method='PUT',
194
204
  url=url,
@@ -209,6 +219,7 @@ class RequestClient:
209
219
  data: dict | None = None,
210
220
  **kwargs: dict[str, Any],
211
221
  ) -> requests.Response:
222
+ """Construct a potch request instance."""
212
223
  return self.call(
213
224
  method='PATCH',
214
225
  url=url,
@@ -0,0 +1,12 @@
1
+ from typing import Any
2
+ from uuid import UUID
3
+
4
+ from simplejson import JSONEncoder
5
+
6
+
7
+ class RequestClientJSONEncoder(JSONEncoder):
8
+ def default(self, o: Any) -> Any:
9
+ """Override JSONEncoder.default to support uuid serialization"""
10
+ if isinstance(o, UUID):
11
+ return o.hex
12
+ return super().default(o)
@@ -1,5 +1,9 @@
1
+ """Initialize all schemas."""
1
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
2
5
  from fiddler3.schemas.model_schema import ModelSchema # noqa
3
6
  from fiddler3.schemas.model_spec import ModelSpec # noqa
4
7
  from fiddler3.schemas.model_task_params import ModelTaskParams # noqa
8
+ from fiddler3.schemas.xai import * # noqa
5
9
  from fiddler3.schemas.xai_params import XaiParams # noqa
@@ -0,0 +1,37 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+ from uuid import UUID
4
+
5
+ from pydantic import Field
6
+
7
+ from fiddler3.schemas.base import BaseModel
8
+ from fiddler3.schemas.dataset import DatasetCompactResp
9
+ from fiddler3.schemas.model import ModelCompactResp
10
+ from fiddler3.schemas.organization import OrganizationCompactResp
11
+ from fiddler3.schemas.project import ProjectCompactResp
12
+
13
+
14
+ class BaselineCompactResp(BaseModel):
15
+ id: UUID
16
+ name: str
17
+
18
+
19
+ class BaselineResp(BaseModel):
20
+ """Baseline response pydantic model."""
21
+
22
+ id: UUID
23
+ name: str
24
+ type: str
25
+ start_time: Optional[int]
26
+ end_time: Optional[int]
27
+ offset: Optional[int]
28
+ window_size: Optional[int]
29
+ row_count: Optional[int]
30
+
31
+ model: ModelCompactResp
32
+ project: ProjectCompactResp
33
+ organization: OrganizationCompactResp
34
+ dataset: DatasetCompactResp = Field(alias='environment')
35
+
36
+ created_at: datetime
37
+ updated_at: datetime
@@ -13,6 +13,7 @@ class CustomFeatureType(str, Enum):
13
13
  FROM_VECTOR = 'FROM_VECTOR'
14
14
  FROM_TEXT_EMBEDDING = 'FROM_TEXT_EMBEDDING'
15
15
  FROM_IMAGE_EMBEDDING = 'FROM_IMAGE_EMBEDDING'
16
+ ENRICHMENT = 'ENRICHMENT'
16
17
 
17
18
 
18
19
  class CustomFeature(BaseModel):
@@ -20,10 +21,6 @@ class CustomFeature(BaseModel):
20
21
  type: CustomFeatureType
21
22
  n_clusters: Optional[int] = DEFAULT_NUM_CLUSTERS
22
23
  centroids: Optional[List] = None
23
- columns: Optional[List[str]] = None
24
- column: Optional[str] = None
25
- source_column: Optional[str] = None
26
- n_tags: Optional[int] = DEFAULT_NUM_TAGS
27
24
 
28
25
  class Config:
29
26
  allow_mutation = False
@@ -49,30 +46,31 @@ class CustomFeature(BaseModel):
49
46
  return TextEmbedding.parse_obj(deserialized_json)
50
47
  elif feature_type == CustomFeatureType.FROM_IMAGE_EMBEDDING:
51
48
  return ImageEmbedding.parse_obj(deserialized_json)
49
+ elif feature_type == CustomFeatureType.ENRICHMENT:
50
+ return Enrichment.parse_obj(deserialized_json)
52
51
  else:
53
52
  raise ValueError(f'Unsupported feature type: {feature_type}')
54
53
 
55
54
  def to_dict(self) -> Dict[str, Any]:
56
- return_dict = {
55
+ return_dict: Dict[str, Any] = {
57
56
  'name': self.name,
58
57
  'type': self.type.value,
59
58
  'n_clusters': self.n_clusters,
60
59
  }
61
- if self.type == CustomFeatureType.FROM_COLUMNS:
62
- return_dict['columns'] = self.columns # type: ignore
63
- elif self.type == CustomFeatureType.FROM_VECTOR:
60
+ if isinstance(self, Multivariate):
61
+ return_dict['columns'] = self.columns
62
+ elif isinstance(self, VectorFeature):
64
63
  return_dict['column'] = self.column
65
- elif self.type in (
66
- CustomFeatureType.FROM_TEXT_EMBEDDING,
67
- CustomFeatureType.FROM_IMAGE_EMBEDDING,
68
- ):
69
- return_dict['source_column'] = self.source_column
70
- return_dict['column'] = self.column
71
-
72
- if self.type == CustomFeatureType.FROM_TEXT_EMBEDDING:
73
- return_dict['n_tags'] = self.n_tags
64
+ if isinstance(self, (ImageEmbedding, TextEmbedding)):
65
+ return_dict['source_column'] = self.source_column
66
+ if isinstance(self, TextEmbedding):
67
+ return_dict['n_tags'] = self.n_tags
68
+ elif isinstance(self, Enrichment):
69
+ return_dict['columns'] = self.columns
70
+ return_dict['enrichment'] = self.enrichment
71
+ return_dict['config'] = self.config
74
72
  else:
75
- raise ValueError(f'Unsupported feature type: {self.type}')
73
+ raise ValueError(f'Unsupported feature type: {self.type} {type(self)}')
76
74
 
77
75
  return return_dict
78
76
 
@@ -96,3 +94,10 @@ class TextEmbedding(VectorFeature):
96
94
 
97
95
  class ImageEmbedding(VectorFeature):
98
96
  type: CustomFeatureType = CustomFeatureType.FROM_IMAGE_EMBEDDING
97
+
98
+
99
+ class Enrichment(CustomFeature):
100
+ type: CustomFeatureType = CustomFeatureType.ENRICHMENT
101
+ columns: List[str]
102
+ enrichment: str
103
+ config: Dict[str, Any] = {}
@@ -0,0 +1,27 @@
1
+ from typing import Optional
2
+ from uuid import UUID
3
+
4
+ from fiddler3.constants.dataset import EnvType
5
+ from fiddler3.schemas.base import BaseModel
6
+ from fiddler3.schemas.model import ModelCompactResp
7
+ from fiddler3.schemas.organization import OrganizationCompactResp
8
+ from fiddler3.schemas.project import ProjectCompactResp
9
+
10
+
11
+ class DatasetCompactResp(BaseModel):
12
+ """Dataset Compact."""
13
+
14
+ id: UUID
15
+ name: str
16
+ type: EnvType
17
+
18
+
19
+ class DatasetResp(BaseModel):
20
+ """Dataset response pydantic model."""
21
+
22
+ id: UUID
23
+ name: str
24
+ row_count: Optional[int]
25
+ model: ModelCompactResp
26
+ project: ProjectCompactResp
27
+ organization: OrganizationCompactResp
@@ -0,0 +1,25 @@
1
+ import enum
2
+ from typing import Optional
3
+
4
+ from fiddler3.schemas.base import BaseModel
5
+
6
+
7
+ @enum.unique
8
+ class ArtifactType(str, enum.Enum):
9
+ SURROGATE = 'SURROGATE'
10
+ PYTHON_PACKAGE = 'PYTHON_PACKAGE'
11
+
12
+
13
+ @enum.unique
14
+ class DeploymentType(str, enum.Enum):
15
+ BASE_CONTAINER = 'BASE_CONTAINER'
16
+ MANUAL = 'MANUAL'
17
+
18
+
19
+ class DeploymentParams(BaseModel):
20
+ artifact_type: str = ArtifactType.PYTHON_PACKAGE
21
+ deployment_type: DeploymentType
22
+ image_uri: Optional[str]
23
+ replicas: Optional[int]
24
+ cpu: Optional[int]
25
+ memory: Optional[int]
@@ -0,0 +1,54 @@
1
+ import enum
2
+ from typing import Any, List, Union
3
+
4
+ from fiddler3.schemas.base import BaseModel
5
+
6
+
7
+ class QueryConditionType(str, enum.Enum):
8
+ AND = 'AND'
9
+ OR = 'OR'
10
+
11
+
12
+ class QueryRule(BaseModel):
13
+ field: str
14
+ operator: str
15
+ value: Any
16
+
17
+
18
+ class QueryCondition(BaseModel):
19
+ condition: QueryConditionType = QueryConditionType.AND
20
+
21
+ rules: List[Union[QueryRule, 'QueryCondition']]
22
+
23
+ def add_rule(self, rule: Union[QueryRule, 'QueryCondition']) -> None:
24
+ """Add a filter rule"""
25
+ self.rules.append(rule)
26
+
27
+ def add_rules(self, rules: List[Union[QueryRule, 'QueryCondition']]) -> None:
28
+ """Add multiple filter rules"""
29
+ self.rules.extend(rules)
30
+
31
+
32
+ @enum.unique
33
+ class OperatorType(str, enum.Enum):
34
+ ANY = 'any'
35
+ EQUAL = 'equal'
36
+ NOT_EQUAL = 'not_equal'
37
+ IN = 'in'
38
+ NOT_IN = 'not_in'
39
+ LESS = 'less'
40
+ LESS_OR_EQUAL = 'less_or_equal'
41
+ GREATER = 'greater'
42
+ GREATER_OR_EQUAL = 'greater_or_equal'
43
+ BETWEEN = 'between'
44
+ NOT_BETWEEN = 'not_between'
45
+ BEGINS_WITH = 'begins_with'
46
+ NOT_BEGINS_WITH = 'not_begins_with'
47
+ CONTAINS = 'contains'
48
+ NOT_CONTAINS = 'not_contains'
49
+ ENDS_WITH = 'ends_with'
50
+ NOT_ENDS_WITH = 'not_ends_with'
51
+ IS_EMPTY = 'is_empty'
52
+ IS_NOT_EMPTY = 'is_not_empty'
53
+ IS_NULL = 'is_null'
54
+ IS_NOT_NULL = 'is_not_null'
fiddler3/schemas/job.py CHANGED
@@ -4,12 +4,12 @@ from uuid import UUID
4
4
  from fiddler3.schemas.base import BaseModel
5
5
 
6
6
 
7
- class JobCompact(BaseModel):
7
+ class JobCompactResp(BaseModel):
8
8
  id: UUID
9
9
  name: str
10
10
 
11
11
 
12
- class JobResponse(BaseModel):
12
+ class JobResp(BaseModel):
13
13
  id: UUID
14
14
  name: str
15
15
  status: str