datarobot-moderations 11.2.5__py3-none-any.whl → 11.2.7__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.
@@ -232,9 +232,10 @@ class AsyncHTTPClient:
232
232
  try:
233
233
  response = await self.session.post(url, json=payload, headers=self.json_headers)
234
234
  if response.status != HTTPStatus.ACCEPTED:
235
+ response_text = await response.text()
235
236
  raise Exception(
236
237
  f"Error uploading custom metrics: Status Code: {response.status}"
237
- f"Message: {response.text}"
238
+ f"Message: {response_text}"
238
239
  )
239
240
  self._logger.info("Successfully uploaded custom metrics")
240
241
  except Exception as e:
@@ -774,11 +774,8 @@ def guard_chat_wrapper(
774
774
  # extract any fields mentioned in "datarobot_metrics" to send as custom metrics later
775
775
  completion_create_params, chat_extra_body_params = filter_extra_body(completion_create_params)
776
776
 
777
- # register them for ID lookup later
778
- pipeline.add_extra_body_custom_metric_definition(chat_extra_body_params)
779
-
780
777
  # define all pipeline-based and guard-based custom metrics (but not those from extra_body)
781
- # sends single API request to look up IDs for all custom metrics (including extra_body)
778
+ # note: this is usually partially done at pipeline init; see delayed_custom_metric_creation
782
779
  pipeline.get_new_metrics_payload()
783
780
 
784
781
  # the chat request is not a dataframe, but we'll build a DF internally for moderation.
@@ -801,6 +798,13 @@ def guard_chat_wrapper(
801
798
  if association_id:
802
799
  data[association_id_column_name] = [association_id]
803
800
 
801
+ # DRUM initializes the pipeline (which reads the deployment's list of custom metrics)
802
+ # at start time.
803
+ # If there are no extra_body fields (meaning no user-defined custom metrics to report),
804
+ # then the list does not need to be reread.
805
+ if chat_extra_body_params:
806
+ pipeline.lookup_custom_metric_ids()
807
+
804
808
  # report any metrics from extra_body. They are not tied to a prompt or response phase.
805
809
  _logger.debug("Report extra_body params as custom metrics")
806
810
  pipeline.report_custom_metrics_from_extra_body(association_id, chat_extra_body_params)
@@ -380,20 +380,13 @@ class LLMPipeline(Pipeline):
380
380
  buckets = self._add_guard_specific_custom_metrics(row, self.get_postscore_guards())
381
381
  payload["buckets"].extend(buckets)
382
382
 
383
- def add_extra_body_custom_metric_definition(self, chat_extra_body_params: dict) -> None:
384
- """
385
- For each name found in extra_body earlier, add to the internal map for reporting later.
386
- Custom metric IDs are looked up in create_custom_metrics().
387
- :param chat_extra_body_params: dict of name=value pairs
388
- """
389
- for name in chat_extra_body_params.keys():
390
- self.extra_body_custom_metric_map[name] = {"id": None}
391
-
392
383
  def report_custom_metrics_from_extra_body(
393
384
  self, association_id: str, extra_params: dict
394
385
  ) -> None:
395
386
  """
396
387
  Add any key-value pairs extracted from extra_body as custom metrics.
388
+ The custom metrics must be deployment-based, not model-specific.
389
+ (Bulk upload does not support heterogeneous model/non-model metrics.)
397
390
  :param association_id: Association ID of the chat request
398
391
  :param extra_params: a dict of {"name": value} for all extra_body parameters found
399
392
  """
@@ -412,19 +405,19 @@ class LLMPipeline(Pipeline):
412
405
  f"will not be sent: {name}"
413
406
  )
414
407
  continue
415
- if name not in self.extra_body_custom_metric_map:
408
+ if name not in self.custom_metric_names_to_ids:
416
409
  self._logger.warning(f"extra_body custom metric ID not in map: {name}")
417
410
  continue
418
- metric_id = self.extra_body_custom_metric_map[name].get("id")
411
+ metric_id = self.custom_metric_names_to_ids.get(name)
419
412
  if not metric_id:
413
+ # this should not be possible, as the name/id information
414
+ # is taken directly from DataRobot API
420
415
  self._logger.warning(f"extra_body custom metric has missing ID: {name}")
421
416
  continue
422
417
  payload["buckets"].append(
423
- {
424
- "customMetricId": metric_id,
425
- "value": value,
426
- "associationId": association_id,
427
- }
418
+ self.custom_metric_individual_payload(
419
+ metric_id=metric_id, value=value, association_id=association_id
420
+ )
428
421
  )
429
422
  self._logger.debug(f"Sending custom metrics payload from extra_body: {payload}")
430
423
  self.upload_custom_metrics(payload)
@@ -53,8 +53,7 @@ class Pipeline:
53
53
  self._custom_metrics_bulk_upload_url = None
54
54
  self.aggregate_custom_metric = None
55
55
  self.custom_metric_map = dict()
56
- # even though only LLMPipeline really uses the next map, Pipeline still references it
57
- self.extra_body_custom_metric_map = dict()
56
+ self.custom_metric_names_to_ids = dict()
58
57
  self.delayed_custom_metric_creation = False
59
58
  self.upload_custom_metrics_tasks = set()
60
59
 
@@ -198,6 +197,38 @@ class Pipeline:
198
197
  **kwargs,
199
198
  }
200
199
 
200
+ def lookup_custom_metric_ids(self):
201
+ """
202
+ The deployment's list of custom metrics is known when the pipeline is created.
203
+ The complete set of guard metrics is also known at that time.
204
+ However, the extra_body metrics needed are not known until guard_chat_wrapper
205
+ parses extra_body.
206
+ For that reason, refresh the list when a chat request arrives.
207
+ Side effect: updates self.custom_metric_names_to_ids
208
+ """
209
+ # set up DataRobot API client, only if not already set up
210
+ self.create_dr_client()
211
+
212
+ # Manually paginate; the dmm list_custom_metrics does not implement pagination
213
+ custom_metrics_list = []
214
+ offset, limit = 0, 50
215
+ while True:
216
+ response_list = self.dr_client.get(
217
+ f"deployments/{self._deployment_id}/customMetrics/?offset={offset}&limit={limit}"
218
+ ).json()
219
+ custom_metrics_list.extend(response_list["data"])
220
+ offset += response_list["count"]
221
+ if response_list["next"] is None:
222
+ break
223
+
224
+ if not custom_metrics_list:
225
+ self._logger.warning(
226
+ "lookup_custom_metric_ids: result list is empty; no custom metrics to process"
227
+ )
228
+
229
+ self.custom_metric_names_to_ids = {m["name"]: m["id"] for m in custom_metrics_list}
230
+ self._logger.debug(f"lookup_custom_metric_ids(): found {self.custom_metric_names_to_ids}")
231
+
201
232
  def create_custom_metrics(self):
202
233
  """
203
234
  Creates all the custom-metrics in the DR app for an active deployment.
@@ -268,35 +299,16 @@ class Pipeline:
268
299
  # Now query all the metrics and get their custom metric ids. Specifically,
269
300
  # required in case a metric is duplicated, in which case, we don't have its
270
301
  # id in the loop above
271
- #
272
- # Note: user-defined custom metrics (such as for extra_body params) will also
273
- # be included in the list. We will warn if an existing metric is not found
274
- # in either list, which means this chat request does not have a value for it.
275
- #
276
- # We have to go through pagination - dmm list_custom_metrics does not implement
277
- # pagination
278
- custom_metrics_list = []
279
- offset, limit = 0, 50
280
- while True:
281
- response_list = self.dr_client.get(
282
- f"deployments/{self._deployment_id}/customMetrics/?offset={offset}&limit={limit}"
283
- ).json()
284
- custom_metrics_list.extend(response_list["data"])
285
- offset += response_list["count"]
286
- if response_list["next"] is None:
287
- break
302
+ self.lookup_custom_metric_ids()
288
303
 
289
304
  # assign IDs to the "metric by name" maps so we can upload by ID later
290
- for metric in custom_metrics_list:
291
- metric_name = metric["name"]
305
+ for metric_name, metric_id in self.custom_metric_names_to_ids.items():
292
306
  if metric_name in self.custom_metric_map:
293
- self.custom_metric_map[metric_name]["id"] = metric["id"]
294
- elif metric_name in self.extra_body_custom_metric_map:
295
- self.extra_body_custom_metric_map[metric_name]["id"] = metric["id"]
307
+ self.custom_metric_map[metric_name]["id"] = metric_id
296
308
  else:
297
309
  self._logger.warning(
298
310
  f"Metric '{metric_name}' exists at DR but not in moderation; "
299
- "no value will be reported"
311
+ "no moderation value will be reported for it"
300
312
  )
301
313
  continue
302
314
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datarobot-moderations
3
- Version: 11.2.5
3
+ Version: 11.2.7
4
4
  Summary: DataRobot Monitoring and Moderation framework
5
5
  License: DataRobot Tool and Utility Agreement
6
6
  Author: DataRobot
@@ -1,8 +1,8 @@
1
1
  datarobot_dome/__init__.py,sha256=B5Rx8_CNCNsOpxBbRj27XOXCfRZmvmrAR-NzlzIKnDw,583
2
- datarobot_dome/async_http_client.py,sha256=cQFoSI2ovt0Kyk4XWQPXod5PAfA-ZPkjLYVWQZhDGDE,9809
2
+ datarobot_dome/async_http_client.py,sha256=YVkzSSP7ItOEwEskjH0MAs1kJW0jXfbvJWkF_52K7CE,9863
3
3
  datarobot_dome/chat_helper.py,sha256=BzvtUyZSZxzOqq-5a2wQKhHhr2kMlcP1MFrHaDAeD_o,9671
4
4
  datarobot_dome/constants.py,sha256=jvgpHa3Wh_nZVZmfU-6ab8FHnKNW3KxOPYIIEb_oS6U,10662
5
- datarobot_dome/drum_integration.py,sha256=DIGeiw6fwKCbgi6mb-mJdjPEB6C1uMc-pZotpTnlGZQ,45770
5
+ datarobot_dome/drum_integration.py,sha256=SLKpK-0pI9Dsi6iMBVhPv_w-SF0rqs2of-OYF3_KSPk,45983
6
6
  datarobot_dome/guard.py,sha256=xJds9hcbUaS-KD5nC1mn0GiPdBrileFUu6BuTAjDNuY,34668
7
7
  datarobot_dome/guard_executor.py,sha256=ox5_jOHcqMaxaaagIYJJHhCwEI7Wg-rUEiu5rutsfVU,35363
8
8
  datarobot_dome/guard_helpers.py,sha256=jfu8JTWCcxu4WD1MKxeP1n53DeebY3SSuP-t5sWyV1U,17187
@@ -14,11 +14,11 @@ datarobot_dome/metrics/citation_metrics.py,sha256=l2mnV1gz7nQeJ_yfaS4dcP3DFWf0p5
14
14
  datarobot_dome/metrics/factory.py,sha256=7caa8paI9LuFXDgguXdC4on28V7IwwIsKJT2Z-Aps8A,2187
15
15
  datarobot_dome/metrics/metric_scorer.py,sha256=uJ_IJRw7ZFHueg8xjsaXbt0ypO7JiydZ0WapCp96yng,2540
16
16
  datarobot_dome/pipeline/__init__.py,sha256=B5Rx8_CNCNsOpxBbRj27XOXCfRZmvmrAR-NzlzIKnDw,583
17
- datarobot_dome/pipeline/llm_pipeline.py,sha256=g8XZQjJPPs43GPqaBG4TtsCbvu1o7TWraOmzhtUGB_o,21172
18
- datarobot_dome/pipeline/pipeline.py,sha256=iDR6h25RDp4bnTW1eGWErG9W_ZXDdJ_sQGVD-tT_Ll0,18805
17
+ datarobot_dome/pipeline/llm_pipeline.py,sha256=bindyorJq9VSPZt2X8w0-3REaXui7wIA-8c-zNOIdZU,20961
18
+ datarobot_dome/pipeline/pipeline.py,sha256=vCcP8dgu4TE-af6wUt7RqD4Xg7MzfwfK8njjC4XLnIA,19249
19
19
  datarobot_dome/pipeline/vdb_pipeline.py,sha256=zt5d_41oJjdT8qOtvpgz-l5uvImwKE9f6pQsAU_TdR4,9866
20
20
  datarobot_dome/runtime.py,sha256=FD8wXOweqoQVzbZMh-mucL66xT2kGxPsJUGAcJBgwxw,1468
21
21
  datarobot_dome/streaming.py,sha256=DkvKEH0yN0aPEWMTAjMFJB3Kx4iLGdjUMQU1pAplbeg,17751
22
- datarobot_moderations-11.2.5.dist-info/METADATA,sha256=9Aj3fZnLvcgKpiAQwnvkkAzA3SxaBq2j45ctWEMCEaI,4741
23
- datarobot_moderations-11.2.5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
- datarobot_moderations-11.2.5.dist-info/RECORD,,
22
+ datarobot_moderations-11.2.7.dist-info/METADATA,sha256=RqNHfInWAiYqI346wVcJEwZj2sTvJ3qvC9FXZEEKLAE,4741
23
+ datarobot_moderations-11.2.7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
+ datarobot_moderations-11.2.7.dist-info/RECORD,,