fiddler-client 2.4.0.dev2__py3-none-any.whl → 2.5.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 +51 -1
- fiddler/api/webhooks_mixin.py +6 -8
- fiddler/core_objects.py +67 -30
- fiddler/schema/alert.py +8 -1
- fiddler/schemas/custom_features.py +13 -0
- fiddler3/__init__.py +1 -36
- fiddler3/connection.py +68 -1
- fiddler3/constants/alert_rule.py +30 -0
- fiddler3/constants/events.py +7 -0
- fiddler3/entities/__init__.py +3 -0
- fiddler3/entities/alert_record.py +106 -0
- fiddler3/entities/alert_rule.py +246 -0
- fiddler3/entities/base.py +2 -16
- fiddler3/entities/events.py +120 -0
- fiddler3/entities/files.py +170 -0
- fiddler3/entities/helpers.py +6 -6
- fiddler3/entities/model.py +67 -18
- fiddler3/entities/model_artifact.py +48 -220
- fiddler3/entities/project.py +1 -1
- fiddler3/entities/webhook.py +128 -0
- fiddler3/entities/xai.py +267 -0
- fiddler3/libs/json_encoder.py +1 -1
- fiddler3/schemas/alert_record.py +27 -0
- fiddler3/schemas/alert_rule.py +32 -0
- fiddler3/schemas/custom_features.py +34 -0
- fiddler3/schemas/events.py +15 -0
- fiddler3/schemas/files.py +23 -0
- fiddler3/schemas/model.py +1 -6
- fiddler3/schemas/webhook.py +26 -0
- fiddler3/tests/apis/test_alert_record.py +99 -0
- fiddler3/tests/apis/test_alert_rule.py +323 -0
- fiddler3/tests/apis/test_events.py +128 -0
- fiddler3/tests/apis/test_files.py +164 -0
- fiddler3/tests/apis/test_model.py +55 -8
- fiddler3/tests/apis/test_model_artifact.py +21 -0
- fiddler3/tests/apis/test_project.py +2 -2
- fiddler3/tests/apis/test_webhook.py +280 -0
- fiddler3/tests/apis/test_xai.py +233 -1
- fiddler3/tests/conftest.py +2 -2
- fiddler3/tests/constants.py +5 -0
- fiddler3/tests/test_connection.py +24 -1
- fiddler3/tests/test_json_encoder.py +2 -2
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev1.dist-info}/METADATA +1 -1
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev1.dist-info}/RECORD +50 -34
- tests/fiddler/test_alert.py +95 -5
- fiddler3/schemas/model_artifact.py +0 -6
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev1.dist-info}/LICENSE.txt +0 -0
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev1.dist-info}/WHEEL +0 -0
- {fiddler_client-2.4.0.dev2.dist-info → fiddler_client-2.5.0.dev1.dist-info}/top_level.txt +0 -0
fiddler3/entities/xai.py
CHANGED
|
@@ -9,8 +9,11 @@ from uuid import UUID
|
|
|
9
9
|
|
|
10
10
|
import pandas as pd
|
|
11
11
|
|
|
12
|
+
from fiddler3.constants.dataset import EnvType
|
|
12
13
|
from fiddler3.constants.xai import ExplainMethod
|
|
13
14
|
from fiddler3.decorators import handle_api_error
|
|
15
|
+
from fiddler3.entities.job import Job
|
|
16
|
+
from fiddler3.schemas.job import JobCompactResp
|
|
14
17
|
from fiddler3.schemas.xai import (
|
|
15
18
|
DatasetDataSource,
|
|
16
19
|
EventIdDataSource,
|
|
@@ -18,12 +21,19 @@ from fiddler3.schemas.xai import (
|
|
|
18
21
|
SqlSliceQueryDataSource,
|
|
19
22
|
)
|
|
20
23
|
from fiddler3.utils.helpers import try_series_retype
|
|
24
|
+
from fiddler3.utils.logger import get_logger
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__name__)
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
class XaiMixin:
|
|
24
30
|
id: UUID | None
|
|
25
31
|
_client: Callable
|
|
26
32
|
|
|
33
|
+
def _get_method(self, update: bool = False) -> Callable:
|
|
34
|
+
"""Get HTTP method"""
|
|
35
|
+
return self._client().put if update else self._client().post
|
|
36
|
+
|
|
27
37
|
@handle_api_error
|
|
28
38
|
def explain( # pylint: disable=too-many-arguments
|
|
29
39
|
self,
|
|
@@ -273,6 +283,221 @@ class XaiMixin:
|
|
|
273
283
|
)
|
|
274
284
|
return pd.DataFrame(response.json()['data']['predictions'])
|
|
275
285
|
|
|
286
|
+
@handle_api_error
|
|
287
|
+
def get_feature_impact( # pylint: disable=too-many-arguments
|
|
288
|
+
self,
|
|
289
|
+
data_source: DatasetDataSource | SqlSliceQueryDataSource,
|
|
290
|
+
num_iterations: int | None = None,
|
|
291
|
+
num_refs: int | None = None,
|
|
292
|
+
ci_level: float | None = None,
|
|
293
|
+
min_support: int | None = None,
|
|
294
|
+
output_columns: list[str] | None = None,
|
|
295
|
+
) -> tuple:
|
|
296
|
+
"""
|
|
297
|
+
Get global feature impact for a model over a dataset or a slice.
|
|
298
|
+
|
|
299
|
+
:param data_source: DataSource for the input dataset to compute feature
|
|
300
|
+
impact on (DatasetDataSource or SqlSliceQueryDataSource)
|
|
301
|
+
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
302
|
+
:param num_refs: The number of reference points used in the explanation
|
|
303
|
+
:param ci_level: The confidence level (between 0 and 1)
|
|
304
|
+
:param min_support: Only used for NLP (TEXT inputs) models. Specify a minimum
|
|
305
|
+
support (number of times a specific word was present in the sample data)
|
|
306
|
+
to retrieve top words. Default to 15.
|
|
307
|
+
:param output_columns: Only used for NLP (TEXT inputs) models. Output column
|
|
308
|
+
names to compute feature impact on.
|
|
309
|
+
|
|
310
|
+
:return: Feature Impact tuple
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
self._check_id_attributes()
|
|
314
|
+
|
|
315
|
+
payload: dict[str, Any] = {
|
|
316
|
+
'model_id': self.id,
|
|
317
|
+
'data_source': data_source.dict(),
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if num_refs:
|
|
321
|
+
payload['num_refs'] = num_refs
|
|
322
|
+
if num_iterations:
|
|
323
|
+
payload['num_iterations'] = num_iterations
|
|
324
|
+
if ci_level:
|
|
325
|
+
payload['ci_level'] = ci_level
|
|
326
|
+
if min_support:
|
|
327
|
+
payload['min_support'] = min_support
|
|
328
|
+
if output_columns:
|
|
329
|
+
payload['output_columns'] = output_columns
|
|
330
|
+
|
|
331
|
+
response = self._client().post(
|
|
332
|
+
url='/v3/analytics/feature-impact',
|
|
333
|
+
data=payload,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
return namedtuple('FeatureImpact', response.json()['data'])(
|
|
337
|
+
**response.json()['data']
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
@handle_api_error
|
|
341
|
+
def get_feature_importance( # pylint: disable=too-many-arguments
|
|
342
|
+
self,
|
|
343
|
+
data_source: DatasetDataSource | SqlSliceQueryDataSource,
|
|
344
|
+
num_iterations: int | None = None,
|
|
345
|
+
num_refs: int | None = None,
|
|
346
|
+
ci_level: float | None = None,
|
|
347
|
+
) -> tuple:
|
|
348
|
+
"""
|
|
349
|
+
Get global feature importance for a model over a dataset or a slice.
|
|
350
|
+
|
|
351
|
+
:param data_source: DataSource for the input dataset to compute feature
|
|
352
|
+
importance on (DatasetDataSource or SqlSliceQueryDataSource)
|
|
353
|
+
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
354
|
+
:param num_refs: The number of reference points used in the explanation
|
|
355
|
+
:param ci_level: The confidence level (between 0 and 1)
|
|
356
|
+
|
|
357
|
+
:return: Feature Importance tuple
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
self._check_id_attributes()
|
|
361
|
+
|
|
362
|
+
payload: dict[str, Any] = {
|
|
363
|
+
'model_id': self.id,
|
|
364
|
+
'data_source': data_source.dict(),
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if num_refs:
|
|
368
|
+
payload['num_refs'] = num_refs
|
|
369
|
+
if num_iterations:
|
|
370
|
+
payload['num_iterations'] = num_iterations
|
|
371
|
+
if ci_level:
|
|
372
|
+
payload['ci_level'] = ci_level
|
|
373
|
+
|
|
374
|
+
response = self._client().post(
|
|
375
|
+
url='/v3/analytics/feature-importance',
|
|
376
|
+
data=payload,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return namedtuple('FeatureImportance', response.json()['data'])(
|
|
380
|
+
**response.json()['data']
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
@handle_api_error
|
|
384
|
+
def precompute_feature_impact( # pylint: disable=too-many-arguments
|
|
385
|
+
self,
|
|
386
|
+
dataset_id: UUID,
|
|
387
|
+
num_samples: int | None = None,
|
|
388
|
+
num_iterations: int | None = None,
|
|
389
|
+
num_refs: int | None = None,
|
|
390
|
+
ci_level: float | None = None,
|
|
391
|
+
min_support: int | None = None,
|
|
392
|
+
update: bool = False,
|
|
393
|
+
) -> Job:
|
|
394
|
+
"""Pre-compute feature impact for a model on a dataset.
|
|
395
|
+
|
|
396
|
+
This is used in various places in the UI.
|
|
397
|
+
A single feature impact can be precomputed (computed and cached) for a model.
|
|
398
|
+
|
|
399
|
+
:param dataset_id: The unique identifier of the dataset
|
|
400
|
+
:param num_samples: The number of samples used
|
|
401
|
+
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
402
|
+
:param num_refs: The number of reference points used in the explanation
|
|
403
|
+
:param ci_level: The confidence level (between 0 and 1)
|
|
404
|
+
:param min_support: Only used for NLP (TEXT inputs) models. Specify a minimum
|
|
405
|
+
support (number of times a specific word was present in the sample data)
|
|
406
|
+
to retrieve top words. Default to 15.
|
|
407
|
+
:param update: Whether the precomputed feature impact should be recomputed and updated
|
|
408
|
+
|
|
409
|
+
:return: Async Job
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
self._check_id_attributes()
|
|
413
|
+
|
|
414
|
+
payload: dict[str, Any] = {
|
|
415
|
+
'model_id': self.id,
|
|
416
|
+
'env_id': dataset_id,
|
|
417
|
+
'env_type': EnvType.PRE_PRODUCTION,
|
|
418
|
+
}
|
|
419
|
+
if num_samples:
|
|
420
|
+
payload['num_samples'] = num_samples
|
|
421
|
+
if num_refs:
|
|
422
|
+
payload['num_refs'] = num_refs
|
|
423
|
+
if num_iterations:
|
|
424
|
+
payload['num_iterations'] = num_iterations
|
|
425
|
+
if ci_level:
|
|
426
|
+
payload['ci_level'] = ci_level
|
|
427
|
+
if min_support:
|
|
428
|
+
payload['min_support'] = min_support
|
|
429
|
+
|
|
430
|
+
method = self._get_method(update)
|
|
431
|
+
|
|
432
|
+
response = method(
|
|
433
|
+
url='/v3/analytics/precompute-feature-impact',
|
|
434
|
+
params=payload,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
438
|
+
logger.info(
|
|
439
|
+
'Model[%s] - Submitted job (%s) for precomputing feature impact',
|
|
440
|
+
self.id,
|
|
441
|
+
job_compact.id,
|
|
442
|
+
)
|
|
443
|
+
return Job.get(id_=job_compact.id)
|
|
444
|
+
|
|
445
|
+
@handle_api_error
|
|
446
|
+
def precompute_feature_importance( # pylint: disable=too-many-arguments
|
|
447
|
+
self,
|
|
448
|
+
dataset_id: UUID,
|
|
449
|
+
num_samples: int | None = None,
|
|
450
|
+
num_iterations: int | None = None,
|
|
451
|
+
num_refs: int | None = None,
|
|
452
|
+
ci_level: float | None = None,
|
|
453
|
+
update: bool = False,
|
|
454
|
+
) -> Job:
|
|
455
|
+
"""Pre-compute feature importance for a model on a dataset.
|
|
456
|
+
|
|
457
|
+
This is used in various places in the UI.
|
|
458
|
+
A single feature importance can be precomputed (computed and cached) for a model.
|
|
459
|
+
|
|
460
|
+
:param dataset_id: The unique identifier of the dataset
|
|
461
|
+
:param num_samples: The number of samples used
|
|
462
|
+
:param num_iterations: The maximum number of ablated model inferences per feature
|
|
463
|
+
:param num_refs: The number of reference points used in the explanation
|
|
464
|
+
:param ci_level: The confidence level (between 0 and 1)
|
|
465
|
+
:param update: Whether the precomputed feature impact should be recomputed and updated
|
|
466
|
+
|
|
467
|
+
:return: Async Job
|
|
468
|
+
"""
|
|
469
|
+
|
|
470
|
+
self._check_id_attributes()
|
|
471
|
+
|
|
472
|
+
payload: dict[str, Any] = {
|
|
473
|
+
'model_id': self.id,
|
|
474
|
+
'env_id': dataset_id,
|
|
475
|
+
'env_type': EnvType.PRE_PRODUCTION,
|
|
476
|
+
}
|
|
477
|
+
if num_samples:
|
|
478
|
+
payload['num_samples'] = num_samples
|
|
479
|
+
if num_refs:
|
|
480
|
+
payload['num_refs'] = num_refs
|
|
481
|
+
if num_iterations:
|
|
482
|
+
payload['num_iterations'] = num_iterations
|
|
483
|
+
if ci_level:
|
|
484
|
+
payload['ci_level'] = ci_level
|
|
485
|
+
|
|
486
|
+
method = self._get_method(update)
|
|
487
|
+
|
|
488
|
+
response = method(
|
|
489
|
+
url='/v3/analytics/precompute-feature-importance',
|
|
490
|
+
params=payload,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
494
|
+
logger.info(
|
|
495
|
+
'Model[%s] - Submitted job (%s) for precomputing feature importance',
|
|
496
|
+
self.id,
|
|
497
|
+
job_compact.id,
|
|
498
|
+
)
|
|
499
|
+
return Job.get(id_=job_compact.id)
|
|
500
|
+
|
|
276
501
|
@handle_api_error
|
|
277
502
|
def get_precomputed_feature_importance(self) -> tuple:
|
|
278
503
|
"""Get precomputed feature importance for a model"""
|
|
@@ -301,6 +526,48 @@ class XaiMixin:
|
|
|
301
526
|
**response.json()['data']
|
|
302
527
|
)
|
|
303
528
|
|
|
529
|
+
@handle_api_error
|
|
530
|
+
def precompute_predictions(
|
|
531
|
+
self,
|
|
532
|
+
dataset_id: UUID,
|
|
533
|
+
chunk_size: int | None = None,
|
|
534
|
+
update: bool = False,
|
|
535
|
+
) -> Job:
|
|
536
|
+
"""
|
|
537
|
+
Pre-compute predictions for a model on a dataset
|
|
538
|
+
|
|
539
|
+
:param dataset_id: The unique identifier of the dataset
|
|
540
|
+
:param chunk_size: Chunk size for fetching predictions
|
|
541
|
+
:param update: Whether the pre-computed predictions should be re-computed and updated for this dataset
|
|
542
|
+
|
|
543
|
+
:return: Dataframe of the predictions
|
|
544
|
+
"""
|
|
545
|
+
self._check_id_attributes()
|
|
546
|
+
|
|
547
|
+
payload: dict[str, Any] = {
|
|
548
|
+
'model_id': self.id,
|
|
549
|
+
'env_id': dataset_id,
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if chunk_size:
|
|
553
|
+
payload['batch_size'] = chunk_size
|
|
554
|
+
|
|
555
|
+
method = self._get_method(update)
|
|
556
|
+
|
|
557
|
+
response = method(
|
|
558
|
+
url='/v3/analytics/precompute-predictions',
|
|
559
|
+
params=payload,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
job_compact = JobCompactResp(**response.json()['data']['job'])
|
|
563
|
+
logger.info(
|
|
564
|
+
'Model[%s] - Submitted job (%s) for precomputing predictions on dataset[%s]',
|
|
565
|
+
self.id,
|
|
566
|
+
job_compact.id,
|
|
567
|
+
dataset_id,
|
|
568
|
+
)
|
|
569
|
+
return Job.get(id_=job_compact.id)
|
|
570
|
+
|
|
304
571
|
def _check_id_attributes(self) -> None:
|
|
305
572
|
if not self.id:
|
|
306
573
|
raise AttributeError(
|
fiddler3/libs/json_encoder.py
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
|
|
9
|
+
|
|
10
|
+
class AlertRecordResp(BaseModel):
|
|
11
|
+
id: UUID = Field(alias='uuid')
|
|
12
|
+
alert_rule_id: UUID = Field(alias='alert_config_uuid')
|
|
13
|
+
alert_run_start_time: int
|
|
14
|
+
alert_time_bucket: int
|
|
15
|
+
alert_value: float
|
|
16
|
+
baseline_time_bucket: Optional[int]
|
|
17
|
+
baseline_value: Optional[float]
|
|
18
|
+
is_alert: bool
|
|
19
|
+
severity: str
|
|
20
|
+
failure_reason: str
|
|
21
|
+
message: str
|
|
22
|
+
feature_name: Optional[str]
|
|
23
|
+
alert_record_main_version: int
|
|
24
|
+
alert_record_sub_version: int
|
|
25
|
+
|
|
26
|
+
created_at: datetime
|
|
27
|
+
updated_at: datetime
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from fiddler3.constants.alert_rule import AlertCondition, BinSize, CompareTo, Priority
|
|
8
|
+
from fiddler3.schemas.base import BaseModel
|
|
9
|
+
from fiddler3.schemas.baseline import BaselineCompactResp
|
|
10
|
+
from fiddler3.schemas.model import ModelCompactResp
|
|
11
|
+
from fiddler3.schemas.project import ProjectCompactResp
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AlertRuleResp(BaseModel):
|
|
15
|
+
id: UUID = Field(alias='uuid')
|
|
16
|
+
name: str
|
|
17
|
+
model: ModelCompactResp
|
|
18
|
+
project: ProjectCompactResp
|
|
19
|
+
baseline: Optional[BaselineCompactResp]
|
|
20
|
+
priority: Union[str, Priority]
|
|
21
|
+
compare_to: Union[str, CompareTo]
|
|
22
|
+
metric_id: Union[str, UUID] = Field(alias='metric')
|
|
23
|
+
critical_threshold: float
|
|
24
|
+
condition: Union[str, AlertCondition]
|
|
25
|
+
bin_size: Union[str, BinSize]
|
|
26
|
+
columns: Optional[List[str]]
|
|
27
|
+
baseline_id: Optional[UUID]
|
|
28
|
+
compare_bin_delta: Optional[int]
|
|
29
|
+
warning_threshold: Optional[float]
|
|
30
|
+
|
|
31
|
+
created_at: datetime
|
|
32
|
+
updated_at: datetime = Field(alias='last_updated')
|
|
@@ -46,6 +46,8 @@ class CustomFeature(BaseModel):
|
|
|
46
46
|
return TextEmbedding.parse_obj(deserialized_json)
|
|
47
47
|
if feature_type == CustomFeatureType.FROM_IMAGE_EMBEDDING:
|
|
48
48
|
return ImageEmbedding.parse_obj(deserialized_json)
|
|
49
|
+
if feature_type == CustomFeatureType.ENRICHMENT:
|
|
50
|
+
return Enrichment.parse_obj(deserialized_json)
|
|
49
51
|
|
|
50
52
|
raise ValueError(f'Unsupported feature type: {feature_type}')
|
|
51
53
|
|
|
@@ -63,6 +65,10 @@ class CustomFeature(BaseModel):
|
|
|
63
65
|
return_dict['source_column'] = self.source_column
|
|
64
66
|
if isinstance(self, TextEmbedding):
|
|
65
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
|
|
66
72
|
else:
|
|
67
73
|
raise ValueError(f'Unsupported feature type: {self.type} {type(self)}')
|
|
68
74
|
|
|
@@ -88,3 +94,31 @@ class TextEmbedding(VectorFeature):
|
|
|
88
94
|
|
|
89
95
|
class ImageEmbedding(VectorFeature):
|
|
90
96
|
type: CustomFeatureType = CustomFeatureType.FROM_IMAGE_EMBEDDING
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Enrichment(CustomFeature):
|
|
100
|
+
"""
|
|
101
|
+
Represents an enrichment feature in a data processing or machine learning context.
|
|
102
|
+
|
|
103
|
+
This class inherits from CustomFeature and is used to apply specific enrichment
|
|
104
|
+
operations to a set of columns in a dataset. The type of enrichment and its
|
|
105
|
+
configuration can be specified.
|
|
106
|
+
|
|
107
|
+
Attributes:
|
|
108
|
+
type (CustomFeatureType): The type of the custom feature, set to ENRICHMENT.
|
|
109
|
+
columns (List[str]): The list of input column names in the dataset used to generate the enrichment.
|
|
110
|
+
enrichment (str): A string identifier for the type of enrichment to be applied.
|
|
111
|
+
config (Dict[str, Any]): A dictionary containing configuration options for the enrichment.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
# Setting the feature type to ENRICHMENT
|
|
115
|
+
type: CustomFeatureType = CustomFeatureType.ENRICHMENT
|
|
116
|
+
|
|
117
|
+
# List of input column names used to generate the enrichment
|
|
118
|
+
columns: List[str]
|
|
119
|
+
|
|
120
|
+
# String identifier for the enrichment to be applied. e.g. "embedding" or "toxicity"
|
|
121
|
+
enrichment: str
|
|
122
|
+
|
|
123
|
+
# Dictionary for additional configuration options for the enrichment
|
|
124
|
+
config: Dict[str, Any] = {}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from fiddler3.constants.events import PublishEventsSourceType
|
|
5
|
+
from fiddler3.schemas.base import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileSource(BaseModel):
|
|
9
|
+
file_id: UUID
|
|
10
|
+
type: PublishEventsSourceType = PublishEventsSourceType.FILE
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EventsSource(BaseModel):
|
|
14
|
+
events: List[Dict[Any, Any]]
|
|
15
|
+
type: PublishEventsSourceType = PublishEventsSourceType.EVENTS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from fiddler3.schemas.base import BaseModel
|
|
7
|
+
from fiddler3.schemas.user import UserCompactResp
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FileResp(BaseModel):
|
|
11
|
+
id: UUID
|
|
12
|
+
name: str = Field(alias='filename')
|
|
13
|
+
status: str
|
|
14
|
+
type: str
|
|
15
|
+
created_at: datetime
|
|
16
|
+
updated_at: datetime
|
|
17
|
+
created_by: UserCompactResp
|
|
18
|
+
updated_by: UserCompactResp
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FileCompactResp(BaseModel):
|
|
22
|
+
id: UUID
|
|
23
|
+
name: str
|
fiddler3/schemas/model.py
CHANGED
|
@@ -5,7 +5,7 @@ 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.model_schema import
|
|
8
|
+
from fiddler3.schemas.model_schema import ModelSchema
|
|
9
9
|
from fiddler3.schemas.model_spec import ModelSpec
|
|
10
10
|
from fiddler3.schemas.model_task_params import ModelTaskParams
|
|
11
11
|
from fiddler3.schemas.organization import OrganizationCompactResp
|
|
@@ -40,9 +40,4 @@ class ModelResp(BaseModel):
|
|
|
40
40
|
updated_by: UserCompactResp
|
|
41
41
|
organization: OrganizationCompactResp
|
|
42
42
|
project: ProjectCompactResp
|
|
43
|
-
input_cols: List[Column]
|
|
44
|
-
output_cols: List[Column]
|
|
45
|
-
target_cols: List[Column]
|
|
46
|
-
metadata_cols: List[Column]
|
|
47
|
-
decision_cols: List[Column]
|
|
48
43
|
is_binary_ranking_model: bool
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic.fields import Field
|
|
6
|
+
|
|
7
|
+
from fiddler3.schemas.base import BaseModel
|
|
8
|
+
from fiddler3.schemas.user import UserCompactResp
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@enum.unique
|
|
12
|
+
class WebhookProvider(str, enum.Enum):
|
|
13
|
+
# provider is 'SLACK' or 'OTHER' as of Aug 2023.
|
|
14
|
+
SLACK = 'SLACK'
|
|
15
|
+
OTHER = 'OTHER'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WebhookResp(BaseModel):
|
|
19
|
+
id: UUID = Field(alias='uuid')
|
|
20
|
+
name: str
|
|
21
|
+
url: str
|
|
22
|
+
provider: WebhookProvider
|
|
23
|
+
created_at: datetime
|
|
24
|
+
updated_at: datetime
|
|
25
|
+
created_by: UserCompactResp
|
|
26
|
+
updated_by: UserCompactResp
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import responses
|
|
4
|
+
|
|
5
|
+
from fiddler3.entities.alert_record import AlertRecord
|
|
6
|
+
from fiddler3.tests.constants import ALERT_RULE_ID, URL
|
|
7
|
+
|
|
8
|
+
ALERT_RECORD_LIST_RESPONSE = {
|
|
9
|
+
'data': {
|
|
10
|
+
'page_size': 100,
|
|
11
|
+
'total': 2,
|
|
12
|
+
'item_count': 2,
|
|
13
|
+
'page_count': 1,
|
|
14
|
+
'page_index': 1,
|
|
15
|
+
'offset': 0,
|
|
16
|
+
'items': [
|
|
17
|
+
{
|
|
18
|
+
'id': 26410,
|
|
19
|
+
'uuid': 'b520da6b-c01c-4375-bde6-2560cd972485',
|
|
20
|
+
'alert_config_uuid': ALERT_RULE_ID,
|
|
21
|
+
'alert_run_start_time': 1703255503133,
|
|
22
|
+
'alert_time_bucket': 1703116800000,
|
|
23
|
+
'baseline_time_bucket': None,
|
|
24
|
+
'baseline_value': None,
|
|
25
|
+
'is_alert': True,
|
|
26
|
+
'severity': 'CRITICAL',
|
|
27
|
+
'failure_reason': 'NA',
|
|
28
|
+
'message': '',
|
|
29
|
+
'alert_value': 37.0,
|
|
30
|
+
'feature_name': 'estimatedsalary',
|
|
31
|
+
'alert_record_main_version': 1,
|
|
32
|
+
'alert_record_sub_version': 2,
|
|
33
|
+
'created_at': '2023-12-22T14:31:43.499697+00:00',
|
|
34
|
+
'updated_at': '2023-12-22T14:31:43.499697+00:00',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
'id': 26418,
|
|
38
|
+
'uuid': '6e55540c-9053-4af8-b20e-1728b1436f35',
|
|
39
|
+
'alert_config_uuid': ALERT_RULE_ID,
|
|
40
|
+
'alert_run_start_time': 1703259102527,
|
|
41
|
+
'alert_time_bucket': 1703116800000,
|
|
42
|
+
'baseline_time_bucket': None,
|
|
43
|
+
'baseline_value': None,
|
|
44
|
+
'is_alert': True,
|
|
45
|
+
'severity': 'CRITICAL',
|
|
46
|
+
'failure_reason': 'NA',
|
|
47
|
+
'message': '',
|
|
48
|
+
'alert_value': 37.0,
|
|
49
|
+
'feature_name': 'estimatedsalary',
|
|
50
|
+
'alert_record_main_version': 1,
|
|
51
|
+
'alert_record_sub_version': 3,
|
|
52
|
+
'created_at': '2023-12-22T15:31:42.698623+00:00',
|
|
53
|
+
'updated_at': '2023-12-22T15:31:42.698623+00:00',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
'api_version': '2.0',
|
|
58
|
+
'kind': 'PAGINATED',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
ALERT_RECORD_EMPTY_LIST_RESPONSE = {
|
|
62
|
+
'data': {
|
|
63
|
+
'page_size': 100,
|
|
64
|
+
'total': 0,
|
|
65
|
+
'item_count': 0,
|
|
66
|
+
'page_count': 1,
|
|
67
|
+
'page_index': 1,
|
|
68
|
+
'offset': 0,
|
|
69
|
+
'items': [],
|
|
70
|
+
},
|
|
71
|
+
'api_version': '2.0',
|
|
72
|
+
'kind': 'PAGINATED',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@responses.activate
|
|
77
|
+
def test_alert_record_list_success() -> None:
|
|
78
|
+
responses.get(
|
|
79
|
+
url=f'{URL}/v2/alert-configs/{ALERT_RULE_ID}/records',
|
|
80
|
+
json=ALERT_RECORD_LIST_RESPONSE,
|
|
81
|
+
)
|
|
82
|
+
alert_records = AlertRecord.list(
|
|
83
|
+
alert_rule_id=ALERT_RULE_ID,
|
|
84
|
+
start_time=datetime(2023, 12, 18),
|
|
85
|
+
end_time=datetime(2023, 12, 25),
|
|
86
|
+
)
|
|
87
|
+
for record in alert_records:
|
|
88
|
+
assert isinstance(record, AlertRecord)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@responses.activate
|
|
92
|
+
def test_alert_record_list_success() -> None:
|
|
93
|
+
responses.get(
|
|
94
|
+
url=f'{URL}/v2/alert-configs/{ALERT_RULE_ID}/records',
|
|
95
|
+
json=ALERT_RECORD_EMPTY_LIST_RESPONSE,
|
|
96
|
+
)
|
|
97
|
+
alert_records = list(AlertRecord.list(alert_rule_id=ALERT_RULE_ID))
|
|
98
|
+
|
|
99
|
+
assert len(alert_records) == 0
|