beekeeper-monitors-watsonx 1.0.5.post1__py3-none-any.whl → 1.0.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.
- beekeeper/monitors/watsonx/__init__.py +11 -3
- beekeeper/monitors/watsonx/base.py +128 -889
- beekeeper/monitors/watsonx/custom_metric.py +645 -0
- beekeeper/monitors/watsonx/supporting_classes/__init__.py +0 -0
- beekeeper/monitors/watsonx/supporting_classes/credentials.py +137 -0
- beekeeper/monitors/watsonx/supporting_classes/enums.py +71 -0
- beekeeper/monitors/watsonx/supporting_classes/metric.py +109 -0
- beekeeper/monitors/watsonx/utils/data_utils.py +31 -0
- {beekeeper_monitors_watsonx-1.0.5.post1.dist-info → beekeeper_monitors_watsonx-1.0.7.dist-info}/METADATA +1 -1
- beekeeper_monitors_watsonx-1.0.7.dist-info/RECORD +12 -0
- beekeeper_monitors_watsonx-1.0.5.post1.dist-info/RECORD +0 -6
- /beekeeper/monitors/watsonx/{instrumentation.py → utils/instrumentation.py} +0 -0
- {beekeeper_monitors_watsonx-1.0.5.post1.dist-info → beekeeper_monitors_watsonx-1.0.7.dist-info}/WHEEL +0 -0
|
@@ -1,64 +1,25 @@
|
|
|
1
|
-
import datetime
|
|
2
1
|
import json
|
|
3
2
|
import logging
|
|
4
3
|
import os
|
|
5
4
|
import uuid
|
|
6
|
-
from typing import
|
|
5
|
+
from typing import Dict, List, Literal, Union
|
|
7
6
|
|
|
8
7
|
import certifi
|
|
9
8
|
from beekeeper.core.monitors import PromptMonitor
|
|
10
9
|
from beekeeper.core.monitors.types import PayloadRecord
|
|
11
10
|
from beekeeper.core.prompts.utils import extract_template_vars
|
|
12
|
-
from beekeeper.monitors.watsonx.
|
|
11
|
+
from beekeeper.monitors.watsonx.supporting_classes.credentials import (
|
|
12
|
+
CloudPakforDataCredentials,
|
|
13
|
+
)
|
|
14
|
+
from beekeeper.monitors.watsonx.supporting_classes.enums import Region
|
|
15
|
+
from beekeeper.monitors.watsonx.utils.data_utils import validate_and_filter_dict
|
|
16
|
+
from beekeeper.monitors.watsonx.utils.instrumentation import suppress_output
|
|
13
17
|
from deprecated import deprecated
|
|
14
|
-
from pydantic.v1 import BaseModel
|
|
15
18
|
|
|
16
19
|
os.environ["REQUESTS_CA_BUNDLE"] = certifi.where()
|
|
17
20
|
logging.getLogger("ibm_watsonx_ai.client").setLevel(logging.ERROR)
|
|
18
21
|
logging.getLogger("ibm_watsonx_ai.wml_resource").setLevel(logging.ERROR)
|
|
19
22
|
|
|
20
|
-
REGIONS_URL = {
|
|
21
|
-
"us-south": {
|
|
22
|
-
"wml": "https://us-south.ml.cloud.ibm.com",
|
|
23
|
-
"wos": "https://api.aiopenscale.cloud.ibm.com",
|
|
24
|
-
"factsheet": None,
|
|
25
|
-
},
|
|
26
|
-
"eu-de": {
|
|
27
|
-
"wml": "https://eu-de.ml.cloud.ibm.com",
|
|
28
|
-
"wos": "https://eu-de.api.aiopenscale.cloud.ibm.com",
|
|
29
|
-
"factsheet": "frankfurt",
|
|
30
|
-
},
|
|
31
|
-
"au-syd": {
|
|
32
|
-
"wml": "https://au-syd.ml.cloud.ibm.com",
|
|
33
|
-
"wos": "https://au-syd.api.aiopenscale.cloud.ibm.com",
|
|
34
|
-
"factsheet": "sydney",
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _filter_dict(original_dict: Dict, optional_keys: List, required_keys: List = []):
|
|
40
|
-
"""
|
|
41
|
-
Filters a dictionary to keep only the specified keys and checks for required keys.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
original_dict (Dict): The original dictionary.
|
|
45
|
-
optional_keys (list): A list of keys to retain.
|
|
46
|
-
required_keys (list, optional): A list of keys that must be present in the dictionary. Defaults to None.
|
|
47
|
-
"""
|
|
48
|
-
# Ensure all required keys are in the source dict
|
|
49
|
-
missing_keys = [key for key in required_keys if key not in original_dict]
|
|
50
|
-
if missing_keys:
|
|
51
|
-
raise KeyError(f"Missing required parameter: {missing_keys}")
|
|
52
|
-
|
|
53
|
-
all_keys_to_keep = set(required_keys + optional_keys)
|
|
54
|
-
|
|
55
|
-
# Create a new dictionary with only the key-value pairs where the key is in 'keys' and value is not None
|
|
56
|
-
return {
|
|
57
|
-
key: original_dict[key]
|
|
58
|
-
for key in all_keys_to_keep
|
|
59
|
-
if key in original_dict and original_dict[key] is not None
|
|
60
|
-
}
|
|
61
|
-
|
|
62
23
|
|
|
63
24
|
def _convert_payload_format(
|
|
64
25
|
records: List[Dict],
|
|
@@ -93,145 +54,9 @@ def _convert_payload_format(
|
|
|
93
54
|
return payload_data
|
|
94
55
|
|
|
95
56
|
|
|
96
|
-
# ===== Credentials Classes =====
|
|
97
|
-
class CloudPakforDataCredentials(BaseModel):
|
|
98
|
-
"""
|
|
99
|
-
Encapsulates the credentials required for IBM Cloud Pak for Data.
|
|
100
|
-
|
|
101
|
-
Attributes:
|
|
102
|
-
url (str): The host URL of the Cloud Pak for Data environment.
|
|
103
|
-
api_key (str, optional): The API key for the environment, if IAM is enabled.
|
|
104
|
-
username (str, optional): The username for the environment.
|
|
105
|
-
password (str, optional): The password for the environment.
|
|
106
|
-
bedrock_url (str, optional): The Bedrock URL. Required only when IAM integration is enabled on CP4D 4.0.x clusters.
|
|
107
|
-
instance_id (str, optional): The instance ID.
|
|
108
|
-
version (str, optional): The version of Cloud Pak for Data.
|
|
109
|
-
disable_ssl_verification (bool, optional): Indicates whether to disable SSL certificate verification.
|
|
110
|
-
Defaults to `True`.
|
|
111
|
-
"""
|
|
112
|
-
|
|
113
|
-
url: str
|
|
114
|
-
api_key: Optional[str] = None
|
|
115
|
-
username: Optional[str] = None
|
|
116
|
-
password: Optional[str] = None
|
|
117
|
-
bedrock_url: Optional[str] = None
|
|
118
|
-
instance_id: Optional[Literal["icp", "openshift"]] = None
|
|
119
|
-
version: Optional[str] = None
|
|
120
|
-
disable_ssl_verification: bool = True
|
|
121
|
-
|
|
122
|
-
def __init__(
|
|
123
|
-
self,
|
|
124
|
-
url: str,
|
|
125
|
-
api_key: Optional[str] = None,
|
|
126
|
-
username: Optional[str] = None,
|
|
127
|
-
password: Optional[str] = None,
|
|
128
|
-
bedrock_url: Optional[str] = None,
|
|
129
|
-
instance_id: Optional[Literal["icp", "openshift"]] = None,
|
|
130
|
-
version: Optional[str] = None,
|
|
131
|
-
disable_ssl_verification: bool = True,
|
|
132
|
-
) -> None:
|
|
133
|
-
super().__init__(
|
|
134
|
-
url=url,
|
|
135
|
-
api_key=api_key,
|
|
136
|
-
username=username,
|
|
137
|
-
password=password,
|
|
138
|
-
bedrock_url=bedrock_url,
|
|
139
|
-
instance_id=instance_id,
|
|
140
|
-
version=version,
|
|
141
|
-
disable_ssl_verification=disable_ssl_verification,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
145
|
-
cpd_creds = dict([(k, v) for k, v in self.__dict__.items()]) # noqa: C404
|
|
146
|
-
|
|
147
|
-
if "instance_id" in cpd_creds and self.instance_id.lower() not in [
|
|
148
|
-
"icp",
|
|
149
|
-
"openshift",
|
|
150
|
-
]:
|
|
151
|
-
cpd_creds.pop("instance_id")
|
|
152
|
-
|
|
153
|
-
return cpd_creds
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
class IntegratedSystemCredentials(BaseModel):
|
|
157
|
-
"""
|
|
158
|
-
Encapsulates the credentials for an Integrated System based on the authentication type.
|
|
159
|
-
|
|
160
|
-
Depending on the `auth_type`, only a subset of the properties is required.
|
|
161
|
-
|
|
162
|
-
Attributes:
|
|
163
|
-
auth_type (str): The type of authentication. Currently supports "basic" and "bearer".
|
|
164
|
-
username (str, optional): The username for Basic Authentication.
|
|
165
|
-
password (str, optional): The password for Basic Authentication.
|
|
166
|
-
token_url (str, optional): The URL of the authentication endpoint used to request a Bearer token.
|
|
167
|
-
token_method (str, optional): The HTTP method (e.g., "POST", "GET") used to request the Bearer token.
|
|
168
|
-
Defaults to "POST".
|
|
169
|
-
token_headers (Dict, optional): Optional headers to include when requesting the Bearer token.
|
|
170
|
-
Defaults to `None`.
|
|
171
|
-
token_payload (str | dict, optional): The body or payload to send when requesting the Bearer token.
|
|
172
|
-
Can be a string (e.g., raw JSON). Defaults to `None`.
|
|
173
|
-
"""
|
|
174
|
-
|
|
175
|
-
auth_type: Literal["basic", "bearer"]
|
|
176
|
-
username: Optional[str] # basic
|
|
177
|
-
password: Optional[str] # basic
|
|
178
|
-
token_url: Optional[str] # bearer
|
|
179
|
-
token_method: Optional[str] = "POST" # bearer
|
|
180
|
-
token_headers: Optional[Dict] = {} # bearer
|
|
181
|
-
token_payload: Optional[Union[str, Dict]] = None # bearer
|
|
182
|
-
|
|
183
|
-
def __init__(
|
|
184
|
-
self,
|
|
185
|
-
auth_type: Literal["basic", "bearer"],
|
|
186
|
-
username: str = None,
|
|
187
|
-
password: str = None,
|
|
188
|
-
token_url: str = None,
|
|
189
|
-
token_method: str = "POST",
|
|
190
|
-
token_headers: Dict = {},
|
|
191
|
-
token_payload: Union[str, Dict] = None,
|
|
192
|
-
) -> None:
|
|
193
|
-
if auth_type == "basic":
|
|
194
|
-
if not username or not password:
|
|
195
|
-
raise ValueError(
|
|
196
|
-
"`username` and `password` are required for auth_type = 'basic'.",
|
|
197
|
-
)
|
|
198
|
-
elif auth_type == "bearer":
|
|
199
|
-
if not token_url:
|
|
200
|
-
raise ValueError(
|
|
201
|
-
"`token_url` are required for auth_type = 'bearer'.",
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
super().__init__(
|
|
205
|
-
auth_type=auth_type,
|
|
206
|
-
username=username,
|
|
207
|
-
password=password,
|
|
208
|
-
token_url=token_url,
|
|
209
|
-
token_method=token_method,
|
|
210
|
-
token_headers=token_headers,
|
|
211
|
-
token_payload=token_payload,
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
def to_dict(self) -> Dict:
|
|
215
|
-
integrated_system_creds = {"auth_type": self.auth_type}
|
|
216
|
-
|
|
217
|
-
if self.auth_type == "basic":
|
|
218
|
-
integrated_system_creds["username"] = self.username
|
|
219
|
-
integrated_system_creds["password"] = self.password
|
|
220
|
-
elif self.auth_type == "bearer":
|
|
221
|
-
integrated_system_creds["token_info"] = {
|
|
222
|
-
"url": self.token_url,
|
|
223
|
-
"method": self.token_method,
|
|
224
|
-
"headers": self.token_headers,
|
|
225
|
-
"payload": self.token_payload,
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return integrated_system_creds
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
# ===== Monitor Classes =====
|
|
232
57
|
class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
233
58
|
"""
|
|
234
|
-
Provides functionality to interact with IBM watsonx.governance for monitoring external LLMs.
|
|
59
|
+
Provides functionality to interact with IBM watsonx.governance for monitoring prompts executed on external LLMs.
|
|
235
60
|
|
|
236
61
|
Note:
|
|
237
62
|
One of the following parameters is required to create a prompt monitor:
|
|
@@ -241,7 +66,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
241
66
|
api_key (str): The API key for IBM watsonx.governance.
|
|
242
67
|
space_id (str, optional): The space ID in watsonx.governance.
|
|
243
68
|
project_id (str, optional): The project ID in watsonx.governance.
|
|
244
|
-
region (
|
|
69
|
+
region (Region, optional): The region where watsonx.governance is hosted when using IBM Cloud.
|
|
245
70
|
Defaults to `us-south`.
|
|
246
71
|
cpd_creds (CloudPakforDataCredentials, optional): The Cloud Pak for Data environment credentials.
|
|
247
72
|
subscription_id (str, optional): The subscription ID associated with the records being logged.
|
|
@@ -278,8 +103,8 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
278
103
|
api_key: str = None,
|
|
279
104
|
space_id: str = None,
|
|
280
105
|
project_id: str = None,
|
|
281
|
-
region:
|
|
282
|
-
cpd_creds: CloudPakforDataCredentials
|
|
106
|
+
region: Union[Region, str] = Region.US_SOUTH,
|
|
107
|
+
cpd_creds: Union[CloudPakforDataCredentials, Dict] = None,
|
|
283
108
|
subscription_id: str = None,
|
|
284
109
|
**kwargs,
|
|
285
110
|
) -> None:
|
|
@@ -292,7 +117,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
292
117
|
|
|
293
118
|
self.space_id = space_id
|
|
294
119
|
self.project_id = project_id
|
|
295
|
-
self.region = region
|
|
120
|
+
self.region = Region.from_value(region)
|
|
296
121
|
self.subscription_id = subscription_id
|
|
297
122
|
self._api_key = api_key
|
|
298
123
|
self._wos_client = None
|
|
@@ -302,18 +127,18 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
302
127
|
self._deployment_stage = "production" if space_id else "development"
|
|
303
128
|
|
|
304
129
|
if cpd_creds:
|
|
305
|
-
self._wos_cpd_creds =
|
|
130
|
+
self._wos_cpd_creds = validate_and_filter_dict(
|
|
306
131
|
cpd_creds.to_dict(),
|
|
307
132
|
["username", "password", "api_key", "disable_ssl_verification"],
|
|
308
133
|
["url"],
|
|
309
134
|
)
|
|
310
|
-
self._fact_cpd_creds =
|
|
135
|
+
self._fact_cpd_creds = validate_and_filter_dict(
|
|
311
136
|
cpd_creds.to_dict(),
|
|
312
137
|
["username", "password", "api_key", "bedrock_url"],
|
|
313
138
|
["url"],
|
|
314
139
|
)
|
|
315
140
|
self._fact_cpd_creds["service_url"] = self._fact_cpd_creds.pop("url")
|
|
316
|
-
self._wml_cpd_creds =
|
|
141
|
+
self._wml_cpd_creds = validate_and_filter_dict(
|
|
317
142
|
cpd_creds.to_dict(),
|
|
318
143
|
[
|
|
319
144
|
"username",
|
|
@@ -356,7 +181,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
356
181
|
container_id=self._container_id,
|
|
357
182
|
container_type=self._container_type,
|
|
358
183
|
disable_tracing=True,
|
|
359
|
-
region=
|
|
184
|
+
region=self.region.factsheet,
|
|
360
185
|
)
|
|
361
186
|
|
|
362
187
|
except Exception as e:
|
|
@@ -385,7 +210,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
385
210
|
|
|
386
211
|
else:
|
|
387
212
|
creds = Credentials(
|
|
388
|
-
url=
|
|
213
|
+
url=self.region.watsonxai,
|
|
389
214
|
api_key=self._api_key,
|
|
390
215
|
)
|
|
391
216
|
wml_client = APIClient(creds)
|
|
@@ -411,7 +236,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
411
236
|
return wml_client.deployments.get_uid(created_deployment)
|
|
412
237
|
|
|
413
238
|
@deprecated(
|
|
414
|
-
reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use '
|
|
239
|
+
reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
|
|
415
240
|
version="1.0.5",
|
|
416
241
|
action="always",
|
|
417
242
|
)
|
|
@@ -439,7 +264,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
439
264
|
context_fields: List[str] = None,
|
|
440
265
|
question_field: str = None,
|
|
441
266
|
) -> Dict:
|
|
442
|
-
return self.
|
|
267
|
+
return self.create_prompt_monitor(
|
|
443
268
|
name=name,
|
|
444
269
|
model_id=model_id,
|
|
445
270
|
task_id=task_id,
|
|
@@ -457,6 +282,11 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
457
282
|
question_field=question_field,
|
|
458
283
|
)
|
|
459
284
|
|
|
285
|
+
@deprecated(
|
|
286
|
+
reason="'add_prompt_monitor()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
|
|
287
|
+
version="1.0.6",
|
|
288
|
+
action="always",
|
|
289
|
+
)
|
|
460
290
|
def add_prompt_monitor(
|
|
461
291
|
self,
|
|
462
292
|
name: str,
|
|
@@ -480,9 +310,51 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
480
310
|
input_text: str = None,
|
|
481
311
|
context_fields: List[str] = None,
|
|
482
312
|
question_field: str = None,
|
|
313
|
+
) -> Dict:
|
|
314
|
+
return self.create_prompt_monitor(
|
|
315
|
+
name=name,
|
|
316
|
+
model_id=model_id,
|
|
317
|
+
task_id=task_id,
|
|
318
|
+
detached_model_provider=detached_model_provider,
|
|
319
|
+
description=description,
|
|
320
|
+
model_parameters=model_parameters,
|
|
321
|
+
detached_model_name=detached_model_name,
|
|
322
|
+
detached_model_url=detached_model_url,
|
|
323
|
+
detached_prompt_url=detached_prompt_url,
|
|
324
|
+
detached_prompt_additional_info=detached_prompt_additional_info,
|
|
325
|
+
prompt_variables=prompt_variables,
|
|
326
|
+
locale=locale,
|
|
327
|
+
input_text=input_text,
|
|
328
|
+
context_fields=context_fields,
|
|
329
|
+
question_field=question_field,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def create_prompt_monitor(
|
|
333
|
+
self,
|
|
334
|
+
name: str,
|
|
335
|
+
model_id: str,
|
|
336
|
+
task_id: Literal[
|
|
337
|
+
"extraction",
|
|
338
|
+
"generation",
|
|
339
|
+
"question_answering",
|
|
340
|
+
"retrieval_augmented_generation",
|
|
341
|
+
"summarization",
|
|
342
|
+
],
|
|
343
|
+
detached_model_provider: str,
|
|
344
|
+
description: str = "",
|
|
345
|
+
model_parameters: Dict = None,
|
|
346
|
+
detached_model_name: str = None,
|
|
347
|
+
detached_model_url: str = None,
|
|
348
|
+
detached_prompt_url: str = None,
|
|
349
|
+
detached_prompt_additional_info: Dict = None,
|
|
350
|
+
prompt_variables: List[str] = None,
|
|
351
|
+
locale: str = "en",
|
|
352
|
+
input_text: str = None,
|
|
353
|
+
context_fields: List[str] = None,
|
|
354
|
+
question_field: str = None,
|
|
483
355
|
) -> Dict:
|
|
484
356
|
"""
|
|
485
|
-
Creates a
|
|
357
|
+
Creates a detached (external) prompt template asset and attaches a monitor to the specified prompt template asset.
|
|
486
358
|
|
|
487
359
|
Args:
|
|
488
360
|
name (str): The name of the External Prompt Template Asset.
|
|
@@ -505,7 +377,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
505
377
|
|
|
506
378
|
Example:
|
|
507
379
|
```python
|
|
508
|
-
wxgov_client.
|
|
380
|
+
wxgov_client.create_prompt_monitor(
|
|
509
381
|
name="Detached prompt (model AWS Anthropic)",
|
|
510
382
|
model_id="anthropic.claude-v2",
|
|
511
383
|
task_id="retrieval_augmented_generation",
|
|
@@ -582,7 +454,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
582
454
|
authenticator = IAMAuthenticator(apikey=self._api_key)
|
|
583
455
|
self._wos_client = WosAPIClient(
|
|
584
456
|
authenticator=authenticator,
|
|
585
|
-
service_url=
|
|
457
|
+
service_url=self.region.openscale,
|
|
586
458
|
)
|
|
587
459
|
|
|
588
460
|
except Exception as e:
|
|
@@ -591,19 +463,19 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
591
463
|
)
|
|
592
464
|
raise
|
|
593
465
|
|
|
594
|
-
detached_details =
|
|
466
|
+
detached_details = validate_and_filter_dict(
|
|
595
467
|
prompt_metadata,
|
|
596
468
|
["model_name", "model_url", "prompt_url", "prompt_additional_info"],
|
|
597
469
|
["model_id", "model_provider"],
|
|
598
470
|
)
|
|
599
471
|
detached_details["prompt_id"] = "detached_prompt_" + str(uuid.uuid4())
|
|
600
472
|
|
|
601
|
-
prompt_details =
|
|
473
|
+
prompt_details = validate_and_filter_dict(
|
|
602
474
|
prompt_metadata,
|
|
603
475
|
["prompt_variables", "input", "model_parameters"],
|
|
604
476
|
)
|
|
605
477
|
|
|
606
|
-
detached_asset_details =
|
|
478
|
+
detached_asset_details = validate_and_filter_dict(
|
|
607
479
|
prompt_metadata,
|
|
608
480
|
["description"],
|
|
609
481
|
["name", "model_id", "task_id"],
|
|
@@ -752,7 +624,7 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
752
624
|
authenticator = IAMAuthenticator(apikey=self._api_key)
|
|
753
625
|
self._wos_client = WosAPIClient(
|
|
754
626
|
authenticator=authenticator,
|
|
755
|
-
service_url=
|
|
627
|
+
service_url=self.region.openscale,
|
|
756
628
|
)
|
|
757
629
|
|
|
758
630
|
except Exception as e:
|
|
@@ -805,7 +677,8 @@ class WatsonxExternalPromptMonitor(PromptMonitor):
|
|
|
805
677
|
|
|
806
678
|
class WatsonxPromptMonitor(PromptMonitor):
|
|
807
679
|
"""
|
|
808
|
-
Provides functionality to interact with IBM watsonx.governance for monitoring
|
|
680
|
+
Provides functionality to interact with IBM watsonx.governance for monitoring prompts executed within
|
|
681
|
+
IBM watsonx.ai LLMs.
|
|
809
682
|
|
|
810
683
|
Note:
|
|
811
684
|
One of the following parameters is required to create a prompt monitor:
|
|
@@ -815,7 +688,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
815
688
|
api_key (str): The API key for IBM watsonx.governance.
|
|
816
689
|
space_id (str, optional): The space ID in watsonx.governance.
|
|
817
690
|
project_id (str, optional): The project ID in watsonx.governance.
|
|
818
|
-
region (
|
|
691
|
+
region (Region, optional): The region where watsonx.governance is hosted when using IBM Cloud.
|
|
819
692
|
Defaults to `us-south`.
|
|
820
693
|
cpd_creds (CloudPakforDataCredentials, optional): The Cloud Pak for Data environment credentials.
|
|
821
694
|
subscription_id (str, optional): The subscription ID associated with the records being logged.
|
|
@@ -848,8 +721,8 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
848
721
|
api_key: str = None,
|
|
849
722
|
space_id: str = None,
|
|
850
723
|
project_id: str = None,
|
|
851
|
-
region:
|
|
852
|
-
cpd_creds: CloudPakforDataCredentials
|
|
724
|
+
region: Union[Region, str] = Region.US_SOUTH,
|
|
725
|
+
cpd_creds: Union[CloudPakforDataCredentials, Dict] = None,
|
|
853
726
|
subscription_id: str = None,
|
|
854
727
|
**kwargs,
|
|
855
728
|
) -> None:
|
|
@@ -862,7 +735,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
862
735
|
|
|
863
736
|
self.space_id = space_id
|
|
864
737
|
self.project_id = project_id
|
|
865
|
-
self.region = region
|
|
738
|
+
self.region = Region.from_value(region)
|
|
866
739
|
self.subscription_id = subscription_id
|
|
867
740
|
self._api_key = api_key
|
|
868
741
|
self._wos_client = None
|
|
@@ -872,18 +745,18 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
872
745
|
self._deployment_stage = "production" if space_id else "development"
|
|
873
746
|
|
|
874
747
|
if cpd_creds:
|
|
875
|
-
self._wos_cpd_creds =
|
|
748
|
+
self._wos_cpd_creds = validate_and_filter_dict(
|
|
876
749
|
cpd_creds.to_dict(),
|
|
877
750
|
["username", "password", "api_key", "disable_ssl_verification"],
|
|
878
751
|
["url"],
|
|
879
752
|
)
|
|
880
|
-
self._fact_cpd_creds =
|
|
753
|
+
self._fact_cpd_creds = validate_and_filter_dict(
|
|
881
754
|
cpd_creds.to_dict(),
|
|
882
755
|
["username", "password", "api_key", "bedrock_url"],
|
|
883
756
|
["url"],
|
|
884
757
|
)
|
|
885
758
|
self._fact_cpd_creds["service_url"] = self._fact_cpd_creds.pop("url")
|
|
886
|
-
self._wml_cpd_creds =
|
|
759
|
+
self._wml_cpd_creds = validate_and_filter_dict(
|
|
887
760
|
cpd_creds.to_dict(),
|
|
888
761
|
[
|
|
889
762
|
"username",
|
|
@@ -924,7 +797,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
924
797
|
container_id=self._container_id,
|
|
925
798
|
container_type=self._container_type,
|
|
926
799
|
disable_tracing=True,
|
|
927
|
-
region=
|
|
800
|
+
region=self.region.factsheet,
|
|
928
801
|
)
|
|
929
802
|
|
|
930
803
|
except Exception as e:
|
|
@@ -953,7 +826,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
953
826
|
|
|
954
827
|
else:
|
|
955
828
|
creds = Credentials(
|
|
956
|
-
url=
|
|
829
|
+
url=self.region.watsonxai,
|
|
957
830
|
api_key=self._api_key,
|
|
958
831
|
)
|
|
959
832
|
|
|
@@ -980,7 +853,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
980
853
|
return wml_client.deployments.get_uid(created_deployment)
|
|
981
854
|
|
|
982
855
|
@deprecated(
|
|
983
|
-
reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use '
|
|
856
|
+
reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
|
|
984
857
|
version="1.0.5",
|
|
985
858
|
action="always",
|
|
986
859
|
)
|
|
@@ -1003,7 +876,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1003
876
|
context_fields: List[str] = None,
|
|
1004
877
|
question_field: str = None,
|
|
1005
878
|
) -> Dict:
|
|
1006
|
-
return self.
|
|
879
|
+
return self.create_prompt_monitor(
|
|
1007
880
|
name=name,
|
|
1008
881
|
model_id=model_id,
|
|
1009
882
|
task_id=task_id,
|
|
@@ -1016,6 +889,11 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1016
889
|
question_field=question_field,
|
|
1017
890
|
)
|
|
1018
891
|
|
|
892
|
+
@deprecated(
|
|
893
|
+
reason="'add_prompt_observer()' is deprecated and will be removed in a future version. Use 'create_prompt_monitor()' instead.",
|
|
894
|
+
version="1.0.6",
|
|
895
|
+
action="always",
|
|
896
|
+
)
|
|
1019
897
|
def add_prompt_monitor(
|
|
1020
898
|
self,
|
|
1021
899
|
name: str,
|
|
@@ -1034,6 +912,38 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1034
912
|
input_text: str = None,
|
|
1035
913
|
context_fields: List[str] = None,
|
|
1036
914
|
question_field: str = None,
|
|
915
|
+
) -> Dict:
|
|
916
|
+
return self.create_prompt_monitor(
|
|
917
|
+
name=name,
|
|
918
|
+
model_id=model_id,
|
|
919
|
+
task_id=task_id,
|
|
920
|
+
description=description,
|
|
921
|
+
model_parameters=model_parameters,
|
|
922
|
+
prompt_variables=prompt_variables,
|
|
923
|
+
locale=locale,
|
|
924
|
+
input_text=input_text,
|
|
925
|
+
context_fields=context_fields,
|
|
926
|
+
question_field=question_field,
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
def create_prompt_monitor(
|
|
930
|
+
self,
|
|
931
|
+
name: str,
|
|
932
|
+
model_id: str,
|
|
933
|
+
task_id: Literal[
|
|
934
|
+
"extraction",
|
|
935
|
+
"generation",
|
|
936
|
+
"question_answering",
|
|
937
|
+
"retrieval_augmented_generation",
|
|
938
|
+
"summarization",
|
|
939
|
+
],
|
|
940
|
+
description: str = "",
|
|
941
|
+
model_parameters: Dict = None,
|
|
942
|
+
prompt_variables: List[str] = None,
|
|
943
|
+
locale: str = "en",
|
|
944
|
+
input_text: str = None,
|
|
945
|
+
context_fields: List[str] = None,
|
|
946
|
+
question_field: str = None,
|
|
1037
947
|
) -> Dict:
|
|
1038
948
|
"""
|
|
1039
949
|
Creates an IBM Prompt Template Asset and ssetup monitor for the given prompt template asset.
|
|
@@ -1054,7 +964,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1054
964
|
|
|
1055
965
|
Example:
|
|
1056
966
|
```python
|
|
1057
|
-
wxgov_client.
|
|
967
|
+
wxgov_client.create_prompt_monitor(
|
|
1058
968
|
name="IBM prompt template",
|
|
1059
969
|
model_id="ibm/granite-3-2b-instruct",
|
|
1060
970
|
task_id="retrieval_augmented_generation",
|
|
@@ -1119,7 +1029,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1119
1029
|
authenticator = IAMAuthenticator(apikey=self._api_key)
|
|
1120
1030
|
self._wos_client = WosAPIClient(
|
|
1121
1031
|
authenticator=authenticator,
|
|
1122
|
-
service_url=
|
|
1032
|
+
service_url=self.region.openscale,
|
|
1123
1033
|
)
|
|
1124
1034
|
|
|
1125
1035
|
except Exception as e:
|
|
@@ -1128,12 +1038,12 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1128
1038
|
)
|
|
1129
1039
|
raise
|
|
1130
1040
|
|
|
1131
|
-
prompt_details =
|
|
1041
|
+
prompt_details = validate_and_filter_dict(
|
|
1132
1042
|
prompt_metadata,
|
|
1133
1043
|
["prompt_variables", "input", "model_parameters"],
|
|
1134
1044
|
)
|
|
1135
1045
|
|
|
1136
|
-
asset_details =
|
|
1046
|
+
asset_details = validate_and_filter_dict(
|
|
1137
1047
|
prompt_metadata,
|
|
1138
1048
|
["description"],
|
|
1139
1049
|
["name", "model_id", "task_id"],
|
|
@@ -1280,7 +1190,7 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1280
1190
|
authenticator = IAMAuthenticator(apikey=self._api_key)
|
|
1281
1191
|
self._wos_client = WosAPIClient(
|
|
1282
1192
|
authenticator=authenticator,
|
|
1283
|
-
service_url=
|
|
1193
|
+
service_url=self.region.openscale,
|
|
1284
1194
|
)
|
|
1285
1195
|
|
|
1286
1196
|
except Exception as e:
|
|
@@ -1329,674 +1239,3 @@ class WatsonxPromptMonitor(PromptMonitor):
|
|
|
1329
1239
|
self.store_payload_records([payload.model_dump()])
|
|
1330
1240
|
else:
|
|
1331
1241
|
self.store_payload_records([{**payload.model_dump(), **template_vars}])
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
# ===== Supporting Classes =====
|
|
1335
|
-
class WatsonxLocalMetric(BaseModel):
|
|
1336
|
-
"""
|
|
1337
|
-
Provides the IBM watsonx.governance local monitor metric definition.
|
|
1338
|
-
|
|
1339
|
-
Attributes:
|
|
1340
|
-
name (str): The name of the metric.
|
|
1341
|
-
data_type (str): The data type of the metric. Currently supports "string", "integer", "double", and "timestamp".
|
|
1342
|
-
nullable (bool, optional): Indicates whether the metric can be null. Defaults to `False`.
|
|
1343
|
-
|
|
1344
|
-
Example:
|
|
1345
|
-
```python
|
|
1346
|
-
from beekeeper.monitors.watsonx import WatsonxLocalMetric
|
|
1347
|
-
|
|
1348
|
-
WatsonxLocalMetric(name="context_quality", data_type="double")
|
|
1349
|
-
```
|
|
1350
|
-
"""
|
|
1351
|
-
|
|
1352
|
-
name: str
|
|
1353
|
-
data_type: Literal["string", "integer", "double", "timestamp"]
|
|
1354
|
-
nullable: bool = True
|
|
1355
|
-
|
|
1356
|
-
def to_dict(self) -> Dict:
|
|
1357
|
-
return {"name": self.name, "type": self.data_type, "nullable": self.nullable}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
class WatsonxMetricThreshold(BaseModel):
|
|
1361
|
-
"""
|
|
1362
|
-
Defines the metric threshold for IBM watsonx.governance.
|
|
1363
|
-
|
|
1364
|
-
Attributes:
|
|
1365
|
-
threshold_type (str): The threshold type. Can be either `lower_limit` or `upper_limit`.
|
|
1366
|
-
default_value (float): The metric threshold value.
|
|
1367
|
-
|
|
1368
|
-
Example:
|
|
1369
|
-
```python
|
|
1370
|
-
from beekeeper.monitors.watsonx import WatsonxMetricThreshold
|
|
1371
|
-
|
|
1372
|
-
WatsonxMetricThreshold(threshold_type="lower_limit", default_value=0.8)
|
|
1373
|
-
```
|
|
1374
|
-
"""
|
|
1375
|
-
|
|
1376
|
-
threshold_type: Literal["lower_limit", "upper_limit"]
|
|
1377
|
-
default_value: float = None
|
|
1378
|
-
|
|
1379
|
-
def to_dict(self) -> Dict:
|
|
1380
|
-
return {"type": self.threshold_type, "default": self.default_value}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
class WatsonxMetric(BaseModel):
|
|
1384
|
-
"""
|
|
1385
|
-
Defines the IBM watsonx.governance global monitor metric.
|
|
1386
|
-
|
|
1387
|
-
Attributes:
|
|
1388
|
-
name (str): The name of the metric.
|
|
1389
|
-
applies_to (List[str]): A list of task types that the metric applies to. Currently supports:
|
|
1390
|
-
"summarization", "generation", "question_answering", "extraction", and "retrieval_augmented_generation".
|
|
1391
|
-
thresholds (List[WatsonxMetricThreshold]): A list of metric thresholds associated with the metric.
|
|
1392
|
-
|
|
1393
|
-
Example:
|
|
1394
|
-
```python
|
|
1395
|
-
from beekeeper.monitors.watsonx import (
|
|
1396
|
-
WatsonxMetric,
|
|
1397
|
-
WatsonxMetricThreshold,
|
|
1398
|
-
)
|
|
1399
|
-
|
|
1400
|
-
WatsonxMetric(
|
|
1401
|
-
name="context_quality",
|
|
1402
|
-
applies_to=["retrieval_augmented_generation", "summarization"],
|
|
1403
|
-
thresholds=[
|
|
1404
|
-
WatsonxMetricThreshold(threshold_type="lower_limit", default_value=0.75)
|
|
1405
|
-
],
|
|
1406
|
-
)
|
|
1407
|
-
```
|
|
1408
|
-
"""
|
|
1409
|
-
|
|
1410
|
-
name: str
|
|
1411
|
-
applies_to: List[
|
|
1412
|
-
Literal[
|
|
1413
|
-
"summarization",
|
|
1414
|
-
"generation",
|
|
1415
|
-
"question_answering",
|
|
1416
|
-
"extraction",
|
|
1417
|
-
"retrieval_augmented_generation",
|
|
1418
|
-
]
|
|
1419
|
-
]
|
|
1420
|
-
thresholds: Optional[List[WatsonxMetricThreshold]] = None
|
|
1421
|
-
|
|
1422
|
-
def to_dict(self) -> Dict:
|
|
1423
|
-
from ibm_watson_openscale.base_classes.watson_open_scale_v2 import (
|
|
1424
|
-
ApplicabilitySelection,
|
|
1425
|
-
MetricThreshold,
|
|
1426
|
-
)
|
|
1427
|
-
|
|
1428
|
-
monitor_metric = {
|
|
1429
|
-
"name": self.name,
|
|
1430
|
-
"applies_to": ApplicabilitySelection(problem_type=self.applies_to),
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
if self.thresholds is not None:
|
|
1434
|
-
monitor_metric["thresholds"] = [
|
|
1435
|
-
MetricThreshold(**threshold.to_dict()) for threshold in self.thresholds
|
|
1436
|
-
]
|
|
1437
|
-
|
|
1438
|
-
return monitor_metric
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
# ===== Metric Classes =====
|
|
1442
|
-
class WatsonxCustomMetric:
|
|
1443
|
-
"""
|
|
1444
|
-
Provides functionality to set up a custom metric to measure your model's performance with IBM watsonx.governance.
|
|
1445
|
-
|
|
1446
|
-
Attributes:
|
|
1447
|
-
api_key (str): The API key for IBM watsonx.governance.
|
|
1448
|
-
region (str, optional): The region where IBM watsonx.governance is hosted when using IBM Cloud.
|
|
1449
|
-
Defaults to `us-south`.
|
|
1450
|
-
cpd_creds (CloudPakforDataCredentials, optional): IBM Cloud Pak for Data environment credentials.
|
|
1451
|
-
|
|
1452
|
-
Example:
|
|
1453
|
-
```python
|
|
1454
|
-
from beekeeper.monitors.watsonx import (
|
|
1455
|
-
WatsonxCustomMetric,
|
|
1456
|
-
CloudPakforDataCredentials,
|
|
1457
|
-
)
|
|
1458
|
-
|
|
1459
|
-
# watsonx.governance (IBM Cloud)
|
|
1460
|
-
wxgov_client = WatsonxCustomMetric(api_key="API_KEY")
|
|
1461
|
-
|
|
1462
|
-
# watsonx.governance (CP4D)
|
|
1463
|
-
cpd_creds = CloudPakforDataCredentials(
|
|
1464
|
-
url="CPD_URL",
|
|
1465
|
-
username="USERNAME",
|
|
1466
|
-
password="PASSWORD",
|
|
1467
|
-
version="5.0",
|
|
1468
|
-
instance_id="openshift",
|
|
1469
|
-
)
|
|
1470
|
-
|
|
1471
|
-
wxgov_client = WatsonxCustomMetric(cpd_creds=cpd_creds)
|
|
1472
|
-
```
|
|
1473
|
-
"""
|
|
1474
|
-
|
|
1475
|
-
def __init__(
|
|
1476
|
-
self,
|
|
1477
|
-
api_key: str = None,
|
|
1478
|
-
region: Literal["us-south", "eu-de", "au-syd"] = "us-south",
|
|
1479
|
-
cpd_creds: CloudPakforDataCredentials | Dict = None,
|
|
1480
|
-
) -> None:
|
|
1481
|
-
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator # type: ignore
|
|
1482
|
-
from ibm_watson_openscale import APIClient as WosAPIClient # type: ignore
|
|
1483
|
-
|
|
1484
|
-
self.region = region
|
|
1485
|
-
self._api_key = api_key
|
|
1486
|
-
self._wos_client = None
|
|
1487
|
-
|
|
1488
|
-
if cpd_creds:
|
|
1489
|
-
self._wos_cpd_creds = _filter_dict(
|
|
1490
|
-
cpd_creds.to_dict(),
|
|
1491
|
-
["username", "password", "api_key", "disable_ssl_verification"],
|
|
1492
|
-
["url"],
|
|
1493
|
-
)
|
|
1494
|
-
|
|
1495
|
-
if not self._wos_client:
|
|
1496
|
-
try:
|
|
1497
|
-
if hasattr(self, "_wos_cpd_creds") and self._wos_cpd_creds:
|
|
1498
|
-
from ibm_cloud_sdk_core.authenticators import (
|
|
1499
|
-
CloudPakForDataAuthenticator, # type: ignore
|
|
1500
|
-
)
|
|
1501
|
-
|
|
1502
|
-
authenticator = CloudPakForDataAuthenticator(**self._wos_cpd_creds)
|
|
1503
|
-
|
|
1504
|
-
self._wos_client = WosAPIClient(
|
|
1505
|
-
authenticator=authenticator,
|
|
1506
|
-
service_url=self._wos_cpd_creds["url"],
|
|
1507
|
-
)
|
|
1508
|
-
|
|
1509
|
-
else:
|
|
1510
|
-
from ibm_cloud_sdk_core.authenticators import (
|
|
1511
|
-
IAMAuthenticator, # type: ignore
|
|
1512
|
-
)
|
|
1513
|
-
|
|
1514
|
-
authenticator = IAMAuthenticator(apikey=self._api_key)
|
|
1515
|
-
self._wos_client = WosAPIClient(
|
|
1516
|
-
authenticator=authenticator,
|
|
1517
|
-
service_url=REGIONS_URL[self.region]["wos"],
|
|
1518
|
-
)
|
|
1519
|
-
|
|
1520
|
-
except Exception as e:
|
|
1521
|
-
logging.error(
|
|
1522
|
-
f"Error connecting to IBM watsonx.governance (openscale): {e}",
|
|
1523
|
-
)
|
|
1524
|
-
raise
|
|
1525
|
-
|
|
1526
|
-
def _add_integrated_system(
|
|
1527
|
-
self,
|
|
1528
|
-
credentials: IntegratedSystemCredentials,
|
|
1529
|
-
name: str,
|
|
1530
|
-
endpoint: str,
|
|
1531
|
-
) -> str:
|
|
1532
|
-
custom_metrics_integrated_system = self._wos_client.integrated_systems.add(
|
|
1533
|
-
name=name,
|
|
1534
|
-
description="Integrated system created by Beekeeper.",
|
|
1535
|
-
type="custom_metrics_provider",
|
|
1536
|
-
credentials=credentials.to_dict(),
|
|
1537
|
-
connection={"display_name": name, "endpoint": endpoint},
|
|
1538
|
-
).result
|
|
1539
|
-
|
|
1540
|
-
return custom_metrics_integrated_system.metadata.id
|
|
1541
|
-
|
|
1542
|
-
def _add_monitor_definitions(
|
|
1543
|
-
self,
|
|
1544
|
-
name: str,
|
|
1545
|
-
metrics: List[WatsonxMetric],
|
|
1546
|
-
schedule: bool,
|
|
1547
|
-
):
|
|
1548
|
-
from ibm_watson_openscale.base_classes.watson_open_scale_v2 import (
|
|
1549
|
-
ApplicabilitySelection,
|
|
1550
|
-
MonitorInstanceSchedule,
|
|
1551
|
-
MonitorMetricRequest,
|
|
1552
|
-
MonitorRuntime,
|
|
1553
|
-
ScheduleStartTime,
|
|
1554
|
-
)
|
|
1555
|
-
|
|
1556
|
-
_metrics = [MonitorMetricRequest(**metric.to_dict()) for metric in metrics]
|
|
1557
|
-
_monitor_runtime = None
|
|
1558
|
-
_monitor_schedule = None
|
|
1559
|
-
|
|
1560
|
-
if schedule:
|
|
1561
|
-
_monitor_runtime = MonitorRuntime(type="custom_metrics_provider")
|
|
1562
|
-
_monitor_schedule = MonitorInstanceSchedule(
|
|
1563
|
-
repeat_interval=1,
|
|
1564
|
-
repeat_unit="hour",
|
|
1565
|
-
start_time=ScheduleStartTime(
|
|
1566
|
-
type="relative",
|
|
1567
|
-
delay_unit="minute",
|
|
1568
|
-
delay=30,
|
|
1569
|
-
),
|
|
1570
|
-
)
|
|
1571
|
-
|
|
1572
|
-
custom_monitor_details = self._wos_client.monitor_definitions.add(
|
|
1573
|
-
name=name,
|
|
1574
|
-
metrics=_metrics,
|
|
1575
|
-
tags=[],
|
|
1576
|
-
schedule=_monitor_schedule,
|
|
1577
|
-
applies_to=ApplicabilitySelection(input_data_type=["unstructured_text"]),
|
|
1578
|
-
monitor_runtime=_monitor_runtime,
|
|
1579
|
-
background_mode=False,
|
|
1580
|
-
).result
|
|
1581
|
-
|
|
1582
|
-
return custom_monitor_details.metadata.id
|
|
1583
|
-
|
|
1584
|
-
def _get_monitor_instance(self, subscription_id: str, monitor_definition_id: str):
|
|
1585
|
-
monitor_instances = self._wos_client.monitor_instances.list(
|
|
1586
|
-
monitor_definition_id=monitor_definition_id,
|
|
1587
|
-
target_target_id=subscription_id,
|
|
1588
|
-
).result.monitor_instances
|
|
1589
|
-
|
|
1590
|
-
if len(monitor_instances) == 1:
|
|
1591
|
-
return monitor_instances[0]
|
|
1592
|
-
else:
|
|
1593
|
-
return None
|
|
1594
|
-
|
|
1595
|
-
def _update_monitor_instance(
|
|
1596
|
-
self,
|
|
1597
|
-
integrated_system_id: str,
|
|
1598
|
-
custom_monitor_id: str,
|
|
1599
|
-
):
|
|
1600
|
-
payload = [
|
|
1601
|
-
{
|
|
1602
|
-
"op": "replace",
|
|
1603
|
-
"path": "/parameters",
|
|
1604
|
-
"value": {
|
|
1605
|
-
"custom_metrics_provider_id": integrated_system_id,
|
|
1606
|
-
"custom_metrics_wait_time": 60,
|
|
1607
|
-
"enable_custom_metric_runs": True,
|
|
1608
|
-
},
|
|
1609
|
-
},
|
|
1610
|
-
]
|
|
1611
|
-
|
|
1612
|
-
return self._wos_client.monitor_instances.update(
|
|
1613
|
-
custom_monitor_id,
|
|
1614
|
-
payload,
|
|
1615
|
-
update_metadata_only=True,
|
|
1616
|
-
).result
|
|
1617
|
-
|
|
1618
|
-
def _get_patch_request_field(
|
|
1619
|
-
self,
|
|
1620
|
-
field_path: str,
|
|
1621
|
-
field_value: Any,
|
|
1622
|
-
op_name: str = "replace",
|
|
1623
|
-
) -> Dict:
|
|
1624
|
-
return {"op": op_name, "path": field_path, "value": field_value}
|
|
1625
|
-
|
|
1626
|
-
def _get_dataset_id(
|
|
1627
|
-
self,
|
|
1628
|
-
subscription_id: str,
|
|
1629
|
-
data_set_type: Literal["feedback", "payload_logging"],
|
|
1630
|
-
) -> str:
|
|
1631
|
-
data_sets = self._wos_client.data_sets.list(
|
|
1632
|
-
target_target_id=subscription_id,
|
|
1633
|
-
type=data_set_type,
|
|
1634
|
-
).result.data_sets
|
|
1635
|
-
data_set_id = None
|
|
1636
|
-
if len(data_sets) > 0:
|
|
1637
|
-
data_set_id = data_sets[0].metadata.id
|
|
1638
|
-
return data_set_id
|
|
1639
|
-
|
|
1640
|
-
def _get_dataset_data(self, data_set_id: str):
|
|
1641
|
-
json_data = self._wos_client.data_sets.get_list_of_records(
|
|
1642
|
-
data_set_id=data_set_id,
|
|
1643
|
-
format="list",
|
|
1644
|
-
).result
|
|
1645
|
-
|
|
1646
|
-
if not json_data.get("records"):
|
|
1647
|
-
return None
|
|
1648
|
-
|
|
1649
|
-
return json_data["records"][0]
|
|
1650
|
-
|
|
1651
|
-
def _get_existing_data_mart(self):
|
|
1652
|
-
data_marts = self._wos_client.data_marts.list().result.data_marts
|
|
1653
|
-
if len(data_marts) == 0:
|
|
1654
|
-
raise Exception(
|
|
1655
|
-
"No data marts found. Please ensure at least one data mart is available.",
|
|
1656
|
-
)
|
|
1657
|
-
|
|
1658
|
-
return data_marts[0].metadata.id
|
|
1659
|
-
|
|
1660
|
-
# ===== Global Custom Metrics =====
|
|
1661
|
-
def add_metric_definition(
|
|
1662
|
-
self,
|
|
1663
|
-
name: str,
|
|
1664
|
-
metrics: List[WatsonxMetric],
|
|
1665
|
-
integrated_system_url: str,
|
|
1666
|
-
integrated_system_credentials: IntegratedSystemCredentials,
|
|
1667
|
-
schedule: bool = False,
|
|
1668
|
-
):
|
|
1669
|
-
"""
|
|
1670
|
-
Creates a custom monitor definition for IBM watsonx.governance.
|
|
1671
|
-
|
|
1672
|
-
This must be done before using custom metrics.
|
|
1673
|
-
|
|
1674
|
-
Args:
|
|
1675
|
-
name (str): The name of the custom metric group.
|
|
1676
|
-
metrics (List[WatsonxMetric]): A list of metrics to be measured.
|
|
1677
|
-
schedule (bool, optional): Enable or disable the scheduler. Defaults to `False`.
|
|
1678
|
-
integrated_system_url (str): The URL of the external metric provider.
|
|
1679
|
-
integrated_system_credentials (IntegratedSystemCredentials): The credentials for the integrated system.
|
|
1680
|
-
|
|
1681
|
-
Example:
|
|
1682
|
-
```python
|
|
1683
|
-
from beekeeper.monitors.watsonx import (
|
|
1684
|
-
WatsonxMetric,
|
|
1685
|
-
IntegratedSystemCredentials,
|
|
1686
|
-
WatsonxMetricThreshold,
|
|
1687
|
-
)
|
|
1688
|
-
|
|
1689
|
-
wxgov_client.add_metric_definition(
|
|
1690
|
-
name="Custom Metric - Custom LLM Quality",
|
|
1691
|
-
metrics=[
|
|
1692
|
-
WatsonxMetric(
|
|
1693
|
-
name="context_quality",
|
|
1694
|
-
applies_to=[
|
|
1695
|
-
"retrieval_augmented_generation",
|
|
1696
|
-
"summarization",
|
|
1697
|
-
],
|
|
1698
|
-
thresholds=[
|
|
1699
|
-
WatsonxMetricThreshold(
|
|
1700
|
-
threshold_type="lower_limit", default_value=0.75
|
|
1701
|
-
)
|
|
1702
|
-
],
|
|
1703
|
-
)
|
|
1704
|
-
],
|
|
1705
|
-
integrated_system_url="IS_URL", # URL to the endpoint computing the metric
|
|
1706
|
-
integrated_system_credentials=IntegratedSystemCredentials(
|
|
1707
|
-
auth_type="basic", username="USERNAME", password="PASSWORD"
|
|
1708
|
-
),
|
|
1709
|
-
)
|
|
1710
|
-
```
|
|
1711
|
-
"""
|
|
1712
|
-
integrated_system_id = self._add_integrated_system(
|
|
1713
|
-
integrated_system_credentials,
|
|
1714
|
-
name,
|
|
1715
|
-
integrated_system_url,
|
|
1716
|
-
)
|
|
1717
|
-
|
|
1718
|
-
external_monitor_id = suppress_output(
|
|
1719
|
-
self._add_monitor_definitions,
|
|
1720
|
-
name,
|
|
1721
|
-
metrics,
|
|
1722
|
-
schedule,
|
|
1723
|
-
)
|
|
1724
|
-
|
|
1725
|
-
# Associate the external monitor with the integrated system
|
|
1726
|
-
payload = [
|
|
1727
|
-
{
|
|
1728
|
-
"op": "add",
|
|
1729
|
-
"path": "/parameters",
|
|
1730
|
-
"value": {"monitor_definition_ids": [external_monitor_id]},
|
|
1731
|
-
},
|
|
1732
|
-
]
|
|
1733
|
-
|
|
1734
|
-
self._wos_client.integrated_systems.update(integrated_system_id, payload)
|
|
1735
|
-
|
|
1736
|
-
return {
|
|
1737
|
-
"integrated_system_id": integrated_system_id,
|
|
1738
|
-
"monitor_definition_id": external_monitor_id,
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
@deprecated(
|
|
1742
|
-
reason="'add_observer_instance()' is deprecated and will be removed in a future version. Use 'add_monitor_instance()' from 'beekeeper-monitors-watsonx' instead.",
|
|
1743
|
-
version="1.0.5",
|
|
1744
|
-
action="always",
|
|
1745
|
-
)
|
|
1746
|
-
def add_observer_instance(
|
|
1747
|
-
self,
|
|
1748
|
-
integrated_system_id: str,
|
|
1749
|
-
monitor_definition_id: str,
|
|
1750
|
-
subscription_id: str,
|
|
1751
|
-
):
|
|
1752
|
-
return self.add_monitor_instance(
|
|
1753
|
-
integrated_system_id=integrated_system_id,
|
|
1754
|
-
monitor_definition_id=monitor_definition_id,
|
|
1755
|
-
subscription_id=subscription_id,
|
|
1756
|
-
)
|
|
1757
|
-
|
|
1758
|
-
def add_monitor_instance(
|
|
1759
|
-
self,
|
|
1760
|
-
integrated_system_id: str,
|
|
1761
|
-
monitor_definition_id: str,
|
|
1762
|
-
subscription_id: str,
|
|
1763
|
-
):
|
|
1764
|
-
"""
|
|
1765
|
-
Enables a custom monitor for the specified subscription and monitor definition.
|
|
1766
|
-
|
|
1767
|
-
Args:
|
|
1768
|
-
integrated_system_id (str): The ID of the integrated system.
|
|
1769
|
-
monitor_definition_id (str): The ID of the custom metric monitor instance.
|
|
1770
|
-
subscription_id (str): The ID of the subscription to associate the monitor with.
|
|
1771
|
-
|
|
1772
|
-
Example:
|
|
1773
|
-
```python
|
|
1774
|
-
wxgov_client.add_monitor_instance(
|
|
1775
|
-
integrated_system_id="019667ca-5687-7838-8d29-4ff70c2b36b0",
|
|
1776
|
-
monitor_definition_id="custom_llm_quality",
|
|
1777
|
-
subscription_id="0195e95d-03a4-7000-b954-b607db10fe9e",
|
|
1778
|
-
)
|
|
1779
|
-
```
|
|
1780
|
-
"""
|
|
1781
|
-
from ibm_watson_openscale.base_classes.watson_open_scale_v2 import Target
|
|
1782
|
-
|
|
1783
|
-
data_marts = self._wos_client.data_marts.list().result.data_marts
|
|
1784
|
-
if len(data_marts) == 0:
|
|
1785
|
-
raise Exception(
|
|
1786
|
-
"No data marts found. Please ensure at least one data mart is available.",
|
|
1787
|
-
)
|
|
1788
|
-
|
|
1789
|
-
data_mart_id = data_marts[0].metadata.id
|
|
1790
|
-
existing_monitor_instance = self._get_monitor_instance(
|
|
1791
|
-
subscription_id,
|
|
1792
|
-
monitor_definition_id,
|
|
1793
|
-
)
|
|
1794
|
-
|
|
1795
|
-
if existing_monitor_instance is None:
|
|
1796
|
-
target = Target(target_type="subscription", target_id=subscription_id)
|
|
1797
|
-
|
|
1798
|
-
parameters = {
|
|
1799
|
-
"custom_metrics_provider_id": integrated_system_id,
|
|
1800
|
-
"custom_metrics_wait_time": 60,
|
|
1801
|
-
"enable_custom_metric_runs": True,
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
monitor_instance_details = suppress_output(
|
|
1805
|
-
self._wos_client.monitor_instances.create,
|
|
1806
|
-
data_mart_id=data_mart_id,
|
|
1807
|
-
background_mode=False,
|
|
1808
|
-
monitor_definition_id=monitor_definition_id,
|
|
1809
|
-
target=target,
|
|
1810
|
-
parameters=parameters,
|
|
1811
|
-
).result
|
|
1812
|
-
else:
|
|
1813
|
-
existing_instance_id = existing_monitor_instance.metadata.id
|
|
1814
|
-
monitor_instance_details = self._update_monitor_instance(
|
|
1815
|
-
integrated_system_id,
|
|
1816
|
-
existing_instance_id,
|
|
1817
|
-
)
|
|
1818
|
-
|
|
1819
|
-
return monitor_instance_details
|
|
1820
|
-
|
|
1821
|
-
def publish_metrics(
|
|
1822
|
-
self,
|
|
1823
|
-
monitor_instance_id: str,
|
|
1824
|
-
run_id: str,
|
|
1825
|
-
request_records: Dict[str, Union[float, int]],
|
|
1826
|
-
):
|
|
1827
|
-
"""
|
|
1828
|
-
Publishes computed custom metrics for a specific global monitor instance.
|
|
1829
|
-
|
|
1830
|
-
Args:
|
|
1831
|
-
monitor_instance_id (str): The unique ID of the monitor instance.
|
|
1832
|
-
run_id (str): The ID of the monitor run that generated the metrics.
|
|
1833
|
-
request_records (Dict[str | float | int]): Dict containing the metrics to be published.
|
|
1834
|
-
|
|
1835
|
-
Example:
|
|
1836
|
-
```python
|
|
1837
|
-
wxgov_client.publish_metrics(
|
|
1838
|
-
monitor_instance_id="01966801-f9ee-7248-a706-41de00a8a998",
|
|
1839
|
-
run_id="RUN_ID",
|
|
1840
|
-
request_records={"context_quality": 0.914, "sensitivity": 0.85},
|
|
1841
|
-
)
|
|
1842
|
-
```
|
|
1843
|
-
"""
|
|
1844
|
-
from ibm_watson_openscale.base_classes.watson_open_scale_v2 import (
|
|
1845
|
-
MonitorMeasurementRequest,
|
|
1846
|
-
Runs,
|
|
1847
|
-
)
|
|
1848
|
-
|
|
1849
|
-
measurement_request = MonitorMeasurementRequest(
|
|
1850
|
-
timestamp=datetime.datetime.now(datetime.timezone.utc).strftime(
|
|
1851
|
-
"%Y-%m-%dT%H:%M:%S.%fZ",
|
|
1852
|
-
),
|
|
1853
|
-
run_id=run_id,
|
|
1854
|
-
metrics=[request_records],
|
|
1855
|
-
)
|
|
1856
|
-
|
|
1857
|
-
self._wos_client.monitor_instances.add_measurements(
|
|
1858
|
-
monitor_instance_id=monitor_instance_id,
|
|
1859
|
-
monitor_measurement_request=[measurement_request],
|
|
1860
|
-
).result
|
|
1861
|
-
|
|
1862
|
-
run = Runs(watson_open_scale=self._wos_client)
|
|
1863
|
-
patch_payload = []
|
|
1864
|
-
patch_payload.append(self._get_patch_request_field("/status/state", "finished"))
|
|
1865
|
-
patch_payload.append(
|
|
1866
|
-
self._get_patch_request_field(
|
|
1867
|
-
"/status/completed_at",
|
|
1868
|
-
datetime.datetime.now(datetime.timezone.utc).strftime(
|
|
1869
|
-
"%Y-%m-%dT%H:%M:%S.%fZ",
|
|
1870
|
-
),
|
|
1871
|
-
),
|
|
1872
|
-
)
|
|
1873
|
-
|
|
1874
|
-
return run.update(
|
|
1875
|
-
monitor_instance_id=monitor_instance_id,
|
|
1876
|
-
monitoring_run_id=run_id,
|
|
1877
|
-
json_patch_operation=patch_payload,
|
|
1878
|
-
).result
|
|
1879
|
-
|
|
1880
|
-
# ===== Local Custom Metrics =====
|
|
1881
|
-
def add_local_metric_definition(
|
|
1882
|
-
self,
|
|
1883
|
-
name: str,
|
|
1884
|
-
metrics: List[WatsonxLocalMetric],
|
|
1885
|
-
subscription_id: str,
|
|
1886
|
-
) -> str:
|
|
1887
|
-
"""
|
|
1888
|
-
Creates a custom metric definition to compute metrics at the local (transaction) level for IBM watsonx.governance.
|
|
1889
|
-
|
|
1890
|
-
Args:
|
|
1891
|
-
name (str): The name of the custom transaction metric group.
|
|
1892
|
-
metrics (List[WatsonxLocalMetric]): A list of metrics to be monitored at the local (transaction) level.
|
|
1893
|
-
subscription_id (str): The IBM watsonx.governance subscription ID associated with the metric definition.
|
|
1894
|
-
|
|
1895
|
-
Example:
|
|
1896
|
-
```python
|
|
1897
|
-
from beekeeper.monitors.watsonx import WatsonxLocalMetric
|
|
1898
|
-
|
|
1899
|
-
wxgov_client.add_local_metric_definition(
|
|
1900
|
-
name="Custom LLM Local Metric",
|
|
1901
|
-
subscription_id="019674ca-0c38-745f-8e9b-58546e95174e",
|
|
1902
|
-
metrics=[
|
|
1903
|
-
WatsonxLocalMetric(name="context_quality", data_type="double")
|
|
1904
|
-
],
|
|
1905
|
-
)
|
|
1906
|
-
```
|
|
1907
|
-
"""
|
|
1908
|
-
from ibm_watson_openscale.base_classes.watson_open_scale_v2 import (
|
|
1909
|
-
LocationTableName,
|
|
1910
|
-
SparkStruct,
|
|
1911
|
-
SparkStructFieldPrimitive,
|
|
1912
|
-
Target,
|
|
1913
|
-
)
|
|
1914
|
-
|
|
1915
|
-
target = Target(target_id=subscription_id, target_type="subscription")
|
|
1916
|
-
data_mart_id = self._get_existing_data_mart()
|
|
1917
|
-
metrics = [SparkStructFieldPrimitive(**metric.to_dict()) for metric in metrics]
|
|
1918
|
-
|
|
1919
|
-
schema_fields = [
|
|
1920
|
-
SparkStructFieldPrimitive(
|
|
1921
|
-
name="scoring_id",
|
|
1922
|
-
type="string",
|
|
1923
|
-
nullable=False,
|
|
1924
|
-
),
|
|
1925
|
-
SparkStructFieldPrimitive(
|
|
1926
|
-
name="run_id",
|
|
1927
|
-
type="string",
|
|
1928
|
-
nullable=True,
|
|
1929
|
-
),
|
|
1930
|
-
SparkStructFieldPrimitive(
|
|
1931
|
-
name="computed_on",
|
|
1932
|
-
type="string",
|
|
1933
|
-
nullable=False,
|
|
1934
|
-
),
|
|
1935
|
-
]
|
|
1936
|
-
|
|
1937
|
-
schema_fields.extend(metrics)
|
|
1938
|
-
|
|
1939
|
-
data_schema = SparkStruct(type="struct", fields=schema_fields)
|
|
1940
|
-
|
|
1941
|
-
return self._wos_client.data_sets.add(
|
|
1942
|
-
target=target,
|
|
1943
|
-
name=name,
|
|
1944
|
-
type="custom",
|
|
1945
|
-
data_schema=data_schema,
|
|
1946
|
-
data_mart_id=data_mart_id,
|
|
1947
|
-
location=LocationTableName(
|
|
1948
|
-
table_name=name.lower().replace(" ", "_") + "_" + str(uuid.uuid4())[:8],
|
|
1949
|
-
),
|
|
1950
|
-
background_mode=False,
|
|
1951
|
-
).result.metadata.id
|
|
1952
|
-
|
|
1953
|
-
def publish_local_metrics(
|
|
1954
|
-
self,
|
|
1955
|
-
metric_instance_id: str,
|
|
1956
|
-
request_records: List[Dict],
|
|
1957
|
-
):
|
|
1958
|
-
"""
|
|
1959
|
-
Publishes computed custom metrics for a specific transaction record.
|
|
1960
|
-
|
|
1961
|
-
Args:
|
|
1962
|
-
metric_instance_id (str): The unique ID of the custom transaction metric.
|
|
1963
|
-
request_records (List[Dict]): A list of dictionaries containing the records to be stored.
|
|
1964
|
-
|
|
1965
|
-
Example:
|
|
1966
|
-
```python
|
|
1967
|
-
wxgov_client.publish_local_metrics(
|
|
1968
|
-
metric_instance_id="0196ad39-1b75-7e77-bddb-cc5393d575c2",
|
|
1969
|
-
request_records=[
|
|
1970
|
-
{
|
|
1971
|
-
"scoring_id": "304a9270-44a1-4c4d-bfd4-f756541011f8",
|
|
1972
|
-
"run_id": "RUN_ID",
|
|
1973
|
-
"computed_on": "payload",
|
|
1974
|
-
"context_quality": 0.786,
|
|
1975
|
-
}
|
|
1976
|
-
],
|
|
1977
|
-
)
|
|
1978
|
-
```
|
|
1979
|
-
"""
|
|
1980
|
-
return self._wos_client.data_sets.store_records(
|
|
1981
|
-
data_set_id=metric_instance_id,
|
|
1982
|
-
request_body=request_records,
|
|
1983
|
-
).result
|
|
1984
|
-
|
|
1985
|
-
def list_local_metrics(
|
|
1986
|
-
self,
|
|
1987
|
-
metric_instance_id: str,
|
|
1988
|
-
):
|
|
1989
|
-
"""
|
|
1990
|
-
Lists records from a custom local metric definition.
|
|
1991
|
-
|
|
1992
|
-
Args:
|
|
1993
|
-
metric_instance_id (str): The unique ID of the custom transaction metric.
|
|
1994
|
-
|
|
1995
|
-
Example:
|
|
1996
|
-
```python
|
|
1997
|
-
wxgov_client.list_local_metrics(
|
|
1998
|
-
metric_instance_id="0196ad47-c505-73c0-9d7b-91c082b697e3"
|
|
1999
|
-
)
|
|
2000
|
-
```
|
|
2001
|
-
"""
|
|
2002
|
-
return self._get_dataset_data(metric_instance_id)
|