fiddler-client 3.5.0.dev2__tar.gz → 3.6.0__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 (117) hide show
  1. {fiddler_client-3.5.0.dev2/fiddler_client.egg-info → fiddler_client-3.6.0}/PKG-INFO +1 -1
  2. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/README.md +2 -2
  3. fiddler_client-3.6.0/fiddler/VERSION +1 -0
  4. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/__init__.py +0 -2
  5. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/job.py +45 -10
  6. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/xai.py +19 -81
  7. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/exceptions.py +12 -3
  8. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/libs/http_client.py +33 -21
  9. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/xai.py +0 -6
  10. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_xai.py +0 -108
  11. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0/fiddler_client.egg-info}/PKG-INFO +1 -1
  12. fiddler_client-3.5.0.dev2/fiddler/VERSION +0 -1
  13. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/LICENSE.txt +0 -0
  14. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/MANIFEST.in +0 -0
  15. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/PUBLIC.md +0 -0
  16. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/configs.py +0 -0
  17. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/connection.py +0 -0
  18. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/__init__.py +0 -0
  19. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/alert_rule.py +0 -0
  20. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/baseline.py +0 -0
  21. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/common.py +0 -0
  22. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/dataset.py +0 -0
  23. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/events.py +0 -0
  24. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/job.py +0 -0
  25. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/model.py +0 -0
  26. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/model_deployment.py +0 -0
  27. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/constants/xai.py +0 -0
  28. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/decorators.py +0 -0
  29. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/__init__.py +0 -0
  30. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/alert_record.py +0 -0
  31. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/alert_rule.py +0 -0
  32. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/base.py +0 -0
  33. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/baseline.py +0 -0
  34. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/custom_expression.py +0 -0
  35. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/dataset.py +0 -0
  36. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/events.py +0 -0
  37. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/file.py +0 -0
  38. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/model.py +0 -0
  39. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/model_artifact.py +0 -0
  40. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/model_deployment.py +0 -0
  41. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/organization.py +0 -0
  42. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/project.py +0 -0
  43. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/surrogate.py +0 -0
  44. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/user.py +0 -0
  45. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/entities/webhook.py +0 -0
  46. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/libs/__init__.py +0 -0
  47. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/libs/json_encoder.py +0 -0
  48. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/libs/semver.py +0 -0
  49. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/packtools/__init__.py +0 -0
  50. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/packtools/gem.py +0 -0
  51. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/packtools/keras_ig_helpers.py +0 -0
  52. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/packtools/project_attributions_helpers.py +0 -0
  53. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/packtools/template_model.py +0 -0
  54. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/__init__.py +0 -0
  55. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/alert_record.py +0 -0
  56. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/alert_rule.py +0 -0
  57. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/base.py +0 -0
  58. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/baseline.py +0 -0
  59. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/custom_expression.py +0 -0
  60. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/custom_features.py +0 -0
  61. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/dataset.py +0 -0
  62. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/events.py +0 -0
  63. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/file.py +0 -0
  64. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/filter_query.py +0 -0
  65. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/job.py +0 -0
  66. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/model.py +0 -0
  67. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/model_deployment.py +0 -0
  68. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/model_schema.py +0 -0
  69. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/model_spec.py +0 -0
  70. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/model_task_params.py +0 -0
  71. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/organization.py +0 -0
  72. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/project.py +0 -0
  73. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/response.py +0 -0
  74. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/server_info.py +0 -0
  75. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/user.py +0 -0
  76. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/webhook.py +0 -0
  77. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/schemas/xai_params.py +0 -0
  78. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/__init__.py +0 -0
  79. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/__init__.py +0 -0
  80. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_alert_record.py +0 -0
  81. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_alert_rule.py +0 -0
  82. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_baseline.py +0 -0
  83. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_custom_metric.py +0 -0
  84. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_dataset.py +0 -0
  85. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_events.py +0 -0
  86. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_files.py +0 -0
  87. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_generate_model.py +0 -0
  88. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_job.py +0 -0
  89. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_mixin.py +0 -0
  90. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_model.py +0 -0
  91. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_model_artifact.py +0 -0
  92. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_model_deployment.py +0 -0
  93. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_model_surrogate.py +0 -0
  94. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_project.py +0 -0
  95. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_segment.py +0 -0
  96. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/apis/test_webhook.py +0 -0
  97. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/conftest.py +0 -0
  98. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/constants.py +0 -0
  99. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/test_connection.py +0 -0
  100. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/test_json_encoder.py +0 -0
  101. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/test_logger.py +0 -0
  102. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/test_utils.py +0 -0
  103. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/tests/utils.py +0 -0
  104. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/__init__.py +0 -0
  105. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/decorators.py +0 -0
  106. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/helpers.py +0 -0
  107. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/logger.py +0 -0
  108. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/model_generator.py +0 -0
  109. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/validations.py +0 -0
  110. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/utils/version.py +0 -0
  111. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler/version.py +0 -0
  112. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler_client.egg-info/SOURCES.txt +0 -0
  113. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler_client.egg-info/dependency_links.txt +0 -0
  114. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler_client.egg-info/requires.txt +0 -0
  115. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/fiddler_client.egg-info/top_level.txt +0 -0
  116. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/setup.cfg +0 -0
  117. {fiddler_client-3.5.0.dev2 → fiddler_client-3.6.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fiddler-client
3
- Version: 3.5.0.dev2
3
+ Version: 3.6.0
4
4
  Summary: Python client for Fiddler Platform
5
5
  Home-page: https://fiddler.ai
6
6
  Author: Fiddler Labs
@@ -1,6 +1,6 @@
1
- [![codecov](https://codecov.io/gh/fiddler-labs/python-client/graph/badge.svg?token=2E6AAUXO56)](https://codecov.io/gh/fiddler-labs/python-client)
1
+ # fiddler-client
2
2
 
3
- # Fiddler Python client
3
+ A Python library for interacting with the Fiddler HTTP API.
4
4
 
5
5
  ## Pre Release
6
6
 
@@ -0,0 +1 @@
1
+ 3.6.0
@@ -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',
@@ -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
- response = self._client().get(url=self._get_url(id_=self.id))
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
- except ConnectionError:
122
- logger.exception(
123
- 'Failed to connect to the server. Retrying...\n'
124
- 'If this error persists, please reach out to Fiddler.'
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
- yield self
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
- current_time = time.time()
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,
@@ -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 | SqlSliceQueryDataSource | None = None,
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: DataSource for the reference data to compute explanation
59
- on (DatasetDataSource, SqlSliceQueryDataSource).
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
- ) -> pd.DataFrame:
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
- self._check_id_attributes()
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
- self._check_id_attributes()
254
- output_dir = Path(output_dir)
255
- if not output_dir.exists():
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
- ) -> dict:
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
- self._check_id_attributes()
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 | SqlSliceQueryDataSource,
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: DataSource for the input dataset to compute feature
358
- impact on (DatasetDataSource or SqlSliceQueryDataSource)
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 | SqlSliceQueryDataSource,
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: DataSource for the input dataset to compute feature
410
- importance on (DatasetDataSource or SqlSliceQueryDataSource)
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)
@@ -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
 
@@ -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: int | None = None,
50
+ timeout: float | tuple[float, float] | None = None,
53
51
  **kwargs: Any,
54
52
  ) -> requests.Response:
55
53
  """
56
- Make request to server
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('Making %s call to %s', method, url)
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
- response = self.session.request(
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
- except requests.Timeout as exc:
97
- logger.error('%s call failed with Timeout error for URL %s', method, url)
98
- raise ConnTimeout() from exc
99
- except requests.ConnectionError as exc:
100
- logger.error('%s call failed with ConnectionError for URL %s', method, url)
101
- raise ConnError() from exc
102
- except requests.RequestException as exc:
103
- # catastrophic error.
104
- logger.error('%s call failed with RequestException for %s', method, url)
105
- raise HttpError() from exc
106
-
107
- response.raise_for_status()
108
- return response
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: int | None = None,
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."""
@@ -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]
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fiddler-client
3
- Version: 3.5.0.dev2
3
+ Version: 3.6.0
4
4
  Summary: Python client for Fiddler Platform
5
5
  Home-page: https://fiddler.ai
6
6
  Author: Fiddler Labs
@@ -1 +0,0 @@
1
- 3.5.0.dev2