beekeeper-ai 0.6.6__py3-none-any.whl → 1.0.1__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.
Files changed (41) hide show
  1. beekeeper/_bundle/__init__.py +0 -0
  2. beekeeper_ai-1.0.1.dist-info/METADATA +46 -0
  3. beekeeper_ai-1.0.1.dist-info/RECORD +5 -0
  4. {beekeeper_ai-0.6.6.dist-info → beekeeper_ai-1.0.1.dist-info}/WHEEL +1 -1
  5. beekeeper_ai-1.0.1.dist-info/licenses/LICENSE +176 -0
  6. beekeeper/__init__.py +0 -1
  7. beekeeper/core/document/__init__.py +0 -6
  8. beekeeper/core/document/schema.py +0 -97
  9. beekeeper/core/document_loaders/__init__.py +0 -5
  10. beekeeper/core/document_loaders/base.py +0 -24
  11. beekeeper/core/embeddings/__init__.py +0 -6
  12. beekeeper/core/embeddings/base.py +0 -44
  13. beekeeper/core/text_splitters/utils.py +0 -142
  14. beekeeper/core/utils/pairwise.py +0 -20
  15. beekeeper/document_loaders/__init__.py +0 -17
  16. beekeeper/document_loaders/directory.py +0 -65
  17. beekeeper/document_loaders/docx.py +0 -31
  18. beekeeper/document_loaders/html.py +0 -77
  19. beekeeper/document_loaders/json.py +0 -53
  20. beekeeper/document_loaders/pdf.py +0 -38
  21. beekeeper/document_loaders/s3.py +0 -72
  22. beekeeper/document_loaders/watson_discovery.py +0 -121
  23. beekeeper/embeddings/__init__.py +0 -7
  24. beekeeper/embeddings/huggingface.py +0 -66
  25. beekeeper/embeddings/watsonx.py +0 -100
  26. beekeeper/evaluation/__init__.py +0 -5
  27. beekeeper/evaluation/knowledge_base_coverage.py +0 -62
  28. beekeeper/monitor/__init__.py +0 -11
  29. beekeeper/monitor/watsonx.py +0 -843
  30. beekeeper/retrievers/__init__.py +0 -5
  31. beekeeper/retrievers/watson_discovery.py +0 -121
  32. beekeeper/text_splitters/__init__.py +0 -9
  33. beekeeper/text_splitters/semantic.py +0 -139
  34. beekeeper/text_splitters/sentence.py +0 -107
  35. beekeeper/text_splitters/token.py +0 -101
  36. beekeeper/vector_stores/__init__.py +0 -7
  37. beekeeper/vector_stores/chroma.py +0 -115
  38. beekeeper/vector_stores/elasticsearch.py +0 -183
  39. beekeeper_ai-0.6.6.dist-info/LICENSE +0 -7
  40. beekeeper_ai-0.6.6.dist-info/METADATA +0 -49
  41. beekeeper_ai-0.6.6.dist-info/RECORD +0 -37
@@ -1,843 +0,0 @@
1
- import json
2
- import logging
3
- import uuid
4
- from typing import Any, List, Literal
5
-
6
- logging.getLogger("ibm_watsonx_ai.client").setLevel(logging.ERROR)
7
- logging.getLogger("ibm_watsonx_ai.wml_resource").setLevel(logging.ERROR)
8
-
9
- REGIONS_URL = {
10
- "us-south": {"wml": "https://us-south.ml.cloud.ibm.com",
11
- "wos": "https://api.aiopenscale.cloud.ibm.com",
12
- "factsheet": None},
13
- "eu-de": {"wml": "https://eu-de.ml.cloud.ibm.com",
14
- "wos": "https://eu-de.api.aiopenscale.cloud.ibm.com",
15
- "factsheet": "frankfurt"},
16
- "au-syd": {"wml": "https://au-syd.ml.cloud.ibm.com",
17
- "wos": "https://au-syd.api.aiopenscale.cloud.ibm.com",
18
- "factsheet": "sydney"},
19
- }
20
-
21
- def _filter_dict(original_dict: dict, optional_keys: List, required_keys: List = []):
22
- """Filters a dictionary to keep only the specified keys and check required.
23
-
24
- Args:
25
- original_dict (dict): The original dictionary
26
- optional_keys (list): A list of keys to keep
27
- required_keys (list, optional): A list of keys that must exist in the dictionary
28
- """
29
- # Ensure all required keys are in the source dictionary
30
- missing_keys = [key for key in required_keys if key not in original_dict]
31
- if missing_keys:
32
- raise KeyError(f"Missing required parameter: {missing_keys}")
33
-
34
- all_keys_to_keep = set(required_keys + optional_keys)
35
-
36
- # Create a new dictionary with only the key-value pairs where the key is in 'keys' and value is not None
37
- return {key: original_dict[key] for key in all_keys_to_keep if key in original_dict and original_dict[key] is not None}
38
-
39
- def _convert_payload_format(records: List[dict], feature_fields: List[str]) -> List[dict]:
40
-
41
- payload_data = []
42
- response_fields = ["generated_text", "input_token_count", "generated_token_count"]
43
-
44
- for record in records:
45
- request = { "parameters": { "template_variables": {}}}
46
- results = {}
47
-
48
- request["parameters"]["template_variables"] = {field: str(record.get(field, "")) for field in feature_fields}
49
-
50
- results = {field: record.get(field) for field in response_fields if record.get(field)}
51
-
52
- pl_record = {"request": request, "response": {"results": [results]}}
53
- payload_data.append(pl_record)
54
-
55
- return payload_data
56
-
57
-
58
- class CloudPakforDataCredentials:
59
- """Encapsulate passed credentials for CloudPakforData.
60
-
61
- Args:
62
- url (str): Host URL of Cloud Pak for Data environment.
63
- api_key (str, optional): Environment api_key if IAM enabled.
64
- username (str, optional): Environment username.
65
- password (str, optional): Environment password.
66
- bedrock_url (str, optional): Bedrock URL. This url is required only when iam-integration is enabled on CP4D 4.0.x cluster.
67
- instance_id (str, optional): Instance ID.
68
- version (str, optional): CPD Version.
69
- disable_ssl_verification (bool, optional): Indicates whether verification of the server's SSL certificate. Defaults to ``True``.
70
- """
71
-
72
- def __init__(self,
73
- url: str,
74
- api_key: str = None,
75
- username: str = None,
76
- password: str = None,
77
- bedrock_url: str = None,
78
- instance_id: Literal["icp","openshift"] = None,
79
- version: str = None,
80
- disable_ssl_verification: bool = True) -> None:
81
-
82
- self.url = url
83
- self.api_key = api_key
84
- self.username = username
85
- self.api_key = api_key
86
- self.password = password
87
- self.bedrock_url = bedrock_url
88
- self.instance_id = instance_id
89
- self.api_key = api_key
90
- self.version = version
91
- self.disable_ssl_verification = disable_ssl_verification
92
-
93
- def to_dict(self) -> dict[str, Any]:
94
- data = dict([(k, v) for k, v in self.__dict__.items()])
95
-
96
- if "instance_id" in data and self.instance_id.lower() not in ["icp","openshift"]:
97
- data.pop("instance_id")
98
-
99
- return data
100
-
101
-
102
- class WatsonxExternalPromptMonitoring:
103
- """Provides functionality to interact with IBM watsonx.governance for monitoring external LLM's.
104
-
105
- Note:
106
- One of these parameters is required to create prompt monitor: ``project_id`` or ``space_id``. Not both.
107
-
108
- Args:
109
- api_key (str): IBM watsonx.governance API key.
110
- space_id (str, optional): watsonx.governance space_id.
111
- project_id (str, optional): watsonx.governance project_id.
112
- region (str, optional): Region where the watsonx.governance is hosted when using IBM Cloud. Defaults to ``us-south``
113
- cpd_creds (CloudPakforDataCredentials, optional): Cloud Pak for Data environment details.
114
-
115
- **Example**
116
-
117
- .. code-block:: python
118
-
119
- from beekeeper.monitor import WatsonxExternalPromptMonitoring
120
-
121
- # watsonx.governance (IBM Cloud)
122
- detached_watsonx_monitor = WatsonxExternalPromptMonitoring(api_key="your_api_key",
123
- space_id="your_space_id")
124
-
125
- # watsonx.governance (cp4d)
126
- from beekeeper.monitor import CloudPakforDataCredentials
127
-
128
- cpd_creds = CloudPakforDataCredentials(url="your_cpd_url",
129
- username="your_username", password="your_password",
130
- version="5.0", instance_id="openshift")
131
-
132
- detached_watsonx_monitor = WatsonxExternalPromptMonitoring(space_id="your_space_id"
133
- cpd_creds=cpd_creds)
134
- """
135
-
136
- def __init__(self,
137
- api_key: str = None,
138
- space_id: str = None,
139
- project_id: str = None,
140
- region: Literal["us-south", "eu-de", "au-syd"] = "us-south",
141
- cpd_creds: CloudPakforDataCredentials | dict = None,
142
- ) -> None:
143
-
144
- try:
145
- import ibm_aigov_facts_client # noqa: F401
146
- import ibm_cloud_sdk_core.authenticators # noqa: F401
147
- import ibm_watson_openscale # noqa: F401
148
- import ibm_watsonx_ai # noqa: F401
149
-
150
- except ImportError:
151
- raise ImportError("""ibm-aigov-facts-client, ibm-watson-openscale or ibm-watsonx-ai module not found,
152
- please install it with `pip install ibm-aigov-facts-client ibm-watson-openscale ibm-watsonx-ai`""")
153
-
154
- if (not (project_id or space_id)) or (project_id and space_id):
155
- raise ValueError("`project_id` and `space_id` parameter cannot be set at the same time.")
156
-
157
- self.space_id = space_id
158
- self.project_id = project_id
159
- self.region = region
160
- self._api_key = api_key
161
- self._wos_client = None
162
-
163
- self._container_id = space_id if space_id else project_id
164
- self._container_type = "space" if space_id else "project"
165
- self._deployment_stage = "production" if space_id else "development"
166
-
167
- if cpd_creds:
168
- self._wos_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key",
169
- "disable_ssl_verification"], ["url"])
170
- self._fact_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key",
171
- "bedrock_url"],["url"])
172
- self._fact_cpd_creds["service_url"] = self._fact_cpd_creds.pop("url")
173
- self._wml_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key", "instance_id",
174
- "version", "bedrock_url"], ["url"])
175
-
176
-
177
- def _create_detached_prompt(self, detached_details: dict,
178
- prompt_template_details: dict,
179
- detached_asset_details: dict) -> str:
180
- from ibm_aigov_facts_client import ( # type: ignore
181
- AIGovFactsClient,
182
- CloudPakforDataConfig,
183
- DetachedPromptTemplate,
184
- PromptTemplate,
185
- )
186
-
187
- try:
188
- if hasattr(self, '_fact_cpd_creds') and self._fact_cpd_creds:
189
- cpd_creds = CloudPakforDataConfig(**self._fact_cpd_creds)
190
-
191
- aigov_client = AIGovFactsClient(
192
- container_id=self._container_id,
193
- container_type=self._container_type,
194
- cloud_pak_for_data_configs=cpd_creds,
195
- disable_tracing=True)
196
-
197
- else:
198
- aigov_client = AIGovFactsClient(
199
- api_key=self._api_key,
200
- container_id=self._container_id,
201
- container_type=self._container_type,
202
- disable_tracing=True,
203
- region=REGIONS_URL[self.region]["factsheet"])
204
-
205
- except Exception as e:
206
- logging.error(f"Error connecting to IBM watsonx.governance (factsheets): {e}")
207
- raise
208
-
209
- created_detached_pta = aigov_client.assets.create_detached_prompt(
210
- **detached_asset_details,
211
- prompt_details=PromptTemplate(**prompt_template_details),
212
- detached_information=DetachedPromptTemplate(**detached_details))
213
-
214
- return created_detached_pta.to_dict()["asset_id"]
215
-
216
-
217
- def _create_deployment_pta(self, asset_id: str,
218
- name: str,
219
- model_id: str) -> str:
220
- from ibm_watsonx_ai import APIClient, Credentials # type: ignore
221
-
222
- try:
223
- if hasattr(self, '_wml_cpd_creds') and self._wml_cpd_creds:
224
- creds = Credentials(**self._wml_cpd_creds)
225
-
226
- wml_client = APIClient(creds)
227
- wml_client.set.default_space(self.space_id)
228
-
229
- else:
230
- creds = Credentials(url= REGIONS_URL[self.region]["wml"], api_key=self._api_key)
231
- wml_client = APIClient(creds)
232
- wml_client.set.default_space(self.space_id)
233
-
234
- except Exception as e:
235
- logging.error(f"Error connecting to IBM watsonx.ai Runtime: {e}")
236
- raise
237
-
238
- meta_props = {
239
- wml_client.deployments.ConfigurationMetaNames.PROMPT_TEMPLATE: { "id" : asset_id },
240
- wml_client.deployments.ConfigurationMetaNames.DETACHED: {},
241
- wml_client.deployments.ConfigurationMetaNames.NAME: name + " " + "deployment",
242
- wml_client.deployments.ConfigurationMetaNames.BASE_MODEL_ID: model_id
243
- }
244
-
245
- created_deployment = wml_client.deployments.create(asset_id, meta_props)
246
-
247
- return wml_client.deployments.get_uid(created_deployment)
248
-
249
-
250
- def create_prompt_monitor(self,
251
- name: str,
252
- model_id: str,
253
- task_id: Literal["retrieval_augmented_generation", "summarization"],
254
- detached_model_provider: str,
255
- description: str = "",
256
- model_parameters: dict = None,
257
- detached_model_name: str = None,
258
- detached_model_url: str = None,
259
- detached_prompt_url: str = None,
260
- detached_prompt_additional_info: dict = None,
261
- prompt_variables: List[str] = None,
262
- prompt_template_version: str = None,
263
- prompt_instruction: str = None,
264
- input_text: str = None,
265
- input_prefix: str = None,
266
- output_prefix: str = None,
267
- context_fields: List[str] = None,
268
- question_field: str = None) -> dict:
269
- """Create a Detached/External Prompt Template Asset and setup monitors for a given prompt template asset.
270
-
271
- Args:
272
- name (str): The name of the External Prompt Template Asset..
273
- model_id (str): Id of the model associated with the prompt.
274
- task_id (str): The task identifier. Currently supports "retrieval_augmented_generation" and "summarization" tasks.
275
- detached_model_provider (str): The external model provider.
276
- description (str, optional): Description of the External Prompt Template Asset.
277
- model_parameters (dict, optional): Model parameters and their respective values.
278
- detached_model_name (str, optional): The name of the external model.
279
- detached_model_url (str, optional): URL of the external model.
280
- detached_prompt_url (str, optional): URL of the external prompt.
281
- detached_prompt_additional_info (dict, optional): Additional information related to the external prompt.
282
- prompt_variables (List[str], optional): Values for prompt variables.
283
- prompt_template_version (str, optional): Semantic version of the External Prompt Template Asset.
284
- prompt_instruction (str, optional): Instruction for using the prompt.
285
- input_text (str, optional): The input text for the prompt.
286
- input_prefix (str, optional): A prefix to add to the input.
287
- output_prefix (str, optional): A prefix to add to the output.
288
- context_fields (List[str], optional): A list of fields that will provide context to the prompt. Applicable only for ``retrieval_augmented_generation`` problem type.
289
- question_field (str, optional): The field containing the question to be answered. Applicable only for ``retrieval_augmented_generation`` problem type.
290
-
291
- **Example**
292
-
293
- .. code-block:: python
294
-
295
- detached_watsonx_monitor.create_prompt_monitor(name="Detached prompt (model AWS Anthropic)",
296
- model_id="anthropic.claude-v2",
297
- task_id="retrieval_augmented_generation",
298
- detached_model_provider="AWS Bedrock",
299
- detached_model_name="Anthropic Claude 2.0",
300
- detached_model_url="https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html",
301
- prompt_variables=["context1", "context2", "input_query"],
302
- input_text="Prompt text to be given",
303
- context_fields=["context1", "context2"],
304
- question_field="input_query")
305
-
306
- """
307
- prompt_metadata = locals()
308
- # remove unused vars from dict
309
- prompt_metadata.pop("self", None)
310
- prompt_metadata.pop("context_fields", None)
311
- prompt_metadata.pop("question_field", None)
312
-
313
- # update name of keys to aigov_facts api
314
- prompt_metadata["model_version"] = prompt_metadata.pop("prompt_template_version", None)
315
- prompt_metadata["input"] = prompt_metadata.pop("input_text", None)
316
- prompt_metadata["model_provider"] = prompt_metadata.pop("detached_model_provider", None)
317
- prompt_metadata["model_name"] = prompt_metadata.pop("detached_model_name", None)
318
- prompt_metadata["model_url"] = prompt_metadata.pop("detached_model_url", None)
319
- prompt_metadata["prompt_url"] = prompt_metadata.pop("detached_prompt_url", None)
320
- prompt_metadata["prompt_additional_info"] = prompt_metadata.pop("detached_prompt_additional_info", None)
321
-
322
- # update list of vars to dict
323
- prompt_metadata["prompt_variables"] = { prompt_var: "" for prompt_var in prompt_metadata["prompt_variables"] }
324
-
325
- from ibm_watson_openscale import APIClient as WosAPIClient # type: ignore
326
-
327
- if not self._wos_client:
328
- try:
329
- if hasattr(self, '_wos_cpd_creds') and self._wos_cpd_creds:
330
- from ibm_cloud_sdk_core.authenticators import (
331
- CloudPakForDataAuthenticator, # type: ignore
332
- )
333
-
334
- authenticator = CloudPakForDataAuthenticator(**self._wos_cpd_creds)
335
- self._wos_client = WosAPIClient(authenticator=authenticator,
336
- service_url=self._wos_cpd_creds["url"])
337
-
338
- else:
339
- from ibm_cloud_sdk_core.authenticators import (
340
- IAMAuthenticator, # type: ignore
341
- )
342
-
343
- authenticator = IAMAuthenticator(apikey=self._api_key)
344
- self._wos_client = WosAPIClient(authenticator=authenticator, service_url=REGIONS_URL[self.region]["wos"])
345
-
346
- except Exception as e:
347
- logging.error(f"Error connecting to IBM watsonx.governance (openscale): {e}")
348
- raise
349
-
350
- detached_details = _filter_dict(prompt_metadata,
351
- ["model_name", "model_url", "prompt_url", "prompt_additional_info"],
352
- ["model_id", "model_provider"])
353
- detached_details["prompt_id"] = "detached_prompt_" + str(uuid.uuid4())
354
-
355
- prompt_details = _filter_dict(prompt_metadata,
356
- ["model_version", "prompt_variables", "prompt_instruction",
357
- "input_prefix", "output_prefix", "input", "model_parameters"])
358
-
359
- detached_asset_details = _filter_dict(prompt_metadata, ["description"],
360
- ["name", "model_id", "task_id"])
361
-
362
- detached_pta_id = self._create_detached_prompt(detached_details, prompt_details, detached_asset_details)
363
- deployment_id = None
364
- if self._container_type == "space":
365
- deployment_id = self._create_deployment_pta(detached_pta_id, name, model_id)
366
-
367
- monitors = {
368
- "generative_ai_quality": {
369
- "parameters": {
370
- "min_sample_size": 10,
371
- "metrics_configuration":{}
372
- }
373
- }}
374
-
375
- max_attempt_execute_prompt_setup = 0
376
- while max_attempt_execute_prompt_setup < 2:
377
- try:
378
- generative_ai_monitor_details = self._wos_client.wos.execute_prompt_setup(
379
- prompt_template_asset_id = detached_pta_id,
380
- space_id = self.space_id,
381
- project_id=self.project_id,
382
- deployment_id = deployment_id,
383
- label_column = "reference_output",
384
- context_fields=context_fields,
385
- question_field = question_field,
386
- operational_space_id = self._deployment_stage,
387
- problem_type = task_id,
388
- input_data_type = "unstructured_text",
389
- supporting_monitors = monitors,
390
- background_mode = False).result
391
-
392
- break
393
-
394
- except Exception as e:
395
- if e.code == 403 and "The user entitlement does not exist" in e.message \
396
- and max_attempt_execute_prompt_setup < 1:
397
- max_attempt_execute_prompt_setup = max_attempt_execute_prompt_setup + 1
398
-
399
- data_marts = self._wos_client.data_marts.list().result
400
- if (data_marts.data_marts is None) or (not data_marts.data_marts):
401
- raise ValueError("Error retrieving IBM watsonx.governance (openscale) data mart. \
402
- Make sure the data mart are configured.")
403
-
404
- data_mart_id = data_marts.data_marts[0].metadata.id
405
-
406
- self._wos_client.wos.add_instance_mapping(
407
- service_instance_id=data_mart_id,
408
- space_id=self.space_id,
409
- project_id=self.project_id)
410
- else:
411
- max_attempt_execute_prompt_setup = 2
412
- raise e
413
-
414
- generative_ai_monitor_details = generative_ai_monitor_details._to_dict()
415
-
416
- return {"detached_prompt_template_asset_id": detached_pta_id,
417
- "deployment_id": deployment_id,
418
- "subscription_id": generative_ai_monitor_details["subscription_id"]}
419
-
420
-
421
- def payload_logging(self, payload_records: List[dict], subscription_id: str) -> None:
422
- """Store records to payload logging.
423
-
424
- Args:
425
- payload_records (List[dict]):
426
- subscription_id (str):
427
-
428
- **Example**
429
-
430
- .. code-block:: python
431
-
432
- detached_watsonx_monitor.payload_logging(payload_records=[{"context1": "value_context1",
433
- "context2": "value_context1",
434
- "input_query": "What's Beekeeper?",
435
- "input_token_count": 25,
436
- "generated_token_count": 150}],
437
- subscription_id="5d62977c-a53d-4b6d-bda1-7b79b3b9d1a0")
438
- """
439
- from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
440
- from ibm_watson_openscale import APIClient as WosAPIClient
441
- from ibm_watson_openscale.supporting_classes.enums import (
442
- DataSetTypes,
443
- TargetTypes,
444
- )
445
-
446
- if not self._wos_client:
447
- try:
448
- if hasattr(self, '_wos_cpd_creds') and self._wos_cpd_creds:
449
- from ibm_cloud_sdk_core.authenticators import (
450
- CloudPakForDataAuthenticator, # type: ignore
451
- )
452
-
453
- authenticator = CloudPakForDataAuthenticator(**self._wos_cpd_creds)
454
- self._wos_client = WosAPIClient(authenticator=authenticator,
455
- service_url=self._wos_cpd_creds["url"])
456
-
457
- else:
458
- from ibm_cloud_sdk_core.authenticators import (
459
- IAMAuthenticator, # type: ignore
460
- )
461
-
462
- authenticator = IAMAuthenticator(apikey=self._api_key)
463
- self._wos_client = WosAPIClient(authenticator=authenticator, service_url=REGIONS_URL[self.region]["wos"])
464
-
465
- except Exception as e:
466
- logging.error(f"Error connecting to IBM watsonx.governance (openscale): {e}")
467
- raise
468
-
469
- subscription_details = self._wos_client.subscriptions.get(subscription_id).result
470
- subscription_details = json.loads(str(subscription_details))
471
-
472
- feature_fields = subscription_details["entity"]["asset_properties"]["feature_fields"]
473
-
474
- payload_data_set_id = self._wos_client.data_sets.list(type=DataSetTypes.PAYLOAD_LOGGING,
475
- target_target_id=subscription_id,
476
- target_target_type=TargetTypes.SUBSCRIPTION).result.data_sets[0].metadata.id
477
-
478
- payload_data = _convert_payload_format(payload_records, feature_fields)
479
- self._wos_client.data_sets.store_records(data_set_id=payload_data_set_id,
480
- request_body=payload_data,
481
- background_mode=False)
482
-
483
-
484
- class WatsonxPromptMonitoring:
485
- """Provides functionality to interact with IBM watsonx.governance for monitoring IBM watsonx.ai LLM's.
486
-
487
- Note:
488
- One of these parameters is required to create prompt monitor: ``project_id`` or ``space_id``. Not both.
489
-
490
- Args:
491
- api_key (str): IBM watsonx.governance API key.
492
- space_id (str, optional): watsonx.governance space_id.
493
- project_id (str, optional): watsonx.governance project_id.
494
- region (str, optional): Region where the watsonx.governance is hosted when using IBM Cloud. Defaults to ``us-south``
495
- cpd_creds (CloudPakforDataCredentials, optional): Cloud Pak for Data environment details.
496
-
497
- **Example**
498
-
499
- .. code-block:: python
500
-
501
- from beekeeper.monitor import WatsonxPromptMonitoring
502
-
503
- # watsonx.governance (IBM Cloud)
504
- watsonx_monitor = WatsonxExternalPromptMonitoring(api_key="your_api_key",
505
- space_id="your_space_id")
506
-
507
- # watsonx.governance (cp4d)
508
- from beekeeper.monitor import CloudPakforDataCredentials
509
-
510
- cpd_creds = CloudPakforDataCredentials(url="your_cpd_url",
511
- username="your_username", password="your_password",
512
- version="5.0", instance_id="openshift")
513
-
514
- detached_watsonx_monitor = WatsonxExternalPromptMonitoring(space_id="your_space_id"
515
- cpd_creds=cpd_creds)
516
- """
517
-
518
- def __init__(self,
519
- api_key: str =None,
520
- space_id: str = None,
521
- project_id: str = None,
522
- region: Literal["us-south", "eu-de", "au-syd"] = "us-south",
523
- cpd_creds: CloudPakforDataCredentials | dict = None,
524
- ) -> None:
525
- try:
526
- import ibm_aigov_facts_client # noqa: F401
527
- import ibm_cloud_sdk_core.authenticators # noqa: F401
528
- import ibm_watson_openscale # noqa: F401
529
- import ibm_watsonx_ai # noqa: F401
530
-
531
- except ImportError:
532
- raise ImportError("""ibm-aigov-facts-client, ibm-watson-openscale or ibm-watsonx-ai module not found,
533
- please install it with `pip install ibm-aigov-facts-client ibm-watson-openscale ibm-watsonx-ai`""")
534
-
535
- if (not (project_id or space_id)) or (project_id and space_id):
536
- raise ValueError("`project_id` and `space_id` parameter cannot be set at the same time.")
537
-
538
- self.space_id = space_id
539
- self.project_id = project_id
540
- self.region = region
541
- self._api_key = api_key
542
- self._wos_client = None
543
-
544
- self._container_id = space_id if space_id else project_id
545
- self._container_type = "space" if space_id else "project"
546
- self._deployment_stage = "production" if space_id else "development"
547
-
548
- if cpd_creds:
549
- self._wos_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key",
550
- "disable_ssl_verification"], ["url"])
551
- self._fact_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key",
552
- "bedrock_url"],["url"])
553
- self._fact_cpd_creds["service_url"] = self._fact_cpd_creds.pop("url")
554
- self._wml_cpd_creds = _filter_dict(cpd_creds.to_dict(), ["username", "password", "api_key", "instance_id",
555
- "version", "bedrock_url"], ["url"])
556
-
557
-
558
- def _create_prompt_template(self, prompt_template_details: dict, asset_details: dict) -> str:
559
- from ibm_aigov_facts_client import (
560
- AIGovFactsClient,
561
- CloudPakforDataConfig,
562
- PromptTemplate,
563
- )
564
-
565
- try:
566
- if hasattr(self, '_fact_cpd_creds') and self._fact_cpd_creds:
567
- cpd_creds = CloudPakforDataConfig(**self._fact_cpd_creds)
568
-
569
- aigov_client = AIGovFactsClient(
570
- container_id=self._container_id,
571
- container_type=self._container_type,
572
- cloud_pak_for_data_configs=cpd_creds,
573
- disable_tracing=True)
574
-
575
- else:
576
- aigov_client = AIGovFactsClient(
577
- api_key=self._api_key,
578
- container_id=self._container_id,
579
- container_type=self._container_type,
580
- disable_tracing=True,
581
- region=REGIONS_URL[self.region]["factsheet"])
582
-
583
- except Exception as e:
584
- logging.error(f"Error connecting to IBM watsonx.governance (factsheets): {e}")
585
- raise
586
-
587
- created_pta = aigov_client.assets.create_prompt(
588
- **asset_details,
589
- input_mode="structured",
590
- prompt_details=PromptTemplate(**prompt_template_details))
591
-
592
- return created_pta.to_dict()["asset_id"]
593
-
594
-
595
- def _create_deployment_pta(self, asset_id: str,
596
- name: str,
597
- model_id: str) -> str:
598
- from ibm_watsonx_ai import APIClient, Credentials # type: ignore
599
-
600
- try:
601
- if hasattr(self, '_wml_cpd_creds') and self._wml_cpd_creds:
602
- creds = Credentials(**self._wml_cpd_creds)
603
-
604
- wml_client = APIClient(creds)
605
- wml_client.set.default_space(self.space_id)
606
-
607
- else:
608
- creds = Credentials(url= REGIONS_URL[self.region]["wml"], api_key=self._api_key)
609
-
610
- wml_client = APIClient(creds)
611
- wml_client.set.default_space(self.space_id)
612
-
613
- except Exception as e:
614
- logging.error(f"Error connecting to IBM watsonx.ai Runtime: {e}")
615
- raise
616
-
617
- meta_props = {
618
- wml_client.deployments.ConfigurationMetaNames.PROMPT_TEMPLATE: { "id" : asset_id },
619
- wml_client.deployments.ConfigurationMetaNames.FOUNDATION_MODEL: {},
620
- wml_client.deployments.ConfigurationMetaNames.NAME: name + " " + "deployment",
621
- wml_client.deployments.ConfigurationMetaNames.BASE_MODEL_ID: model_id
622
- }
623
-
624
- created_deployment = wml_client.deployments.create(asset_id, meta_props)
625
-
626
- return wml_client.deployments.get_uid(created_deployment)
627
-
628
-
629
- def create_prompt_monitor(self,
630
- name: str,
631
- model_id: str,
632
- task_id: Literal["retrieval_augmented_generation", "summarization"],
633
- description: str = "",
634
- model_parameters: dict = None,
635
- prompt_variables: List[str] = None,
636
- prompt_template_version: str = None,
637
- prompt_instruction: str = None,
638
- input_text: str = None,
639
- input_prefix: str = None,
640
- output_prefix: str = None,
641
- context_fields: List[str] = None,
642
- question_field: str = None,
643
- ) -> dict:
644
- """Create an IBM Prompt Template Asset and setup monitors for a given prompt template asset.
645
-
646
- Args:
647
- name (str): The name of the Prompt Template Asset.
648
- model_id (str): Id of the model associated with the prompt.
649
- task_id (str): The task identifier. Currently supports "retrieval_augmented_generation" and "summarization" tasks.
650
- description (str, optional): Description of the Prompt Template Asset.
651
- model_parameters (dict, optional): Model parameters and their respective values.
652
- prompt_variables (List[str], optional): Values for prompt input variables.
653
- prompt_template_version (str, optional): Semantic version of the Prompt Template Asset.
654
- prompt_instruction (str, optional): Instruction for using the prompt.
655
- input_text (str, optional): The input text for the prompt.
656
- input_prefix (str, optional): A prefix to add to the input.
657
- output_prefix (str, optional): A prefix to add to the output.
658
- context_fields (List[str], optional): A list of fields that will provide context to the prompt. Applicable only for ``retrieval_augmented_generation`` problem type.
659
- question_field (str, optional): The field containing the question to be answered. Applicable only for ``retrieval_augmented_generation`` problem type.
660
-
661
- **Example**
662
-
663
- .. code-block:: python
664
-
665
- watsonx_monitor.create_prompt_monitor(name="IBM prompt template",
666
- model_id="ibm/granite-3-2b-instruct",
667
- task_id="retrieval_augmented_generation",
668
- prompt_variables=["context1", "context2", "input_query"],
669
- input_text="Prompt text to be given",
670
- context_fields=["context1", "context2"],
671
- question_field="input_query")
672
-
673
- """
674
- prompt_metadata = locals()
675
- # remove unused vars from dict
676
- prompt_metadata.pop("self", None)
677
- prompt_metadata.pop("context_fields", None)
678
- prompt_metadata.pop("question_field", None)
679
-
680
- # update name of keys to aigov_facts api
681
- prompt_metadata["model_version"] = prompt_metadata.pop("prompt_template_version", None)
682
- prompt_metadata["input"] = prompt_metadata.pop("input_text", None)
683
-
684
- # update list of vars to dict
685
- prompt_metadata["prompt_variables"] = { prompt_var: "" for prompt_var in prompt_metadata["prompt_variables"] }
686
-
687
- from ibm_cloud_sdk_core.authenticators import IAMAuthenticator # type: ignore
688
- from ibm_watson_openscale import APIClient as WosAPIClient # type: ignore
689
-
690
- if not self._wos_client:
691
- try:
692
- if hasattr(self, '_wos_cpd_creds') and self._wos_cpd_creds:
693
- from ibm_cloud_sdk_core.authenticators import (
694
- CloudPakForDataAuthenticator, # type: ignore
695
- )
696
-
697
- authenticator = CloudPakForDataAuthenticator(**self._wos_cpd_creds)
698
-
699
- self._wos_client = WosAPIClient(authenticator=authenticator,
700
- service_url=self._wos_cpd_creds["url"])
701
-
702
- else:
703
- from ibm_cloud_sdk_core.authenticators import (
704
- IAMAuthenticator, # type: ignore
705
- )
706
-
707
- authenticator = IAMAuthenticator(apikey=self._api_key)
708
- self._wos_client = WosAPIClient(authenticator=authenticator, service_url=REGIONS_URL[self.region]["wos"])
709
-
710
- except Exception as e:
711
- logging.error(f"Error connecting to IBM watsonx.governance (openscale): {e}")
712
- raise
713
-
714
- prompt_details = _filter_dict(prompt_metadata,
715
- ["model_version", "prompt_variables", "prompt_instruction",
716
- "input_prefix", "output_prefix", "input", "model_parameters"])
717
-
718
- asset_details = _filter_dict(prompt_metadata, ["description"],
719
- ["name", "model_id", "task_id"])
720
-
721
- pta_id = self._create_prompt_template(prompt_details, asset_details)
722
- deployment_id = None
723
- if self._container_type == "space":
724
- deployment_id = self._create_deployment_pta(pta_id, name, model_id)
725
-
726
- monitors = {
727
- "generative_ai_quality": {
728
- "parameters": {
729
- "min_sample_size": 10,
730
- "metrics_configuration":{}
731
- }
732
- }}
733
-
734
- max_attempt_execute_prompt_setup = 0
735
- while max_attempt_execute_prompt_setup < 2:
736
- try:
737
- generative_ai_monitor_details = self._wos_client.wos.execute_prompt_setup(
738
- prompt_template_asset_id = pta_id,
739
- space_id = self.space_id,
740
- project_id=self.project_id,
741
- deployment_id = deployment_id,
742
- label_column = "reference_output",
743
- context_fields=context_fields,
744
- question_field = question_field,
745
- operational_space_id = self._deployment_stage,
746
- problem_type = task_id,
747
- input_data_type = "unstructured_text",
748
- supporting_monitors = monitors,
749
- background_mode = False).result
750
-
751
- break
752
-
753
- except Exception as e:
754
- if e.code == 403 and "The user entitlement does not exist" in e.message \
755
- and max_attempt_execute_prompt_setup < 1:
756
- max_attempt_execute_prompt_setup = max_attempt_execute_prompt_setup + 1
757
-
758
- data_marts = self._wos_client.data_marts.list().result
759
- if (data_marts.data_marts is None) or (not data_marts.data_marts):
760
- raise ValueError("Error retrieving IBM watsonx.governance (openscale) data mart. \
761
- Make sure the data mart are configured.")
762
-
763
- data_mart_id = data_marts.data_marts[0].metadata.id
764
-
765
- self._wos_client.wos.add_instance_mapping(
766
- service_instance_id=data_mart_id,
767
- space_id=self.space_id,
768
- project_id=self.project_id)
769
- else:
770
- max_attempt_execute_prompt_setup = 2
771
- raise e
772
-
773
- generative_ai_monitor_details = generative_ai_monitor_details._to_dict()
774
-
775
- return {"prompt_template_asset_id": pta_id,
776
- "deployment_id": deployment_id,
777
- "subscription_id": generative_ai_monitor_details["subscription_id"]}
778
-
779
-
780
- def payload_logging(self, payload_records: List[dict], subscription_id: str) -> None:
781
- """Store records to payload logging.
782
-
783
- Args:
784
- payload_records (List[dict]):
785
- subscription_id (str):
786
-
787
- **Example**
788
-
789
- .. code-block:: python
790
-
791
- watsonx_monitor.payload_logging(payload_records=[{"context1": "value_context1",
792
- "context2": "value_context1",
793
- "input_query": "What's Beekeeper?",
794
- "input_token_count": 25,
795
- "generated_token_count": 150}],
796
- subscription_id="5d62977c-a53d-4b6d-bda1-7b79b3b9d1a0")
797
- """
798
- from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
799
- from ibm_watson_openscale import APIClient as WosAPIClient
800
- from ibm_watson_openscale.supporting_classes.enums import (
801
- DataSetTypes,
802
- TargetTypes,
803
- )
804
-
805
- if not self._wos_client:
806
- try:
807
- if hasattr(self, '_wos_cpd_creds') and self._wos_cpd_creds:
808
- from ibm_cloud_sdk_core.authenticators import (
809
- CloudPakForDataAuthenticator, # type: ignore
810
- )
811
-
812
- authenticator = CloudPakForDataAuthenticator(**self._wos_cpd_creds)
813
-
814
- self._wos_client = WosAPIClient(authenticator=authenticator,
815
- service_url=self._wos_cpd_creds["url"])
816
-
817
- else:
818
- from ibm_cloud_sdk_core.authenticators import (
819
- IAMAuthenticator, # type: ignore
820
- )
821
-
822
- authenticator = IAMAuthenticator(apikey=self._api_key)
823
- self._wos_client = WosAPIClient(authenticator=authenticator, service_url=REGIONS_URL[self.region]["wos"])
824
-
825
- except Exception as e:
826
- logging.error(f"Error connecting to IBM watsonx.governance (openscale): {e}")
827
- raise
828
-
829
- subscription_details = self._wos_client.subscriptions.get(subscription_id).result
830
- subscription_details = json.loads(str(subscription_details))
831
-
832
- feature_fields = subscription_details["entity"]["asset_properties"]["feature_fields"]
833
-
834
- payload_data_set_id = self._wos_client.data_sets.list(type=DataSetTypes.PAYLOAD_LOGGING,
835
- target_target_id=subscription_id,
836
- target_target_type=TargetTypes.SUBSCRIPTION).result.data_sets[0].metadata.id
837
-
838
- payload_data = _convert_payload_format(payload_records, feature_fields)
839
- self._wos_client.data_sets.store_records(data_set_id=payload_data_set_id,
840
- request_body=payload_data,
841
- background_mode=False)
842
-
843
-