fiddler-client 3.10.0__tar.gz → 3.11.0.dev3__tar.gz

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 (120) hide show
  1. {fiddler-client-3.10.0/fiddler_client.egg-info → fiddler_client-3.11.0.dev3}/PKG-INFO +23 -7
  2. fiddler_client-3.11.0.dev3/fiddler/VERSION +1 -0
  3. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/connection.py +6 -0
  4. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/common.py +1 -0
  5. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/decorators.py +2 -3
  6. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/alert_rule.py +2 -2
  7. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/custom_expression.py +111 -46
  8. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/model.py +91 -2
  9. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/exceptions.py +6 -6
  10. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/alert_rule.py +1 -1
  11. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_model.py +473 -0
  12. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/test_connection.py +44 -1
  13. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/column_generator.py +3 -1
  14. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3/fiddler_client.egg-info}/PKG-INFO +23 -7
  15. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler_client.egg-info/requires.txt +7 -7
  16. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/setup.py +2 -2
  17. fiddler-client-3.10.0/fiddler/VERSION +0 -1
  18. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/LICENSE.txt +0 -0
  19. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/MANIFEST.in +0 -0
  20. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/PUBLIC.md +0 -0
  21. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/README.md +0 -0
  22. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/__init__.py +1 -1
  23. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/configs.py +0 -0
  24. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/__init__.py +0 -0
  25. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/alert_rule.py +0 -0
  26. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/baseline.py +0 -0
  27. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/dataset.py +0 -0
  28. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/events.py +0 -0
  29. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/job.py +0 -0
  30. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/model.py +0 -0
  31. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/model_deployment.py +0 -0
  32. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/constants/xai.py +0 -0
  33. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/__init__.py +0 -0
  34. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/alert_record.py +0 -0
  35. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/base.py +0 -0
  36. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/baseline.py +0 -0
  37. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/dataset.py +0 -0
  38. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/events.py +1 -1
  39. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/file.py +0 -0
  40. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/job.py +0 -0
  41. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/model_artifact.py +0 -0
  42. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/model_deployment.py +0 -0
  43. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/organization.py +0 -0
  44. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/project.py +0 -0
  45. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/surrogate.py +0 -0
  46. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/user.py +0 -0
  47. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/webhook.py +0 -0
  48. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/entities/xai.py +0 -0
  49. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/libs/__init__.py +0 -0
  50. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/libs/aws.py +1 -1
  51. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/libs/http_client.py +0 -0
  52. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/libs/json_encoder.py +0 -0
  53. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/libs/semver.py +0 -0
  54. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/packtools/__init__.py +0 -0
  55. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/packtools/gem.py +0 -0
  56. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/packtools/keras_ig_helpers.py +0 -0
  57. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/packtools/project_attributions_helpers.py +0 -0
  58. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/packtools/template_model.py +0 -0
  59. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/__init__.py +0 -0
  60. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/alert_record.py +0 -0
  61. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/base.py +0 -0
  62. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/baseline.py +0 -0
  63. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/custom_expression.py +0 -0
  64. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/custom_features.py +0 -0
  65. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/dataset.py +0 -0
  66. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/events.py +0 -0
  67. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/file.py +0 -0
  68. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/filter_query.py +0 -0
  69. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/job.py +0 -0
  70. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/model.py +0 -0
  71. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/model_deployment.py +0 -0
  72. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/model_schema.py +0 -0
  73. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/model_spec.py +0 -0
  74. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/model_task_params.py +0 -0
  75. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/organization.py +0 -0
  76. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/project.py +0 -0
  77. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/response.py +0 -0
  78. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/server_info.py +0 -0
  79. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/user.py +0 -0
  80. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/webhook.py +0 -0
  81. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/xai.py +0 -0
  82. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/schemas/xai_params.py +0 -0
  83. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/__init__.py +0 -0
  84. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/__init__.py +0 -0
  85. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_alert_record.py +0 -0
  86. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_alert_rule.py +0 -0
  87. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_baseline.py +0 -0
  88. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_custom_metric.py +0 -0
  89. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_dataset.py +0 -0
  90. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_events.py +0 -0
  91. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_files.py +0 -0
  92. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_generate_model.py +0 -0
  93. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_job.py +0 -0
  94. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_mixin.py +0 -0
  95. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_model_artifact.py +0 -0
  96. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_model_deployment.py +0 -0
  97. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_model_surrogate.py +0 -0
  98. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_project.py +0 -0
  99. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_segment.py +0 -0
  100. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_webhook.py +0 -0
  101. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/apis/test_xai.py +0 -0
  102. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/conftest.py +0 -0
  103. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/constants.py +0 -0
  104. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/test_json_encoder.py +0 -0
  105. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/test_logger.py +0 -0
  106. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/test_sagemaker_auth.py +0 -0
  107. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/test_utils.py +0 -0
  108. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/tests/utils.py +0 -0
  109. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/__init__.py +0 -0
  110. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/decorators.py +0 -0
  111. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/helpers.py +0 -0
  112. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/logger.py +0 -0
  113. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/model_generator.py +0 -0
  114. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/validations.py +0 -0
  115. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/utils/version.py +0 -0
  116. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler/version.py +0 -0
  117. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler_client.egg-info/SOURCES.txt +0 -0
  118. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler_client.egg-info/dependency_links.txt +0 -0
  119. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/fiddler_client.egg-info/top_level.txt +0 -0
  120. {fiddler-client-3.10.0 → fiddler_client-3.11.0.dev3}/setup.cfg +0 -0
@@ -1,17 +1,35 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fiddler-client
3
- Version: 3.10.0
3
+ Version: 3.11.0.dev3
4
4
  Summary: Python client for Fiddler Platform
5
5
  Home-page: https://fiddler.ai
6
6
  Author: Fiddler Labs
7
- License: UNKNOWN
8
- Platform: UNKNOWN
9
7
  Classifier: Programming Language :: Python :: 3
10
8
  Classifier: License :: OSI Approved :: Apache Software License
11
9
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >3.9.0
10
+ Requires-Python: >3.10.0
13
11
  Description-Content-Type: text/markdown
14
12
  License-File: LICENSE.txt
13
+ Requires-Dist: pip>=21.0
14
+ Requires-Dist: requests<3
15
+ Requires-Dist: requests-toolbelt
16
+ Requires-Dist: pydantic>=1.10.17
17
+ Requires-Dist: deprecated==1.2.18
18
+ Requires-Dist: tqdm
19
+ Requires-Dist: simplejson>=3.17.0
20
+ Requires-Dist: pyyaml
21
+ Requires-Dist: typing-extensions<5,>=4.6.0
22
+ Requires-Dist: pandas==3.0.0
23
+ Requires-Dist: pyarrow>=15.0.0
24
+ Dynamic: author
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: license-file
30
+ Dynamic: requires-dist
31
+ Dynamic: requires-python
32
+ Dynamic: summary
15
33
 
16
34
  # fiddler-client
17
35
 
@@ -99,5 +117,3 @@ This package is proprietary software. Please refer to your Fiddler license agree
99
117
  ---
100
118
 
101
119
  **Want to see Fiddler in action?** [Request a demo](https://www.fiddler.ai/demo)
102
-
103
-
@@ -0,0 +1 @@
1
+ 3.11.0.dev3
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import sys
4
5
  from functools import cached_property
5
6
  from uuid import UUID
6
7
 
@@ -10,6 +11,7 @@ from fiddler.constants.common import (
10
11
  CLIENT_NAME,
11
12
  FIDDLER_CLIENT_NAME_HEADER,
12
13
  FIDDLER_CLIENT_VERSION_HEADER,
14
+ FIDDLER_PYTHON_VERSION_HEADER,
13
15
  )
14
16
  from fiddler.decorators import handle_api_error
15
17
  from fiddler.exceptions import IncompatibleClient
@@ -71,6 +73,7 @@ class Connection:
71
73
  validate=False # Skip version compatibility check
72
74
  )
73
75
  """
76
+
74
77
  def __init__( # pylint: disable=too-many-arguments
75
78
  self,
76
79
  url: str,
@@ -107,10 +110,12 @@ class Connection:
107
110
  if not token:
108
111
  raise ValueError('`token` is empty')
109
112
 
113
+ python_version = f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}'
110
114
  self.request_headers = {
111
115
  'Authorization': f'Bearer {token}',
112
116
  FIDDLER_CLIENT_NAME_HEADER: CLIENT_NAME,
113
117
  FIDDLER_CLIENT_VERSION_HEADER: __version__,
118
+ FIDDLER_PYTHON_VERSION_HEADER: python_version,
114
119
  }
115
120
 
116
121
  if validate:
@@ -257,6 +262,7 @@ class ConnectionMixin:
257
262
  org_name = SomeEntityClass.get_organization_name()
258
263
  org_id = SomeEntityClass.get_organization_id()
259
264
  """
265
+
260
266
  @classmethod
261
267
  def _conn(cls) -> Connection:
262
268
  """Get the global Fiddler connection instance.
@@ -7,6 +7,7 @@ JSON_CONTENT_TYPE = 'application/json'
7
7
 
8
8
  FIDDLER_CLIENT_NAME_HEADER = 'X-Fiddler-Client-Name'
9
9
  FIDDLER_CLIENT_VERSION_HEADER = 'X-Fiddler-Client-Version'
10
+ FIDDLER_PYTHON_VERSION_HEADER = 'X-Fiddler-Python-Version'
10
11
 
11
12
  # Multi-part upload
12
13
  MULTI_PART_CHUNK_SIZE = 100 * 1024 * 1024 # 100MB in bytes
@@ -1,15 +1,14 @@
1
+ import logging
1
2
  from functools import wraps
2
3
  from http import HTTPStatus
3
- import logging
4
4
  from typing import Any, Callable, TypeVar, cast
5
5
 
6
- import requests
7
6
  import pydantic.v1
7
+ import requests
8
8
 
9
9
  from fiddler.exceptions import ApiError, Conflict, HttpError, NotFound, Unsupported
10
10
  from fiddler.schemas.response import ErrorResponse
11
11
 
12
-
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
  _WrappedFuncType = TypeVar( # pylint: disable=invalid-name
@@ -148,7 +148,7 @@ class AlertRule(
148
148
  metric_id="accuracy",
149
149
  priority=Priority.MEDIUM,
150
150
  compare_to=CompareTo.TIME_PERIOD,
151
- condition=AlertCondition.LT,
151
+ condition=AlertCondition.LESSER,
152
152
  bin_size=BinSize.DAY,
153
153
  critical_threshold=0.85,
154
154
  compare_bin_delta=7 # Compare to 7 days ago
@@ -257,7 +257,7 @@ class AlertRule(
257
257
  metric_id="accuracy",
258
258
  priority=Priority.MEDIUM,
259
259
  compare_to=CompareTo.TIME_PERIOD,
260
- condition=AlertCondition.LT,
260
+ condition=AlertCondition.LESSER,
261
261
  bin_size=BinSize.DAY,
262
262
  critical_threshold=0.85,
263
263
  compare_bin_delta=7, # Compare to 7 days ago
@@ -70,6 +70,7 @@ Example:
70
70
  description="Accuracy for high-value customer segment"
71
71
  ).create()
72
72
  """
73
+
73
74
  from __future__ import annotations
74
75
 
75
76
  from abc import abstractmethod
@@ -112,6 +113,7 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
112
113
  This is an abstract base class. Use CustomMetric or Segment subclasses
113
114
  for creating specific types of custom expressions.
114
115
  """
116
+
115
117
  def __init__(
116
118
  self,
117
119
  name: str,
@@ -119,7 +121,18 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
119
121
  definition: str,
120
122
  description: str | None = None,
121
123
  ) -> None:
122
- """Construct a custom expression instance."""
124
+ """Construct a CustomExpression instance.
125
+
126
+ Args:
127
+ name: Human-readable name for the CustomExpression.
128
+ Should be descriptive and follow naming conventions.
129
+ model_id: UUID or string identifier of the associated Model.
130
+ definition: Fiddler Query Language (FQL) expression defining the calculation
131
+ or filtering logic. Uses SQL-like syntax with functions like
132
+ sum(), average(), if(), is_null(), etc.
133
+ description: Optional human-readable description explaining the purpose
134
+ and usage of the CustomExpression.
135
+ """
123
136
  self.name = name
124
137
  self.model_id = model_id
125
138
  self.definition = definition
@@ -134,8 +147,8 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
134
147
  @classmethod
135
148
  def _get_url(cls, id_: UUID | str | None = None) -> str:
136
149
  """Get custom expression resource/item url."""
137
- url = f'/v3/{cls._get_url_path()}'
138
- return url if not id_ else f'{url}/{id_}'
150
+ url = f"/v3/{cls._get_url_path()}"
151
+ return url if not id_ else f"{url}/{id_}"
139
152
 
140
153
  @staticmethod
141
154
  @abstractmethod
@@ -153,11 +166,11 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
153
166
  resp_obj = CustomMetricResp(**data)
154
167
  assert self.model_id
155
168
  fields = [
156
- 'id',
157
- 'name',
158
- 'definition',
159
- 'description',
160
- 'created_at',
169
+ "id",
170
+ "name",
171
+ "definition",
172
+ "description",
173
+ "created_at",
161
174
  ]
162
175
  for field in fields:
163
176
  setattr(self, field, getattr(resp_obj, field, None))
@@ -181,8 +194,8 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
181
194
 
182
195
  # Add remaining fields
183
196
  fields = [
184
- 'id',
185
- 'created_at',
197
+ "id",
198
+ "created_at",
186
199
  ]
187
200
  for field in fields:
188
201
  setattr(instance, field, getattr(resp_obj, field, None))
@@ -194,11 +207,20 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
194
207
  @classmethod
195
208
  @handle_api_error
196
209
  def get(cls, id_: UUID | str) -> CustomExpression:
197
- """
198
- Get the CustomMetric instance using custom metric id.
210
+ """Retrieve a CustomExpression by its unique identifier.
211
+
212
+ Fetches a CustomExpression from the Fiddler platform using its UUID.
213
+
214
+ Args:
215
+ id_: The unique identifier (UUID) of the CustomExpression to retrieve.
216
+ Can be provided as a UUID object or string representation.
199
217
 
200
- :param id_: unique uuid format identifier for custom metric
201
- :return: CustomMetric instance
218
+ Returns:
219
+ The CustomExpression instance with all its configuration and metadata.
220
+
221
+ Raises:
222
+ NotFound: If no CustomExpression exists with the specified ID.
223
+ ApiError: If there's an error communicating with the Fiddler API.
202
224
  """
203
225
  response = cls._client().get(url=cls._get_url(id_=id_))
204
226
  return cls._from_response(response=response)
@@ -206,37 +228,45 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
206
228
  @classmethod
207
229
  @handle_api_error
208
230
  def from_name(cls, name: str, model_id: UUID | str) -> CustomExpression:
209
- """
210
- Get the custom metric instance of a model from custom metric name
231
+ """Retrieve a CustomExpression by name and model.
232
+
233
+ Fetches a CustomExpression from the Fiddler platform using its name
234
+ and associated model ID.
211
235
 
212
- :param name: Custom metric name
213
- :param model_id: Model identifier
236
+ Args:
237
+ name: The name of the CustomExpression to retrieve.
238
+ model_id: UUID or string identifier of the associated Model.
214
239
 
215
- :return: CustomMetric instance for the provided params
240
+ Returns:
241
+ The CustomExpression instance for the provided parameters.
242
+
243
+ Raises:
244
+ NotFound: If no CustomExpression exists with the specified name and model.
245
+ ApiError: If there's an error communicating with the Fiddler API.
216
246
  """
217
247
 
218
248
  _filter = QueryCondition(
219
249
  rules=[
220
- QueryRule(field='name', operator=OperatorType.EQUAL, value=name),
250
+ QueryRule(field="name", operator=OperatorType.EQUAL, value=name),
221
251
  QueryRule(
222
- field='model_id', operator=OperatorType.EQUAL, value=model_id
252
+ field="model_id", operator=OperatorType.EQUAL, value=model_id
223
253
  ),
224
254
  ]
225
255
  )
226
256
  params: dict[str, Any] = {
227
- 'filter': _filter.json(),
257
+ "filter": _filter.json(),
228
258
  }
229
259
 
230
260
  response = cls._client().get(
231
261
  url=cls._get_url(),
232
262
  params=params,
233
263
  )
234
- if response.json()['data']['total'] == 0:
264
+ if response.json()["data"]["total"] == 0:
235
265
  raise_not_found(
236
- f'{cls._get_display_name()} not found for the given identifier'
266
+ f"{cls._get_display_name()} not found for the given identifier"
237
267
  )
238
268
 
239
- return cls._from_dict(data=response.json()['data']['items'][0])
269
+ return cls._from_dict(data=response.json()["data"]["items"][0])
240
270
 
241
271
  @classmethod
242
272
  @handle_api_error
@@ -244,36 +274,69 @@ class CustomExpression(BaseEntity, ModelCompactMixin, ProjectCompactMixin):
244
274
  cls,
245
275
  model_id: UUID | str,
246
276
  ) -> Iterator[CustomExpression]:
247
- """Get a list of all custom metrics in the organization."""
277
+ """List all CustomExpression instances for a model.
278
+
279
+ Retrieves all CustomExpression instances associated with a specific model.
280
+
281
+ Args:
282
+ model_id: UUID or string identifier of the Model.
248
283
 
249
- url = f'/v3/models/{model_id}/{cls._get_url_path()}'
284
+ Yields:
285
+ CustomExpression instances for each CustomExpression in the model.
286
+
287
+ Raises:
288
+ ApiError: If there's an error communicating with the Fiddler API.
289
+ """
290
+
291
+ url = f"/v3/models/{model_id}/{cls._get_url_path()}"
250
292
 
251
293
  for item in cls._paginate(url=url):
252
294
  yield cls._from_dict(data=item)
253
295
 
254
296
  @handle_api_error
255
297
  def create(self) -> CustomExpression:
256
- """Create a new custom metric."""
298
+ """Create a new CustomExpression on the Fiddler platform.
299
+
300
+ Registers this CustomExpression with the Fiddler platform. The expression
301
+ must have a name, model_id, and definition specified before calling create().
302
+
303
+ Returns:
304
+ The same CustomExpression instance with updated server-side attributes
305
+ (id, created_at, etc.).
306
+
307
+ Raises:
308
+ ApiError: If there's an error communicating with the Fiddler API.
309
+ Conflict: If a CustomExpression with the same name already exists for this model.
310
+ """
257
311
  payload = {
258
- 'model_id': self.model_id,
259
- 'name': self.name,
260
- 'definition': self.definition,
312
+ "model_id": self.model_id,
313
+ "name": self.name,
314
+ "definition": self.definition,
261
315
  }
262
316
 
263
317
  if self.description:
264
- payload['description'] = self.description
318
+ payload["description"] = self.description
265
319
 
266
320
  response = self._client().post(
267
321
  url=self._get_url(),
268
322
  data=payload,
269
- headers={'Content-Type': 'application/json'},
323
+ headers={"Content-Type": "application/json"},
270
324
  )
271
325
  self._refresh_from_response(response=response)
272
326
  return self
273
327
 
274
328
  @handle_api_error
275
329
  def delete(self) -> None:
276
- """Delete a custom metric."""
330
+ """Delete this CustomExpression from the Fiddler platform.
331
+
332
+ Permanently removes the CustomExpression. This action cannot be undone.
333
+ Any alert rules or monitors using this CustomExpression must be deleted first.
334
+
335
+ Raises:
336
+ NotFound: If the CustomExpression no longer exists.
337
+ ApiError: If there's an error communicating with the Fiddler API.
338
+ Conflict: If the CustomExpression is still being used by alert rules or monitors.
339
+ """
277
340
  assert self.id is not None
278
341
 
279
342
  self._client().delete(url=self._get_url(id_=self.id))
@@ -295,7 +358,7 @@ class CustomMetric(CustomExpression):
295
358
  conversion_rate = CustomMetric(
296
359
  name="weekly_conversion_rate",
297
360
  model_id=model.id,
298
- definition="SUM(CASE WHEN prediction_score > 0.7 AND converted = 1 THEN 1 ELSE 0 END) / SUM(CASE WHEN prediction_score > 0.7 THEN 1 ELSE 0 END)",
361
+ definition="sum(if(prediction_score > 0.7 and converted == 1, 1, 0)) / sum(if(prediction_score > 0.7, 1, 0))",
299
362
  description="Conversion rate for high-confidence predictions"
300
363
  ).create()
301
364
 
@@ -303,7 +366,7 @@ class CustomMetric(CustomExpression):
303
366
  missing_rate = CustomMetric(
304
367
  name="feature_missing_rate",
305
368
  model_id=model.id,
306
- definition="SUM(CASE WHEN income IS NULL THEN 1 ELSE 0 END) / COUNT(*)",
369
+ definition="sum(if(is_null(income), 1, 0)) / count(income)",
307
370
  description="Percentage of records with missing income values"
308
371
  ).create()
309
372
 
@@ -311,7 +374,7 @@ class CustomMetric(CustomExpression):
311
374
  fairness_metric = CustomMetric(
312
375
  name="demographic_parity",
313
376
  model_id=model.id,
314
- definition="ABS(AVG(CASE WHEN gender = 'M' THEN prediction_score ELSE NULL END) - AVG(CASE WHEN gender = 'F' THEN prediction_score ELSE NULL END))",
377
+ definition="abs((sum(if(gender == 'Male', predicted_churn, 0)) / sum(if(gender == 'Male', 1, 0))) - (sum(if(gender == 'Female', predicted_churn, 0)) / sum(if(gender == 'Female', 1, 0))))",
315
378
  description="Demographic parity difference between gender groups"
316
379
  ).create()
317
380
 
@@ -322,7 +385,7 @@ class CustomMetric(CustomExpression):
322
385
  metric_id=conversion_rate.id,
323
386
  priority=Priority.HIGH,
324
387
  compare_to=CompareTo.TIME_PERIOD,
325
- condition=AlertCondition.LT,
388
+ condition=AlertCondition.LESSER,
326
389
  bin_size=BinSize.DAY,
327
390
  critical_threshold=0.15, # Alert if conversion drops below 15%
328
391
  compare_bin_delta=7
@@ -333,6 +396,7 @@ class CustomMetric(CustomExpression):
333
396
  Complex expressions may impact performance, so optimize for efficiency.
334
397
  Test expressions thoroughly before using in production alert rules.
335
398
  """
399
+
336
400
  def __init__(
337
401
  self,
338
402
  name: str,
@@ -348,11 +412,11 @@ class CustomMetric(CustomExpression):
348
412
 
349
413
  @staticmethod
350
414
  def _get_url_path() -> str:
351
- return 'custom-metrics'
415
+ return "custom-metrics"
352
416
 
353
417
  @staticmethod
354
418
  def _get_display_name() -> str:
355
- return 'Custom metric'
419
+ return "Custom metric"
356
420
 
357
421
 
358
422
  class Segment(CustomExpression):
@@ -370,7 +434,7 @@ class Segment(CustomExpression):
370
434
  high_value_segment = Segment(
371
435
  name="high_value_customers",
372
436
  model_id=model.id,
373
- definition="customer_lifetime_value > 10000 AND account_age_days > 365",
437
+ definition="customer_lifetime_value > 10000 and account_age_days > 365",
374
438
  description="Customers with high LTV and established accounts"
375
439
  ).create()
376
440
 
@@ -378,7 +442,7 @@ class Segment(CustomExpression):
378
442
  west_coast_segment = Segment(
379
443
  name="west_coast_users",
380
444
  model_id=model.id,
381
- definition="state IN ('CA', 'OR', 'WA')",
445
+ definition="state == 'CA' or state == 'OR' or state == 'WA'",
382
446
  description="Users from West Coast states"
383
447
  ).create()
384
448
 
@@ -386,7 +450,7 @@ class Segment(CustomExpression):
386
450
  high_risk_segment = Segment(
387
451
  name="high_risk_applications",
388
452
  model_id=model.id,
389
- definition="credit_score < 600 OR debt_to_income > 0.4",
453
+ definition="credit_score < 600 or debt_to_income > 0.4",
390
454
  description="Loan applications with elevated risk factors"
391
455
  ).create()
392
456
 
@@ -394,7 +458,7 @@ class Segment(CustomExpression):
394
458
  young_adults_segment = Segment(
395
459
  name="young_adults",
396
460
  model_id=model.id,
397
- definition="age >= 18 AND age <= 35",
461
+ definition="age >= 18 and age <= 35",
398
462
  description="Young adult demographic (18-35 years)"
399
463
  ).create()
400
464
 
@@ -418,6 +482,7 @@ class Segment(CustomExpression):
418
482
  so optimize for efficiency. Segments are particularly useful for fairness
419
483
  monitoring and business-critical cohort analysis.
420
484
  """
485
+
421
486
  def __init__(
422
487
  self,
423
488
  name: str,
@@ -433,8 +498,8 @@ class Segment(CustomExpression):
433
498
 
434
499
  @staticmethod
435
500
  def _get_url_path() -> str:
436
- return 'segments'
501
+ return "segments"
437
502
 
438
503
  @staticmethod
439
504
  def _get_display_name() -> str:
440
- return 'Segment'
505
+ return "Segment"
@@ -35,6 +35,7 @@ Example:
35
35
  prod_data = pd.read_csv("production_events.csv")
36
36
  job = model.publish(source=prod_data, environment=fiddler.EnvType.PRODUCTION)
37
37
  """
38
+
38
39
  from __future__ import annotations
39
40
 
40
41
  import builtins
@@ -66,7 +67,7 @@ from fiddler.schemas.filter_query import OperatorType, QueryCondition, QueryRule
66
67
  from fiddler.schemas.job import JobCompactResp
67
68
  from fiddler.schemas.model import ModelResp
68
69
  from fiddler.schemas.model_deployment import DeploymentParams
69
- from fiddler.schemas.model_schema import ModelSchema
70
+ from fiddler.schemas.model_schema import Column, ModelSchema
70
71
  from fiddler.schemas.model_spec import ModelSpec
71
72
  from fiddler.schemas.model_task_params import ModelTaskParams
72
73
  from fiddler.schemas.xai_params import XaiParams
@@ -575,6 +576,93 @@ class Model(
575
576
  )
576
577
  self._refresh_from_response(response=response)
577
578
 
579
+ @handle_api_error
580
+ def add_column(
581
+ self,
582
+ column: Column,
583
+ column_type: str = 'metadata',
584
+ ) -> None:
585
+ """Add a new column to the model schema.
586
+
587
+ Updates both the schema and spec to include the new column. This allows
588
+ you to extend your model with additional columns after initial creation.
589
+
590
+ .. versionadded:: 3.11.0
591
+
592
+ Args:
593
+ column: Column object defining the new column's properties (name, data_type, etc.)
594
+ column_type: Type of column in spec. One of: 'inputs', 'outputs', 'targets',
595
+ 'decisions', 'metadata'. Defaults to 'metadata'.
596
+
597
+ Raises:
598
+ ValueError: If column already exists or column_type is invalid
599
+ BadRequest: If column definition is invalid per backend validation
600
+
601
+ Example:
602
+ # Add a numeric metadata column
603
+ new_col = Column(
604
+ name="customer_segment",
605
+ data_type=DataType.INTEGER,
606
+ min=1,
607
+ max=5
608
+ )
609
+ model.add_column(column=new_col, column_type='metadata')
610
+
611
+ # Add a categorical feature
612
+ category_col = Column(
613
+ name="region",
614
+ data_type=DataType.CATEGORY,
615
+ categories=["US", "EU", "APAC"]
616
+ )
617
+ model.add_column(column=category_col, column_type='inputs')
618
+
619
+ Note:
620
+ - Adding a column doesn't populate historical data; new column will be null
621
+ for past events
622
+ - Column names must be unique within the model
623
+ - After adding a column, include it in future event publishing
624
+ """
625
+ valid_column_types = [
626
+ 'inputs',
627
+ 'outputs',
628
+ 'targets',
629
+ 'decisions',
630
+ 'metadata',
631
+ ]
632
+
633
+ if column_type not in valid_column_types:
634
+ raise ValueError(
635
+ f"column_type must be one of {valid_column_types}, got: '{column_type}'"
636
+ )
637
+
638
+ # Check if column already exists
639
+ existing_names = [col.name for col in self.schema.columns]
640
+ if column.name in existing_names:
641
+ raise ValueError(
642
+ f"Column '{column.name}' already exists in model schema. "
643
+ f'Cannot add existing column.'
644
+ )
645
+
646
+ # Create updated schema with new column
647
+ updated_columns = [col.dict() for col in self.schema.columns] + [column.dict()]
648
+
649
+ # Create updated spec with new column
650
+ updated_spec = self.spec.dict()
651
+ current_list = updated_spec.get(column_type) or []
652
+ updated_spec[column_type] = current_list + [column.name]
653
+
654
+ body: dict[str, Any] = {
655
+ 'schema': {'columns': updated_columns},
656
+ 'spec': updated_spec,
657
+ }
658
+
659
+ response = self._client().patch(
660
+ url=self._get_url(id_=self.id),
661
+ json=body,
662
+ )
663
+
664
+ self._refresh_from_response(response=response)
665
+
578
666
  @classmethod
579
667
  @handle_api_error
580
668
  def list(
@@ -967,7 +1055,7 @@ class Model(
967
1055
 
968
1056
  Args:
969
1057
  source: Data to publish. Supported formats:
970
- - **File path (str/Path)**: CSV, Parquet, or JSON files.
1058
+ - **File path (str/Path)**: CSV or Parquet files.
971
1059
  Best for large datasets and batch uploads.
972
1060
  - **DataFrame**: Pandas DataFrame with prediction events.
973
1061
  Good for programmatic uploads and real-time data.
@@ -1185,6 +1273,7 @@ class ModelCompact:
1185
1273
  print(f"Task: {full_model.task}")
1186
1274
  print(f"Schema: {len(full_model.schema.columns)} columns")
1187
1275
  """
1276
+
1188
1277
  id: UUID
1189
1278
  name: str
1190
1279
  version: str | None = None
@@ -46,7 +46,7 @@ class BaseError(Exception):
46
46
  return self.__class__.__name__
47
47
 
48
48
 
49
- class IncompatibleClient(BaseError):
49
+ class IncompatibleClient(BaseError): # noqa: N818
50
50
  """Raised when the Python client version is incompatible with the Fiddler platform version.
51
51
 
52
52
  This exception occurs during connection initialization when the client library
@@ -88,7 +88,7 @@ class IncompatibleClient(BaseError):
88
88
  super().__init__(self.message)
89
89
 
90
90
 
91
- class AsyncJobFailed(BaseError):
91
+ class AsyncJobFailed(BaseError): # noqa: N818
92
92
  """Raised when an asynchronous job fails to execute successfully.
93
93
 
94
94
  This exception is thrown when long-running operations (such as model training,
@@ -120,7 +120,7 @@ class AsyncJobFailed(BaseError):
120
120
  """
121
121
 
122
122
 
123
- class Unsupported(BaseError):
123
+ class Unsupported(BaseError): # noqa: N818
124
124
  """Raised when an unsupported operation is attempted.
125
125
 
126
126
  This exception occurs when users try to perform operations that are not
@@ -176,7 +176,7 @@ class HttpError(BaseError):
176
176
  """
177
177
 
178
178
 
179
- class ConnTimeout(HttpError):
179
+ class ConnTimeout(HttpError): # noqa: N818
180
180
  """Raised when a connection timeout occurs during HTTP requests.
181
181
 
182
182
  This exception was used to indicate that an HTTP request to the Fiddler
@@ -289,7 +289,7 @@ class ApiError(HttpError):
289
289
  super().__init__(self.message)
290
290
 
291
291
 
292
- class NotFound(ApiError):
292
+ class NotFound(ApiError): # noqa: N818
293
293
  """Raised when a requested resource is not found (HTTP 404).
294
294
 
295
295
  This exception is thrown when attempting to access or manipulate a resource
@@ -332,7 +332,7 @@ class NotFound(ApiError):
332
332
  reason: str = 'NotFound'
333
333
 
334
334
 
335
- class Conflict(ApiError):
335
+ class Conflict(ApiError): # noqa: N818
336
336
  """Raised when a request conflicts with the current state of a resource (HTTP 409).
337
337
 
338
338
  This exception occurs when attempting to perform an operation that conflicts
@@ -9,7 +9,7 @@ from fiddler.constants.alert_rule import (
9
9
  AlertThresholdAlgo,
10
10
  BinSize,
11
11
  CompareTo,
12
- Priority
12
+ Priority,
13
13
  )
14
14
  from fiddler.schemas.base import BaseModel
15
15
  from fiddler.schemas.baseline import BaselineCompactResp