beekeeper-monitors-watsonx 1.1.0.post1__py3-none-any.whl → 1.1.4.post1__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.
@@ -2,16 +2,17 @@ import json
2
2
  import logging
3
3
  import os
4
4
  import uuid
5
- from typing import Dict, List, Literal, Union
5
+ import warnings
6
+ from typing import Dict, List, Union
6
7
 
7
8
  import certifi
8
9
  from beekeeper.core.monitors import PromptMonitor
9
10
  from beekeeper.core.monitors.types import PayloadRecord
10
- from beekeeper.core.prompts.utils import extract_template_vars
11
+ from beekeeper.core.prompts import PromptTemplate
11
12
  from beekeeper.monitors.watsonx.supporting_classes.credentials import (
12
13
  CloudPakforDataCredentials,
13
14
  )
14
- from beekeeper.monitors.watsonx.supporting_classes.enums import Region
15
+ from beekeeper.monitors.watsonx.supporting_classes.enums import Region, TaskType
15
16
  from beekeeper.monitors.watsonx.utils.data_utils import validate_and_filter_dict
16
17
  from beekeeper.monitors.watsonx.utils.instrumentation import suppress_output
17
18
  from deprecated import deprecated
@@ -73,6 +74,8 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
73
74
 
74
75
  Example:
75
76
  ```python
77
+ from beekeeper.monitors.watsonx.supporting_classes.enums import Region
78
+
76
79
  from beekeeper.monitors.watsonx import (
77
80
  WatsonxExternalPromptMonitor,
78
81
  CloudPakforDataCredentials,
@@ -80,7 +83,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
80
83
 
81
84
  # watsonx.governance (IBM Cloud)
82
85
  wxgov_client = WatsonxExternalPromptMonitor(
83
- api_key="API_KEY", space_id="SPACE_ID"
86
+ api_key="API_KEY", space_id="SPACE_ID", region=Region.US_SOUTH
84
87
  )
85
88
 
86
89
  # watsonx.governance (CP4D)
@@ -198,6 +201,40 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
198
201
 
199
202
  return created_detached_pta.to_dict()["asset_id"]
200
203
 
204
+ def _delete_detached_prompt(self, detached_pta_id: str) -> None:
205
+ from ibm_aigov_facts_client import ( # type: ignore
206
+ AIGovFactsClient,
207
+ CloudPakforDataConfig,
208
+ )
209
+
210
+ try:
211
+ if hasattr(self, "_fact_cpd_creds") and self._fact_cpd_creds:
212
+ cpd_creds = CloudPakforDataConfig(**self._fact_cpd_creds)
213
+
214
+ aigov_client = AIGovFactsClient(
215
+ container_id=self._container_id,
216
+ container_type=self._container_type,
217
+ cloud_pak_for_data_configs=cpd_creds,
218
+ disable_tracing=True,
219
+ )
220
+
221
+ else:
222
+ aigov_client = AIGovFactsClient(
223
+ api_key=self._api_key,
224
+ container_id=self._container_id,
225
+ container_type=self._container_type,
226
+ disable_tracing=True,
227
+ region=self.region.factsheet,
228
+ )
229
+
230
+ except Exception as e:
231
+ logging.error(
232
+ f"Error connecting to IBM watsonx.governance (factsheets): {e}",
233
+ )
234
+ raise
235
+
236
+ suppress_output(aigov_client.assets.delete_prompt_asset, detached_pta_id)
237
+
201
238
  def _create_deployment_pta(self, asset_id: str, name: str, model_id: str) -> str:
202
239
  from ibm_watsonx_ai import APIClient, Credentials # type: ignore
203
240
 
@@ -235,6 +272,30 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
235
272
 
236
273
  return wml_client.deployments.get_uid(created_deployment)
237
274
 
275
+ def _delete_deployment_pta(self, deployment_id: str):
276
+ from ibm_watsonx_ai import APIClient, Credentials # type: ignore
277
+
278
+ try:
279
+ if hasattr(self, "_wml_cpd_creds") and self._wml_cpd_creds:
280
+ creds = Credentials(**self._wml_cpd_creds)
281
+
282
+ wml_client = APIClient(creds)
283
+ wml_client.set.default_space(self.space_id)
284
+
285
+ else:
286
+ creds = Credentials(
287
+ url=self.region.watsonxai,
288
+ api_key=self._api_key,
289
+ )
290
+ wml_client = APIClient(creds)
291
+ wml_client.set.default_space(self.space_id)
292
+
293
+ except Exception as e:
294
+ logging.error(f"Error connecting to IBM watsonx.ai Runtime: {e}")
295
+ raise
296
+
297
+ suppress_output(wml_client.deployments.delete, deployment_id)
298
+
238
299
  @deprecated(
239
300
  reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
240
301
  version="1.0.5",
@@ -244,13 +305,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
244
305
  self,
245
306
  name: str,
246
307
  model_id: str,
247
- task_id: Literal[
248
- "extraction",
249
- "generation",
250
- "question_answering",
251
- "retrieval_augmented_generation",
252
- "summarization",
253
- ],
308
+ task_id: Union[TaskType, str],
254
309
  detached_model_provider: str,
255
310
  description: str = "",
256
311
  model_parameters: Dict = None,
@@ -291,13 +346,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
291
346
  self,
292
347
  name: str,
293
348
  model_id: str,
294
- task_id: Literal[
295
- "extraction",
296
- "generation",
297
- "question_answering",
298
- "retrieval_augmented_generation",
299
- "summarization",
300
- ],
349
+ task_id: Union[TaskType, str],
301
350
  detached_model_provider: str,
302
351
  description: str = "",
303
352
  model_parameters: Dict = None,
@@ -333,13 +382,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
333
382
  self,
334
383
  name: str,
335
384
  model_id: str,
336
- task_id: Literal[
337
- "extraction",
338
- "generation",
339
- "question_answering",
340
- "retrieval_augmented_generation",
341
- "summarization",
342
- ],
385
+ task_id: Union[TaskType, str],
343
386
  detached_model_provider: str,
344
387
  description: str = "",
345
388
  model_parameters: Dict = None,
@@ -347,9 +390,10 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
347
390
  detached_model_url: str = None,
348
391
  detached_prompt_url: str = None,
349
392
  detached_prompt_additional_info: Dict = None,
393
+ prompt_template: Union[PromptTemplate, str] = None,
350
394
  prompt_variables: List[str] = None,
351
395
  locale: str = "en",
352
- input_text: str = None,
396
+ input_text: str = None, # DEPRECATED
353
397
  context_fields: List[str] = None,
354
398
  question_field: str = None,
355
399
  ) -> Dict:
@@ -359,7 +403,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
359
403
  Args:
360
404
  name (str): The name of the External Prompt Template Asset.
361
405
  model_id (str): The ID of the model associated with the prompt.
362
- task_id (str): The task identifier.
406
+ task_id (TaskType): The task identifier.
363
407
  detached_model_provider (str): The external model provider.
364
408
  description (str, optional): A description of the External Prompt Template Asset.
365
409
  model_parameters (Dict, optional): Model parameters and their respective values.
@@ -367,9 +411,9 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
367
411
  detached_model_url (str, optional): The URL of the external model.
368
412
  detached_prompt_url (str, optional): The URL of the external prompt.
369
413
  detached_prompt_additional_info (Dict, optional): Additional information related to the external prompt.
414
+ prompt_template (PromptTemplate, optional): The prompt template.
370
415
  prompt_variables (List[str], optional): Values for the prompt variables.
371
416
  locale (str, optional): Locale code for the input/output language. eg. "en", "pt", "es".
372
- input_text (str, optional): The input text for the prompt.
373
417
  context_fields (List[str], optional): A list of fields that will provide context to the prompt.
374
418
  Applicable only for "retrieval_augmented_generation" task type.
375
419
  question_field (str, optional): The field containing the question to be answered.
@@ -377,20 +421,39 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
377
421
 
378
422
  Example:
379
423
  ```python
424
+ from beekeeper.monitors.watsonx.supporting_classes.enums import TaskType
425
+
380
426
  wxgov_client.create_prompt_monitor(
381
427
  name="Detached prompt (model AWS Anthropic)",
382
428
  model_id="anthropic.claude-v2",
383
- task_id="retrieval_augmented_generation",
429
+ task_id=TaskType.RETRIEVAL_AUGMENTED_GENERATION,
384
430
  detached_model_provider="AWS Bedrock",
385
431
  detached_model_name="Anthropic Claude 2.0",
386
432
  detached_model_url="https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html",
387
- prompt_variables=["context1", "context2", "input_query"],
388
- input_text="Prompt text to be given",
389
- context_fields=["context1", "context2"],
433
+ prompt_template="You are a helpful AI assistant that provides clear and accurate answers. {context}. Question: {input_query}.",
434
+ prompt_variables=["context", "input_query"],
435
+ context_fields=["context"],
390
436
  question_field="input_query",
391
437
  )
392
438
  ```
393
439
  """
440
+ task_id = TaskType.from_value(task_id).value
441
+ rollback_stack = []
442
+
443
+ # DEPRECATION NOTICE
444
+ if input_text is not None:
445
+ warnings.warn(
446
+ "DEPRECATION NOTICE: `input_text` is deprecated and will be removed in a future release. "
447
+ "Use `prompt_template` instead.",
448
+ DeprecationWarning,
449
+ stacklevel=2,
450
+ )
451
+
452
+ if prompt_template is None:
453
+ prompt_template = input_text
454
+ # END DEPRECATION NOTICE
455
+ prompt_template = PromptTemplate.from_value(prompt_template)
456
+
394
457
  if (not (self.project_id or self.space_id)) or (
395
458
  self.project_id and self.space_id
396
459
  ):
@@ -399,7 +462,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
399
462
  "Both were provided: 'project_id' and 'space_id' cannot be set at the same time."
400
463
  )
401
464
 
402
- if task_id == "retrieval_augmented_generation":
465
+ if task_id == TaskType.RETRIEVAL_AUGMENTED_GENERATION.value:
403
466
  if not context_fields or not question_field:
404
467
  raise ValueError(
405
468
  "For 'retrieval_augmented_generation' task, requires non-empty 'context_fields' and 'question_field'."
@@ -413,7 +476,9 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
413
476
  prompt_metadata.pop("locale", None)
414
477
 
415
478
  # Update name of keys to aigov_facts api
416
- prompt_metadata["input"] = prompt_metadata.pop("input_text", None)
479
+ prompt_metadata["input"] = getattr(
480
+ prompt_metadata.pop("prompt_template", None), "template", None
481
+ )
417
482
  prompt_metadata["model_provider"] = prompt_metadata.pop(
418
483
  "detached_model_provider",
419
484
  None,
@@ -487,11 +552,14 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
487
552
  prompt_details,
488
553
  detached_asset_details,
489
554
  )
555
+ rollback_stack.append(lambda: self._delete_detached_prompt(detached_pta_id))
556
+
490
557
  deployment_id = None
491
558
  if self._container_type == "space":
492
559
  deployment_id = suppress_output(
493
560
  self._create_deployment_pta, detached_pta_id, name, model_id
494
561
  )
562
+ rollback_stack.append(lambda: self._delete_deployment_pta(deployment_id))
495
563
 
496
564
  monitors = {
497
565
  "generative_ai_quality": {
@@ -552,10 +620,18 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
552
620
 
553
621
  generative_ai_monitor_details = generative_ai_monitor_details.result._to_dict()
554
622
 
623
+ wos_status = generative_ai_monitor_details.get("status", {})
624
+ if wos_status.get("state") == "ERROR":
625
+ for rollback_step in reversed(rollback_stack):
626
+ rollback_step()
627
+ raise Exception(wos_status.get("failure"))
628
+
555
629
  return {
556
630
  "detached_prompt_template_asset_id": detached_pta_id,
557
631
  "deployment_id": deployment_id,
558
- "subscription_id": generative_ai_monitor_details["subscription_id"],
632
+ "subscription_id": generative_ai_monitor_details.get(
633
+ "subscription_id", None
634
+ ),
559
635
  }
560
636
 
561
637
  def store_payload_records(
@@ -671,8 +747,9 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
671
747
  """
672
748
  Stores records to the feedback logging system.
673
749
 
674
- Note:
675
- Feedback data for external prompt **must include** the model output named `generated_text`.
750
+ Info:
751
+ - Feedback data for external prompt **must include** the model output named `generated_text`.
752
+ - For prompt monitors created using Beekeeper, the label field is `reference_output`.
676
753
 
677
754
  Args:
678
755
  request_records (List[Dict]): A list of records to be logged, where each record is represented as a dictionary.
@@ -777,15 +854,17 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
777
854
  return {"status": "success"}
778
855
 
779
856
  def __call__(self, payload: PayloadRecord) -> None:
780
- if self.prompt_template:
781
- template_vars = extract_template_vars(
782
- self.prompt_template.template, payload.input_text
783
- )
784
-
785
- if not template_vars:
786
- self.store_payload_records([payload.model_dump()])
787
- else:
788
- self.store_payload_records([{**payload.model_dump(), **template_vars}])
857
+ self.store_payload_records(
858
+ [
859
+ {
860
+ **payload.prompt_variable_values,
861
+ **payload.model_dump(
862
+ exclude_none=True,
863
+ exclude={"system_prompt", "prompt_variable_values"},
864
+ ),
865
+ }
866
+ ]
867
+ )
789
868
 
790
869
 
791
870
  class WatsonxPromptMonitor(PromptMonitor):
@@ -808,13 +887,17 @@ class WatsonxPromptMonitor(PromptMonitor):
808
887
 
809
888
  Example:
810
889
  ```python
890
+ from beekeeper.monitors.watsonx.supporting_classes.enums import Region
891
+
811
892
  from beekeeper.monitors.watsonx import (
812
893
  WatsonxPromptMonitor,
813
894
  CloudPakforDataCredentials,
814
895
  )
815
896
 
816
897
  # watsonx.governance (IBM Cloud)
817
- wxgov_client = WatsonxPromptMonitor(api_key="API_KEY", space_id="SPACE_ID")
898
+ wxgov_client = WatsonxPromptMonitor(
899
+ api_key="API_KEY", space_id="SPACE_ID", region=Region.US_SOUTH
900
+ )
818
901
 
819
902
  # watsonx.governance (CP4D)
820
903
  cpd_creds = CloudPakforDataCredentials(
@@ -927,6 +1010,40 @@ class WatsonxPromptMonitor(PromptMonitor):
927
1010
 
928
1011
  return created_pta.to_dict()["asset_id"]
929
1012
 
1013
+ def _delete_prompt(self, pta_id: str) -> None:
1014
+ from ibm_aigov_facts_client import ( # type: ignore
1015
+ AIGovFactsClient,
1016
+ CloudPakforDataConfig,
1017
+ )
1018
+
1019
+ try:
1020
+ if hasattr(self, "_fact_cpd_creds") and self._fact_cpd_creds:
1021
+ cpd_creds = CloudPakforDataConfig(**self._fact_cpd_creds)
1022
+
1023
+ aigov_client = AIGovFactsClient(
1024
+ container_id=self._container_id,
1025
+ container_type=self._container_type,
1026
+ cloud_pak_for_data_configs=cpd_creds,
1027
+ disable_tracing=True,
1028
+ )
1029
+
1030
+ else:
1031
+ aigov_client = AIGovFactsClient(
1032
+ api_key=self._api_key,
1033
+ container_id=self._container_id,
1034
+ container_type=self._container_type,
1035
+ disable_tracing=True,
1036
+ region=self.region.factsheet,
1037
+ )
1038
+
1039
+ except Exception as e:
1040
+ logging.error(
1041
+ f"Error connecting to IBM watsonx.governance (factsheets): {e}",
1042
+ )
1043
+ raise
1044
+
1045
+ suppress_output(aigov_client.assets.delete_prompt_asset, pta_id)
1046
+
930
1047
  def _create_deployment_pta(self, asset_id: str, name: str, model_id: str) -> str:
931
1048
  from ibm_watsonx_ai import APIClient, Credentials # type: ignore
932
1049
 
@@ -965,6 +1082,30 @@ class WatsonxPromptMonitor(PromptMonitor):
965
1082
 
966
1083
  return wml_client.deployments.get_uid(created_deployment)
967
1084
 
1085
+ def _delete_deployment_pta(self, deployment_id: str):
1086
+ from ibm_watsonx_ai import APIClient, Credentials # type: ignore
1087
+
1088
+ try:
1089
+ if hasattr(self, "_wml_cpd_creds") and self._wml_cpd_creds:
1090
+ creds = Credentials(**self._wml_cpd_creds)
1091
+
1092
+ wml_client = APIClient(creds)
1093
+ wml_client.set.default_space(self.space_id)
1094
+
1095
+ else:
1096
+ creds = Credentials(
1097
+ url=self.region.watsonxai,
1098
+ api_key=self._api_key,
1099
+ )
1100
+ wml_client = APIClient(creds)
1101
+ wml_client.set.default_space(self.space_id)
1102
+
1103
+ except Exception as e:
1104
+ logging.error(f"Error connecting to IBM watsonx.ai Runtime: {e}")
1105
+ raise
1106
+
1107
+ suppress_output(wml_client.deployments.delete, deployment_id)
1108
+
968
1109
  @deprecated(
969
1110
  reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
970
1111
  version="1.0.5",
@@ -974,13 +1115,7 @@ class WatsonxPromptMonitor(PromptMonitor):
974
1115
  self,
975
1116
  name: str,
976
1117
  model_id: str,
977
- task_id: Literal[
978
- "extraction",
979
- "generation",
980
- "question_answering",
981
- "retrieval_augmented_generation",
982
- "summarization",
983
- ],
1118
+ task_id: Union[TaskType, str],
984
1119
  description: str = "",
985
1120
  model_parameters: Dict = None,
986
1121
  prompt_variables: List[str] = None,
@@ -1011,13 +1146,7 @@ class WatsonxPromptMonitor(PromptMonitor):
1011
1146
  self,
1012
1147
  name: str,
1013
1148
  model_id: str,
1014
- task_id: Literal[
1015
- "extraction",
1016
- "generation",
1017
- "question_answering",
1018
- "retrieval_augmented_generation",
1019
- "summarization",
1020
- ],
1149
+ task_id: Union[TaskType, str],
1021
1150
  description: str = "",
1022
1151
  model_parameters: Dict = None,
1023
1152
  prompt_variables: List[str] = None,
@@ -1043,18 +1172,13 @@ class WatsonxPromptMonitor(PromptMonitor):
1043
1172
  self,
1044
1173
  name: str,
1045
1174
  model_id: str,
1046
- task_id: Literal[
1047
- "extraction",
1048
- "generation",
1049
- "question_answering",
1050
- "retrieval_augmented_generation",
1051
- "summarization",
1052
- ],
1175
+ task_id: Union[TaskType, str],
1053
1176
  description: str = "",
1054
1177
  model_parameters: Dict = None,
1178
+ prompt_template: Union[PromptTemplate, str] = None,
1055
1179
  prompt_variables: List[str] = None,
1056
1180
  locale: str = "en",
1057
- input_text: str = None,
1181
+ input_text: str = None, # DEPRECATED
1058
1182
  context_fields: List[str] = None,
1059
1183
  question_field: str = None,
1060
1184
  ) -> Dict:
@@ -1064,12 +1188,12 @@ class WatsonxPromptMonitor(PromptMonitor):
1064
1188
  Args:
1065
1189
  name (str): The name of the Prompt Template Asset.
1066
1190
  model_id (str): The ID of the model associated with the prompt.
1067
- task_id (str): The task identifier.
1191
+ task_id (TaskType): The task identifier.
1068
1192
  description (str, optional): A description of the Prompt Template Asset.
1069
1193
  model_parameters (Dict, optional): A dictionary of model parameters and their respective values.
1194
+ prompt_template (PromptTemplate, optional): The prompt template.
1070
1195
  prompt_variables (List[str], optional): A list of values for prompt input variables.
1071
1196
  locale (str, optional): Locale code for the input/output language. eg. "en", "pt", "es".
1072
- input_text (str, optional): The input text for the prompt.
1073
1197
  context_fields (List[str], optional): A list of fields that will provide context to the prompt.
1074
1198
  Applicable only for the `retrieval_augmented_generation` task type.
1075
1199
  question_field (str, optional): The field containing the question to be answered.
@@ -1077,17 +1201,36 @@ class WatsonxPromptMonitor(PromptMonitor):
1077
1201
 
1078
1202
  Example:
1079
1203
  ```python
1204
+ from beekeeper.monitors.watsonx.supporting_classes.enums import TaskType
1205
+
1080
1206
  wxgov_client.create_prompt_monitor(
1081
1207
  name="IBM prompt template",
1082
1208
  model_id="ibm/granite-3-2b-instruct",
1083
- task_id="retrieval_augmented_generation",
1084
- prompt_variables=["context1", "context2", "input_query"],
1085
- input_text="Prompt text to be given",
1086
- context_fields=["context1", "context2"],
1209
+ task_id=TaskType.RETRIEVAL_AUGMENTED_GENERATION,
1210
+ prompt_template="You are a helpful AI assistant that provides clear and accurate answers. {context}. Question: {input_query}.",
1211
+ prompt_variables=["context", "input_query"],
1212
+ context_fields=["context"],
1087
1213
  question_field="input_query",
1088
1214
  )
1089
1215
  ```
1090
1216
  """
1217
+ task_id = TaskType.from_value(task_id).value
1218
+ rollback_stack = []
1219
+
1220
+ # DEPRECATION NOTICE
1221
+ if input_text is not None:
1222
+ warnings.warn(
1223
+ "DEPRECATION NOTICE: `input_text` is deprecated and will be removed in a future release. "
1224
+ "Use `prompt_template` instead.",
1225
+ DeprecationWarning,
1226
+ stacklevel=2,
1227
+ )
1228
+
1229
+ if prompt_template is None:
1230
+ prompt_template = input_text
1231
+ # END DEPRECATION NOTICE
1232
+ prompt_template = PromptTemplate.from_value(prompt_template)
1233
+
1091
1234
  if (not (self.project_id or self.space_id)) or (
1092
1235
  self.project_id and self.space_id
1093
1236
  ):
@@ -1096,7 +1239,7 @@ class WatsonxPromptMonitor(PromptMonitor):
1096
1239
  "Both were provided: 'project_id' and 'space_id' cannot be set at the same time."
1097
1240
  )
1098
1241
 
1099
- if task_id == "retrieval_augmented_generation":
1242
+ if task_id == TaskType.RETRIEVAL_AUGMENTED_GENERATION.value:
1100
1243
  if not context_fields or not question_field:
1101
1244
  raise ValueError(
1102
1245
  "For 'retrieval_augmented_generation' task, requires non-empty 'context_fields' and 'question_field'."
@@ -1110,7 +1253,9 @@ class WatsonxPromptMonitor(PromptMonitor):
1110
1253
  prompt_metadata.pop("locale", None)
1111
1254
 
1112
1255
  # Update name of keys to aigov_facts api
1113
- prompt_metadata["input"] = prompt_metadata.pop("input_text", None)
1256
+ prompt_metadata["input"] = getattr(
1257
+ prompt_metadata.pop("prompt_template", None), "template", None
1258
+ )
1114
1259
 
1115
1260
  # Update list of vars to dict
1116
1261
  prompt_metadata["prompt_variables"] = Dict.fromkeys(
@@ -1165,11 +1310,14 @@ class WatsonxPromptMonitor(PromptMonitor):
1165
1310
  pta_id = suppress_output(
1166
1311
  self._create_prompt_template, prompt_details, asset_details
1167
1312
  )
1313
+ rollback_stack.append(lambda: self._delete_detached_prompt(pta_id))
1314
+
1168
1315
  deployment_id = None
1169
1316
  if self._container_type == "space":
1170
1317
  deployment_id = suppress_output(
1171
1318
  self._create_deployment_pta, pta_id, name, model_id
1172
1319
  )
1320
+ rollback_stack.append(lambda: self._delete_deployment_pta(deployment_id))
1173
1321
 
1174
1322
  monitors = {
1175
1323
  "generative_ai_quality": {
@@ -1230,10 +1378,18 @@ class WatsonxPromptMonitor(PromptMonitor):
1230
1378
 
1231
1379
  generative_ai_monitor_details = generative_ai_monitor_details._to_dict()
1232
1380
 
1381
+ wos_status = generative_ai_monitor_details.get("status", {})
1382
+ if wos_status.get("state") == "ERROR":
1383
+ for rollback_step in reversed(rollback_stack):
1384
+ rollback_step()
1385
+ raise Exception(wos_status.get("failure"))
1386
+
1233
1387
  return {
1234
1388
  "prompt_template_asset_id": pta_id,
1235
1389
  "deployment_id": deployment_id,
1236
- "subscription_id": generative_ai_monitor_details["subscription_id"],
1390
+ "subscription_id": generative_ai_monitor_details.get(
1391
+ "subscription_id", None
1392
+ ),
1237
1393
  }
1238
1394
 
1239
1395
  def store_payload_records(
@@ -1350,6 +1506,9 @@ class WatsonxPromptMonitor(PromptMonitor):
1350
1506
  """
1351
1507
  Stores records to the feedback logging system.
1352
1508
 
1509
+ Info:
1510
+ - For prompt monitors created using Beekeeper, the label field is `reference_output`.
1511
+
1353
1512
  Args:
1354
1513
  request_records (List[Dict]): A list of records to be logged, where each record is represented as a dictionary.
1355
1514
  subscription_id (str, optional): The subscription ID associated with the records being logged.
@@ -1453,12 +1612,14 @@ class WatsonxPromptMonitor(PromptMonitor):
1453
1612
  return {"status": "success"}
1454
1613
 
1455
1614
  def __call__(self, payload: PayloadRecord) -> None:
1456
- if self.prompt_template:
1457
- template_vars = extract_template_vars(
1458
- self.prompt_template.template, payload.input_text
1459
- )
1460
-
1461
- if not template_vars:
1462
- self.store_payload_records([payload.model_dump()])
1463
- else:
1464
- self.store_payload_records([{**payload.model_dump(), **template_vars}])
1615
+ self.store_payload_records(
1616
+ [
1617
+ {
1618
+ **payload.prompt_variable_values,
1619
+ **payload.model_dump(
1620
+ exclude_none=True,
1621
+ exclude={"system_prompt", "prompt_variable_values"},
1622
+ ),
1623
+ }
1624
+ ]
1625
+ )
@@ -7,7 +7,7 @@ from beekeeper.monitors.watsonx.supporting_classes.credentials import (
7
7
  CloudPakforDataCredentials,
8
8
  IntegratedSystemCredentials,
9
9
  )
10
- from beekeeper.monitors.watsonx.supporting_classes.enums import Region
10
+ from beekeeper.monitors.watsonx.supporting_classes.enums import DataSetType, Region
11
11
  from beekeeper.monitors.watsonx.supporting_classes.metric import (
12
12
  WatsonxLocalMetric,
13
13
  WatsonxMetric,
@@ -29,13 +29,17 @@ class WatsonxCustomMetricsManager:
29
29
 
30
30
  Example:
31
31
  ```python
32
+ from beekeeper.monitors.watsonx.supporting_classes.enums import Region
33
+
32
34
  from beekeeper.monitors.watsonx import (
33
35
  WatsonxCustomMetricsManager,
34
36
  CloudPakforDataCredentials,
35
37
  )
36
38
 
37
39
  # watsonx.governance (IBM Cloud)
38
- wxgov_client = WatsonxCustomMetricsManager(api_key="API_KEY")
40
+ wxgov_client = WatsonxCustomMetricsManager(
41
+ api_key="API_KEY", region=Region.US_SOUTH
42
+ )
39
43
 
40
44
  # watsonx.governance (CP4D)
41
45
  cpd_creds = CloudPakforDataCredentials(
@@ -449,6 +453,12 @@ class WatsonxCustomMetricsManager:
449
453
  existing_instance_id,
450
454
  )
451
455
 
456
+ self._wos_client.custom_monitor.create_custom_dataset(
457
+ data_mart_id=data_mart_id,
458
+ subscription_id=subscription_id,
459
+ custom_monitor_id=monitor_definition_id,
460
+ )
461
+
452
462
  return monitor_instance_details
453
463
 
454
464
  @deprecated(
@@ -484,7 +494,7 @@ class WatsonxCustomMetricsManager:
484
494
 
485
495
  Example:
486
496
  ```python
487
- wxgov_client.put_metrics(
497
+ wxgov_client.store_metric_data(
488
498
  monitor_instance_id="01966801-f9ee-7248-a706-41de00a8a998",
489
499
  run_id="RUN_ID",
490
500
  request_records={"context_quality": 0.914, "sensitivity": 0.85},
@@ -527,6 +537,49 @@ class WatsonxCustomMetricsManager:
527
537
  json_patch_operation=patch_payload,
528
538
  ).result
529
539
 
540
+ def store_record_metric_data(
541
+ self,
542
+ custom_data_set_id: str,
543
+ computed_on: Union[DataSetType, str],
544
+ run_id: str,
545
+ request_records: List[Dict],
546
+ ):
547
+ """
548
+ Stores computed metrics data to the specified transaction record.
549
+
550
+ Args:
551
+ custom_data_set_id (str): The ID of the custom metric data set.
552
+ computed_on (DataSetType): The dataset on which the metric was calculated (e.g., payload or feedback).
553
+ run_id (str): The ID of the monitor run that generated the metrics.
554
+ request_records (List[Dict]): A list of dictionaries containing the records to be stored.
555
+
556
+ Example:
557
+ ```python
558
+ wxgov_client.store_record_metric_data(
559
+ reference_record_id="0196ad39-1b75-7e77-bddb-cc5393d575c2",
560
+ run_id="RUN_ID",
561
+ request_records=[
562
+ {
563
+ "reference_record_id": "304a9270-44a1-4c4d-bfd4-f756541011f8",
564
+ "record_timestamp": "2025-12-09T00:00:00Z",
565
+ "context_quality": 0.786,
566
+ "pii": 0.05,
567
+ }
568
+ ],
569
+ )
570
+ ```
571
+ """
572
+ computed_on = DataSetType.from_value(computed_on).value
573
+
574
+ for record in request_records:
575
+ record["run_id"] = run_id
576
+ record["computed_on"] = computed_on
577
+
578
+ return self._wos_client.data_sets.store_records(
579
+ data_set_id=custom_data_set_id,
580
+ request_body=request_records,
581
+ ).result
582
+
530
583
  # ===== Local Custom Metrics =====
531
584
  @deprecated(
532
585
  reason="'add_local_metric_definition()' is deprecated and will be removed in a future version. Use 'create_local_metric_definition()' from 'beekeeper-monitors-watsonx' instead.",
@@ -545,33 +598,17 @@ class WatsonxCustomMetricsManager:
545
598
  subscription_id=subscription_id,
546
599
  )
547
600
 
601
+ @deprecated(
602
+ reason="'create_local_metric_definition()' is deprecated and will be removed in a future version. Use OOB record level metrics instead.",
603
+ version="1.1.3",
604
+ action="always",
605
+ )
548
606
  def create_local_metric_definition(
549
607
  self,
550
608
  name: str,
551
609
  metrics: List[WatsonxLocalMetric],
552
610
  subscription_id: str,
553
611
  ) -> str:
554
- """
555
- Creates a custom metric definition to compute metrics at the local (transaction) level for IBM watsonx.governance.
556
-
557
- Args:
558
- name (str): The name of the custom transaction metric group.
559
- metrics (List[WatsonxLocalMetric]): A list of metrics to be monitored at the local (transaction) level.
560
- subscription_id (str): The IBM watsonx.governance subscription ID associated with the metric definition.
561
-
562
- Example:
563
- ```python
564
- from beekeeper.monitors.watsonx import WatsonxLocalMetric
565
-
566
- wxgov_client.create_local_metric_definition(
567
- name="Custom LLM Local Metric",
568
- subscription_id="019674ca-0c38-745f-8e9b-58546e95174e",
569
- metrics=[
570
- WatsonxLocalMetric(name="context_quality", data_type="double")
571
- ],
572
- )
573
- ```
574
- """
575
612
  from ibm_watson_openscale.base_classes.watson_open_scale_v2 import (
576
613
  LocationTableName,
577
614
  SparkStruct,
@@ -632,55 +669,30 @@ class WatsonxCustomMetricsManager:
632
669
  request_records=request_records,
633
670
  )
634
671
 
672
+ @deprecated(
673
+ reason="'store_local_metric_data()' is deprecated and will be removed in a future version. Use OOB record level metrics instead.",
674
+ version="1.1.3",
675
+ action="always",
676
+ )
635
677
  def store_local_metric_data(
636
678
  self,
637
679
  metric_instance_id: str,
638
680
  request_records: List[Dict],
639
681
  ):
640
- """
641
- Stores computed metrics data to the specified transaction record.
642
-
643
- Args:
644
- metric_instance_id (str): The unique ID of the custom transaction metric.
645
- request_records (List[Dict]): A list of dictionaries containing the records to be stored.
646
-
647
- Example:
648
- ```python
649
- wxgov_client.store_local_metric_data(
650
- metric_instance_id="0196ad39-1b75-7e77-bddb-cc5393d575c2",
651
- request_records=[
652
- {
653
- "scoring_id": "304a9270-44a1-4c4d-bfd4-f756541011f8",
654
- "run_id": "RUN_ID",
655
- "computed_on": "payload",
656
- "context_quality": 0.786,
657
- }
658
- ],
659
- )
660
- ```
661
- """
662
682
  return self._wos_client.data_sets.store_records(
663
683
  data_set_id=metric_instance_id,
664
684
  request_body=request_records,
665
685
  ).result
666
686
 
687
+ @deprecated(
688
+ reason="'list_local_metrics()' is deprecated and will be removed in a future version. Use OOB record level metrics instead.",
689
+ version="1.1.3",
690
+ action="always",
691
+ )
667
692
  def list_local_metrics(
668
693
  self,
669
694
  metric_instance_id: str,
670
695
  ):
671
- """
672
- Lists records from a custom local metric definition.
673
-
674
- Args:
675
- metric_instance_id (str): The unique ID of the custom transaction metric.
676
-
677
- Example:
678
- ```python
679
- wxgov_client.list_local_metrics(
680
- metric_instance_id="0196ad47-c505-73c0-9d7b-91c082b697e3"
681
- )
682
- ```
683
- """
684
696
  return self._get_dataset_data(metric_instance_id)
685
697
 
686
698
 
@@ -4,7 +4,7 @@ _REGION_DATA = {
4
4
  "us-south": {
5
5
  "watsonxai": "https://us-south.ml.cloud.ibm.com",
6
6
  "openscale": "https://api.aiopenscale.cloud.ibm.com",
7
- "factsheet": None,
7
+ "factsheet": "us-south",
8
8
  },
9
9
  "eu-de": {
10
10
  "watsonxai": "https://eu-de.ml.cloud.ibm.com",
@@ -49,7 +49,7 @@ class Region(str, Enum):
49
49
  return _REGION_DATA[self.value]["factsheet"]
50
50
 
51
51
  @classmethod
52
- def from_value(cls, value):
52
+ def from_value(cls, value: str) -> "Region":
53
53
  if value is None:
54
54
  return cls.US_SOUTH
55
55
 
@@ -58,7 +58,7 @@ class Region(str, Enum):
58
58
 
59
59
  if isinstance(value, str):
60
60
  try:
61
- return cls(value)
61
+ return cls(value.lower())
62
62
  except ValueError:
63
63
  raise ValueError(
64
64
  "Invalid value for parameter 'region'. Received: '{}'. Valid values are: {}.".format(
@@ -69,3 +69,77 @@ class Region(str, Enum):
69
69
  raise TypeError(
70
70
  f"Invalid type for parameter 'region'. Expected str or Region, but received {type(value).__name__}."
71
71
  )
72
+
73
+
74
+ class TaskType(Enum):
75
+ """
76
+ Supported IBM watsonx.governance tasks.
77
+
78
+ Attributes:
79
+ QUESTION_ANSWERING (str): "question_answering"
80
+ SUMMARIZATION (str): "summarization"
81
+ RETRIEVAL_AUGMENTED_GENERATION (str): "retrieval_augmented_generation"
82
+ CLASSIFICATION (str): "classification"
83
+ GENERATION (str): "generation"
84
+ CODE (str): "code"
85
+ EXTRACTION (str): "extraction"
86
+ """
87
+
88
+ QUESTION_ANSWERING = "question_answering"
89
+ SUMMARIZATION = "summarization"
90
+ RETRIEVAL_AUGMENTED_GENERATION = "retrieval_augmented_generation"
91
+ CLASSIFICATION = "classification"
92
+ GENERATION = "generation"
93
+ CODE = "code"
94
+ EXTRACTION = "extraction"
95
+
96
+ @classmethod
97
+ def from_value(cls, value: str) -> "TaskType":
98
+ if isinstance(value, cls):
99
+ return value
100
+
101
+ if isinstance(value, str):
102
+ try:
103
+ return cls(value.lower())
104
+ except ValueError:
105
+ raise ValueError(
106
+ "Invalid value for parameter 'task_id'. Received: '{}'. Valid values are: {}.".format(
107
+ value, [item.value for item in TaskType]
108
+ )
109
+ )
110
+
111
+ raise TypeError(
112
+ f"Invalid type for parameter 'task_id'. Expected str or TaskType, but received {type(value).__name__}."
113
+ )
114
+
115
+
116
+ class DataSetType(Enum):
117
+ """
118
+ Supported IBM watsonx.governance tasks.
119
+
120
+ Attributes:
121
+ PAYLOAD (str): "payload"
122
+ FEEDBACK (str): "feedback"
123
+ """
124
+
125
+ PAYLOAD = "payload"
126
+ FEEDBACK = "feedback"
127
+
128
+ @classmethod
129
+ def from_value(cls, value: str) -> "DataSetType":
130
+ if isinstance(value, cls):
131
+ return value
132
+
133
+ if isinstance(value, str):
134
+ try:
135
+ return cls(value.lower())
136
+ except ValueError:
137
+ raise ValueError(
138
+ "Invalid value. Received: '{}'. Valid values are: {}.".format(
139
+ value, [item.value for item in DataSetType]
140
+ )
141
+ )
142
+
143
+ raise TypeError(
144
+ f"Invalid type. Expected str or DataSetType, but received {type(value).__name__}."
145
+ )
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: beekeeper-monitors-watsonx
3
+ Version: 1.1.4.post1
4
+ Summary: beekeeper monitors watsonx extension
5
+ Author-email: Leonardo Furnielis <leonardofurnielis@outlook.com>
6
+ License: Apache-2.0
7
+ Requires-Python: <3.14,>=3.11
8
+ Requires-Dist: beekeeper-core<2.0.0,>=1.0.12
9
+ Requires-Dist: certifi<2026.0.0,>=2025.11.12
10
+ Requires-Dist: ibm-aigov-facts-client<1.0.200,>=1.0.104
11
+ Requires-Dist: ibm-watson-openscale<3.2.0,>=3.1.3
12
+ Requires-Dist: ibm-watsonx-ai<2.0.0,>=1.4.11
13
+ Requires-Dist: ipython==9.8.0
14
+ Provides-Extra: dev
15
+ Requires-Dist: ruff>=0.14.9; extra == 'dev'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Beekeeper monitors extension - watsonx
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install beekeeper-monitors-watsonx
24
+ ```
@@ -1,12 +1,12 @@
1
1
  beekeeper/monitors/watsonx/__init__.py,sha256=iJv6D68IT00ZC40TNSVYtqyFTen9sWoDqUtxvVVJjOE,789
2
- beekeeper/monitors/watsonx/base.py,sha256=jtgUGIWKWGQDsdEbeHm4iRmXp2-aErj97IKH0P3_Ayw,55775
3
- beekeeper/monitors/watsonx/custom_metric.py,sha256=QCkNDNLK_IehZIdXyczDM7RuDdyvCJN2IaTc-xsm-Lk,23827
2
+ beekeeper/monitors/watsonx/base.py,sha256=04vIs4Da7R8DxERXWEg56ojYqq6qJ3XMrh8KHXSQZ0o,62156
3
+ beekeeper/monitors/watsonx/custom_metric.py,sha256=OVeTxDzk09bqagSQybgce76eAb_jcQovKIZHSBjHE1Y,24316
4
4
  beekeeper/monitors/watsonx/supporting_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  beekeeper/monitors/watsonx/supporting_classes/credentials.py,sha256=x4rYoOFvx0pWDhFZfuy6fM0rj7JCivaSYn_jYFXlV8U,5190
6
- beekeeper/monitors/watsonx/supporting_classes/enums.py,sha256=7HkSrjU7D8pFPCRdYk_1oE27r_sZ_nIL6cuShIdtmR8,1895
6
+ beekeeper/monitors/watsonx/supporting_classes/enums.py,sha256=6Xx0maNAVnotxYdALxFaeiagDzxbhKSluxsJHiWMPEg,4117
7
7
  beekeeper/monitors/watsonx/supporting_classes/metric.py,sha256=iERXRi0iBFHITFaLtonoV97nNUCPXbieD7Z1wX6bvX8,3370
8
8
  beekeeper/monitors/watsonx/utils/data_utils.py,sha256=qBLYtHGY0MJ0JJ8BpFDT2YIjA3QOYJQNemLvpA3DMz0,1252
9
9
  beekeeper/monitors/watsonx/utils/instrumentation.py,sha256=ztgR1kZ9h-JvASzRA47AYHdc-Isv33EQum9XBLg-dQk,525
10
- beekeeper_monitors_watsonx-1.1.0.post1.dist-info/METADATA,sha256=CeEXAqhnYUi0qbbhx6EfkB3-tlkAbFNLMde-t0Bp6L4,722
11
- beekeeper_monitors_watsonx-1.1.0.post1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- beekeeper_monitors_watsonx-1.1.0.post1.dist-info/RECORD,,
10
+ beekeeper_monitors_watsonx-1.1.4.post1.dist-info/METADATA,sha256=_nyLiWQU16XDCNJmWV_MQ-SfNTaXrgy6224LbfFJ37c,724
11
+ beekeeper_monitors_watsonx-1.1.4.post1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ beekeeper_monitors_watsonx-1.1.4.post1.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: beekeeper-monitors-watsonx
3
- Version: 1.1.0.post1
4
- Summary: beekeeper monitors watsonx extension
5
- Author-email: Leonardo Furnielis <leonardofurnielis@outlook.com>
6
- License: Apache-2.0
7
- Requires-Python: <4.0,>=3.10
8
- Requires-Dist: beekeeper-core<2.0.0,>=1.0.5
9
- Requires-Dist: certifi<2026.0.0,>=2025.4.26
10
- Requires-Dist: ibm-aigov-facts-client<1.0.97,>=1.0.96
11
- Requires-Dist: ibm-watson-openscale<3.1.0,>=3.0.49
12
- Requires-Dist: ibm-watsonx-ai<2.0.0,>=1.3.26
13
- Requires-Dist: ipython==8.37.0
14
- Provides-Extra: dev
15
- Requires-Dist: ruff>=0.11.13; extra == 'dev'
16
- Description-Content-Type: text/markdown
17
-
18
- # Beekeeper monitors extension - watsonx
19
-
20
- ## Installation
21
-
22
- ```bash
23
- pip install beekeeper-monitors-watsonx
24
- ```