oracle-ads 2.12.8__py3-none-any.whl → 2.12.10rc0__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 (71) hide show
  1. ads/aqua/__init__.py +4 -4
  2. ads/aqua/app.py +12 -2
  3. ads/aqua/common/enums.py +3 -0
  4. ads/aqua/common/utils.py +62 -2
  5. ads/aqua/data.py +2 -19
  6. ads/aqua/evaluation/entities.py +6 -0
  7. ads/aqua/evaluation/evaluation.py +25 -3
  8. ads/aqua/extension/deployment_handler.py +8 -4
  9. ads/aqua/extension/finetune_handler.py +8 -14
  10. ads/aqua/extension/model_handler.py +25 -6
  11. ads/aqua/extension/ui_handler.py +13 -1
  12. ads/aqua/finetuning/constants.py +5 -2
  13. ads/aqua/finetuning/entities.py +70 -17
  14. ads/aqua/finetuning/finetuning.py +79 -82
  15. ads/aqua/model/entities.py +4 -1
  16. ads/aqua/model/model.py +95 -29
  17. ads/aqua/modeldeployment/deployment.py +13 -1
  18. ads/aqua/modeldeployment/entities.py +7 -4
  19. ads/aqua/ui.py +24 -2
  20. ads/common/auth.py +9 -9
  21. ads/llm/autogen/__init__.py +2 -0
  22. ads/llm/autogen/constants.py +15 -0
  23. ads/llm/autogen/reports/__init__.py +2 -0
  24. ads/llm/autogen/reports/base.py +67 -0
  25. ads/llm/autogen/reports/data.py +103 -0
  26. ads/llm/autogen/reports/session.py +526 -0
  27. ads/llm/autogen/reports/templates/chat_box.html +13 -0
  28. ads/llm/autogen/reports/templates/chat_box_lt.html +5 -0
  29. ads/llm/autogen/reports/templates/chat_box_rt.html +6 -0
  30. ads/llm/autogen/reports/utils.py +56 -0
  31. ads/llm/autogen/v02/__init__.py +4 -0
  32. ads/llm/autogen/{client_v02.py → v02/client.py} +23 -10
  33. ads/llm/autogen/v02/log_handlers/__init__.py +2 -0
  34. ads/llm/autogen/v02/log_handlers/oci_file_handler.py +83 -0
  35. ads/llm/autogen/v02/loggers/__init__.py +6 -0
  36. ads/llm/autogen/v02/loggers/metric_logger.py +320 -0
  37. ads/llm/autogen/v02/loggers/session_logger.py +580 -0
  38. ads/llm/autogen/v02/loggers/utils.py +86 -0
  39. ads/llm/autogen/v02/runtime_logging.py +163 -0
  40. ads/llm/guardrails/base.py +6 -5
  41. ads/llm/langchain/plugins/chat_models/oci_data_science.py +46 -20
  42. ads/llm/langchain/plugins/llms/oci_data_science_model_deployment_endpoint.py +38 -11
  43. ads/model/__init__.py +11 -13
  44. ads/model/artifact.py +47 -8
  45. ads/model/extractor/embedding_onnx_extractor.py +80 -0
  46. ads/model/framework/embedding_onnx_model.py +438 -0
  47. ads/model/generic_model.py +26 -24
  48. ads/model/model_metadata.py +8 -7
  49. ads/opctl/config/merger.py +13 -14
  50. ads/opctl/operator/common/operator_config.py +4 -4
  51. ads/opctl/operator/lowcode/common/transformations.py +12 -5
  52. ads/opctl/operator/lowcode/common/utils.py +11 -5
  53. ads/opctl/operator/lowcode/forecast/const.py +3 -0
  54. ads/opctl/operator/lowcode/forecast/model/arima.py +19 -13
  55. ads/opctl/operator/lowcode/forecast/model/automlx.py +129 -36
  56. ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
  57. ads/opctl/operator/lowcode/forecast/model/base_model.py +58 -17
  58. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +10 -3
  59. ads/opctl/operator/lowcode/forecast/model/prophet.py +25 -18
  60. ads/opctl/operator/lowcode/forecast/model_evaluator.py +3 -2
  61. ads/opctl/operator/lowcode/forecast/schema.yaml +13 -0
  62. ads/opctl/operator/lowcode/forecast/utils.py +8 -6
  63. ads/telemetry/base.py +18 -11
  64. ads/telemetry/client.py +33 -13
  65. ads/templates/schemas/openapi.json +1740 -0
  66. ads/templates/score_embedding_onnx.jinja2 +202 -0
  67. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10rc0.dist-info}/METADATA +9 -10
  68. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10rc0.dist-info}/RECORD +71 -50
  69. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10rc0.dist-info}/LICENSE.txt +0 -0
  70. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10rc0.dist-info}/WHEEL +0 -0
  71. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10rc0.dist-info}/entry_points.txt +0 -0
ads/aqua/model/model.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python
2
- # Copyright (c) 2024 Oracle and/or its affiliates.
2
+ # Copyright (c) 2024, 2025 Oracle and/or its affiliates.
3
3
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
4
  import os
5
5
  import pathlib
@@ -15,6 +15,7 @@ from oci.data_science.models import JobRun, Metadata, Model, UpdateModelDetails
15
15
  from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID, logger
16
16
  from ads.aqua.app import AquaApp
17
17
  from ads.aqua.common.enums import (
18
+ CustomInferenceContainerTypeFamily,
18
19
  FineTuningContainerTypeFamily,
19
20
  InferenceContainerTypeFamily,
20
21
  Tags,
@@ -23,6 +24,7 @@ from ads.aqua.common.errors import AquaRuntimeError, AquaValueError
23
24
  from ads.aqua.common.utils import (
24
25
  LifecycleStatus,
25
26
  _build_resource_identifier,
27
+ cleanup_local_hf_model_artifact,
26
28
  copy_model_config,
27
29
  create_word_icon,
28
30
  generate_tei_cmd_var,
@@ -127,7 +129,13 @@ class AquaModelApp(AquaApp):
127
129
 
128
130
  @telemetry(entry_point="plugin=model&action=create", name="aqua")
129
131
  def create(
130
- self, model_id: str, project_id: str, compartment_id: str = None, **kwargs
132
+ self,
133
+ model_id: str,
134
+ project_id: str,
135
+ compartment_id: str = None,
136
+ freeform_tags: Optional[dict] = None,
137
+ defined_tags: Optional[dict] = None,
138
+ **kwargs,
131
139
  ) -> DataScienceModel:
132
140
  """Creates custom aqua model from service model.
133
141
 
@@ -140,7 +148,10 @@ class AquaModelApp(AquaApp):
140
148
  compartment_id: str
141
149
  The compartment id for custom model. Defaults to None.
142
150
  If not provided, compartment id will be fetched from environment variables.
143
-
151
+ freeform_tags: dict
152
+ Freeform tags for the model
153
+ defined_tags: dict
154
+ Defined tags for the model
144
155
  Returns
145
156
  -------
146
157
  DataScienceModel:
@@ -157,6 +168,16 @@ class AquaModelApp(AquaApp):
157
168
  )
158
169
  return service_model
159
170
 
171
+ # combine tags
172
+ combined_freeform_tags = {
173
+ **(service_model.freeform_tags or {}),
174
+ **(freeform_tags or {}),
175
+ }
176
+ combined_defined_tags = {
177
+ **(service_model.defined_tags or {}),
178
+ **(defined_tags or {}),
179
+ }
180
+
160
181
  custom_model = (
161
182
  DataScienceModel()
162
183
  .with_compartment_id(target_compartment)
@@ -164,8 +185,8 @@ class AquaModelApp(AquaApp):
164
185
  .with_model_file_description(json_dict=service_model.model_file_description)
165
186
  .with_display_name(service_model.display_name)
166
187
  .with_description(service_model.description)
167
- .with_freeform_tags(**(service_model.freeform_tags or {}))
168
- .with_defined_tags(**(service_model.defined_tags or {}))
188
+ .with_freeform_tags(**combined_freeform_tags)
189
+ .with_defined_tags(**combined_defined_tags)
169
190
  .with_custom_metadata_list(service_model.custom_metadata_list)
170
191
  .with_defined_metadata_list(service_model.defined_metadata_list)
171
192
  .with_provenance_metadata(service_model.provenance_metadata)
@@ -357,8 +378,10 @@ class AquaModelApp(AquaApp):
357
378
  f"Failed to delete model:{model_id}. Only registered models or finetuned model can be deleted."
358
379
  )
359
380
 
360
- @telemetry(entry_point="plugin=model&action=delete", name="aqua")
361
- def edit_registered_model(self, id, inference_container, enable_finetuning, task):
381
+ @telemetry(entry_point="plugin=model&action=edit", name="aqua")
382
+ def edit_registered_model(
383
+ self, id, inference_container, inference_container_uri, enable_finetuning, task
384
+ ):
362
385
  """Edits the default config of unverified registered model.
363
386
 
364
387
  Parameters
@@ -367,6 +390,8 @@ class AquaModelApp(AquaApp):
367
390
  The model OCID.
368
391
  inference_container: str.
369
392
  The inference container family name
393
+ inference_container_uri: str
394
+ The inference container uri for embedding models
370
395
  enable_finetuning: str
371
396
  Flag to enable or disable finetuning over the model. Defaults to None
372
397
  task:
@@ -382,19 +407,44 @@ class AquaModelApp(AquaApp):
382
407
  if ds_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None):
383
408
  if ds_model.freeform_tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None):
384
409
  raise AquaRuntimeError(
385
- f"Failed to edit model:{id}. Only registered unverified models can be edited."
410
+ "Only registered unverified models can be edited."
386
411
  )
387
412
  else:
388
413
  custom_metadata_list = ds_model.custom_metadata_list
389
414
  freeform_tags = ds_model.freeform_tags
390
415
  if inference_container:
391
- custom_metadata_list.add(
392
- key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
393
- value=inference_container,
394
- category=MetadataCustomCategory.OTHER,
395
- description="Deployment container mapping for SMC",
396
- replace=True,
397
- )
416
+ if (
417
+ inference_container in CustomInferenceContainerTypeFamily
418
+ and inference_container_uri is None
419
+ ):
420
+ raise AquaRuntimeError(
421
+ "Inference container URI must be provided."
422
+ )
423
+ else:
424
+ custom_metadata_list.add(
425
+ key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
426
+ value=inference_container,
427
+ category=MetadataCustomCategory.OTHER,
428
+ description="Deployment container mapping for SMC",
429
+ replace=True,
430
+ )
431
+ if inference_container_uri:
432
+ if (
433
+ inference_container in CustomInferenceContainerTypeFamily
434
+ or inference_container is None
435
+ ):
436
+ custom_metadata_list.add(
437
+ key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER_URI,
438
+ value=inference_container_uri,
439
+ category=MetadataCustomCategory.OTHER,
440
+ description=f"Inference container URI for {ds_model.display_name}",
441
+ replace=True,
442
+ )
443
+ else:
444
+ raise AquaRuntimeError(
445
+ f"Inference container URI can be edited only with container values: {CustomInferenceContainerTypeFamily.values()}"
446
+ )
447
+
398
448
  if enable_finetuning is not None:
399
449
  if enable_finetuning.lower() == "true":
400
450
  custom_metadata_list.add(
@@ -414,7 +464,7 @@ class AquaModelApp(AquaApp):
414
464
  except Exception as ex:
415
465
  raise AquaRuntimeError(
416
466
  f"The given model already doesn't support finetuning: {ex}"
417
- )
467
+ ) from ex
418
468
 
419
469
  custom_metadata_list.remove("modelDescription")
420
470
  if task:
@@ -429,9 +479,7 @@ class AquaModelApp(AquaApp):
429
479
  )
430
480
  AquaApp().update_model(id, update_model_details)
431
481
  else:
432
- raise AquaRuntimeError(
433
- f"Failed to edit model:{id}. Only registered unverified models can be edited."
434
- )
482
+ raise AquaRuntimeError("Only registered unverified models can be edited.")
435
483
 
436
484
  def _fetch_metric_from_metadata(
437
485
  self,
@@ -766,6 +814,8 @@ class AquaModelApp(AquaApp):
766
814
  compartment_id: Optional[str],
767
815
  project_id: Optional[str],
768
816
  inference_container_uri: Optional[str],
817
+ freeform_tags: Optional[dict] = None,
818
+ defined_tags: Optional[dict] = None,
769
819
  ) -> DataScienceModel:
770
820
  """Create model by reference from the object storage path
771
821
 
@@ -778,6 +828,8 @@ class AquaModelApp(AquaApp):
778
828
  compartment_id (Optional[str]): Compartment Id of the compartment where the model has to be created
779
829
  project_id (Optional[str]): Project id of the project where the model has to be created
780
830
  inference_container_uri (Optional[str]): Inference container uri for BYOC
831
+ freeform_tags (dict): Freeform tags for the model
832
+ defined_tags (dict): Defined tags for the model
781
833
 
782
834
  Returns:
783
835
  DataScienceModel: Returns Datascience model instance.
@@ -846,8 +898,7 @@ class AquaModelApp(AquaApp):
846
898
  # only add cmd vars if inference container is not an SMC
847
899
  if (
848
900
  inference_container not in smc_container_set
849
- and inference_container
850
- == InferenceContainerTypeFamily.AQUA_TEI_CONTAINER_FAMILY
901
+ and inference_container in CustomInferenceContainerTypeFamily.values()
851
902
  ):
852
903
  cmd_vars = generate_tei_cmd_var(os_path)
853
904
  metadata.add(
@@ -918,6 +969,8 @@ class AquaModelApp(AquaApp):
918
969
  category="Other",
919
970
  replace=True,
920
971
  )
972
+ # override tags with freeform tags if set
973
+ tags = {**tags, **(freeform_tags or {})}
921
974
  model = (
922
975
  model.with_custom_metadata_list(metadata)
923
976
  .with_compartment_id(compartment_id or COMPARTMENT_OCID)
@@ -925,6 +978,7 @@ class AquaModelApp(AquaApp):
925
978
  .with_artifact(os_path)
926
979
  .with_display_name(model_name)
927
980
  .with_freeform_tags(**tags)
981
+ .with_defined_tags(**(defined_tags or {}))
928
982
  ).create(model_by_reference=True)
929
983
  logger.debug(model)
930
984
  return model
@@ -1296,25 +1350,25 @@ class AquaModelApp(AquaApp):
1296
1350
  Returns
1297
1351
  -------
1298
1352
  model_artifact_path (str): Location where the model artifacts are downloaded.
1299
-
1300
1353
  """
1301
1354
  # Download the model from hub
1302
- if not local_dir:
1303
- local_dir = os.path.join(os.path.expanduser("~"), "cached-model")
1304
- local_dir = os.path.join(local_dir, model_name)
1305
- os.makedirs(local_dir, exist_ok=True)
1306
- snapshot_download(
1355
+ if local_dir:
1356
+ local_dir = os.path.join(local_dir, model_name)
1357
+ os.makedirs(local_dir, exist_ok=True)
1358
+
1359
+ # if local_dir is not set, the return value points to the cached data folder
1360
+ local_dir = snapshot_download(
1307
1361
  repo_id=model_name,
1308
1362
  local_dir=local_dir,
1309
1363
  allow_patterns=allow_patterns,
1310
1364
  ignore_patterns=ignore_patterns,
1311
1365
  )
1312
- # Upload to object storage and skip .cache/huggingface/ folder
1366
+ # Upload to object storage
1313
1367
  model_artifact_path = upload_folder(
1314
1368
  os_path=os_path,
1315
1369
  local_dir=local_dir,
1316
1370
  model_name=model_name,
1317
- exclude_pattern=f"{HF_METADATA_FOLDER}*"
1371
+ exclude_pattern=f"{HF_METADATA_FOLDER}*",
1318
1372
  )
1319
1373
 
1320
1374
  return model_artifact_path
@@ -1339,6 +1393,8 @@ class AquaModelApp(AquaApp):
1339
1393
  ignore_patterns (list): Model files matching any of the patterns are not downloaded.
1340
1394
  Example: ["*.json"] will ignore all .json files. ["folder/*"] will ignore all files under `folder`.
1341
1395
  Patterns are Standard Wildcards (globbing patterns) and rules can be found here: https://docs.python.org/3/library/fnmatch.html
1396
+ cleanup_model_cache (bool): Deletes downloaded files from local machine after model is successfully
1397
+ registered. Set to True by default.
1342
1398
 
1343
1399
  Returns:
1344
1400
  AquaModel:
@@ -1402,6 +1458,8 @@ class AquaModelApp(AquaApp):
1402
1458
  compartment_id=import_model_details.compartment_id,
1403
1459
  project_id=import_model_details.project_id,
1404
1460
  inference_container_uri=import_model_details.inference_container_uri,
1461
+ freeform_tags=import_model_details.freeform_tags,
1462
+ defined_tags=import_model_details.defined_tags,
1405
1463
  )
1406
1464
  # registered model will always have inference and evaluation container, but
1407
1465
  # fine-tuning container may be not set
@@ -1446,6 +1504,14 @@ class AquaModelApp(AquaApp):
1446
1504
  detail=validation_result.telemetry_model_name,
1447
1505
  )
1448
1506
 
1507
+ if (
1508
+ import_model_details.download_from_hf
1509
+ and import_model_details.cleanup_model_cache
1510
+ ):
1511
+ cleanup_local_hf_model_artifact(
1512
+ model_name=model_name, local_dir=import_model_details.local_dir
1513
+ )
1514
+
1449
1515
  return AquaModel(**aqua_model_attributes)
1450
1516
 
1451
1517
  def _if_show(self, model: DataScienceModel) -> bool:
@@ -110,6 +110,8 @@ class AquaDeploymentApp(AquaApp):
110
110
  private_endpoint_id: Optional[str] = None,
111
111
  container_image_uri: Optional[None] = None,
112
112
  cmd_var: List[str] = None,
113
+ freeform_tags: Optional[dict] = None,
114
+ defined_tags: Optional[dict] = None,
113
115
  ) -> "AquaDeployment":
114
116
  """
115
117
  Creates a new Aqua deployment
@@ -163,6 +165,10 @@ class AquaDeploymentApp(AquaApp):
163
165
  Required parameter for BYOC based deployments if this parameter was not set during model registration.
164
166
  cmd_var: List[str]
165
167
  The cmd of model deployment container runtime.
168
+ freeform_tags: dict
169
+ Freeform tags for the model deployment
170
+ defined_tags: dict
171
+ Defined tags for the model deployment
166
172
  Returns
167
173
  -------
168
174
  AquaDeployment
@@ -172,7 +178,11 @@ class AquaDeploymentApp(AquaApp):
172
178
  # TODO validate if the service model has no artifact and if it requires import step before deployment.
173
179
  # Create a model catalog entry in the user compartment
174
180
  aqua_model = AquaModelApp().create(
175
- model_id=model_id, compartment_id=compartment_id, project_id=project_id
181
+ model_id=model_id,
182
+ compartment_id=compartment_id,
183
+ project_id=project_id,
184
+ freeform_tags=freeform_tags,
185
+ defined_tags=defined_tags,
176
186
  )
177
187
 
178
188
  tags = {}
@@ -418,12 +428,14 @@ class AquaDeploymentApp(AquaApp):
418
428
  if cmd_var:
419
429
  container_runtime.with_cmd(cmd_var)
420
430
 
431
+ tags = {**tags, **(freeform_tags or {})}
421
432
  # configure model deployment and deploy model on container runtime
422
433
  deployment = (
423
434
  ModelDeployment()
424
435
  .with_display_name(display_name)
425
436
  .with_description(description)
426
437
  .with_freeform_tags(**tags)
438
+ .with_defined_tags(**(defined_tags or {}))
427
439
  .with_infrastructure(infrastructure)
428
440
  .with_runtime(container_runtime)
429
441
  ).deploy(wait_for_completion=False)
@@ -98,9 +98,12 @@ class AquaDeployment(DataClassSerializable):
98
98
  ),
99
99
  )
100
100
 
101
- freeform_tags = oci_model_deployment.freeform_tags or UNKNOWN_DICT
102
- aqua_service_model_tag = freeform_tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None)
103
- aqua_model_name = freeform_tags.get(Tags.AQUA_MODEL_NAME_TAG, UNKNOWN)
101
+ tags = {}
102
+ tags.update(oci_model_deployment.freeform_tags or UNKNOWN_DICT)
103
+ tags.update(oci_model_deployment.defined_tags or UNKNOWN_DICT)
104
+
105
+ aqua_service_model_tag = tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None)
106
+ aqua_model_name = tags.get(Tags.AQUA_MODEL_NAME_TAG, UNKNOWN)
104
107
  private_endpoint_id = getattr(
105
108
  instance_configuration, "private_endpoint_id", UNKNOWN
106
109
  )
@@ -125,7 +128,7 @@ class AquaDeployment(DataClassSerializable):
125
128
  ocid=oci_model_deployment.id,
126
129
  region=region,
127
130
  ),
128
- tags=freeform_tags,
131
+ tags=tags,
129
132
  environment_variables=environment_variables,
130
133
  cmd=cmd,
131
134
  )
ads/aqua/ui.py CHANGED
@@ -481,12 +481,12 @@ class AquaUIApp(AquaApp):
481
481
 
482
482
  @telemetry(entry_point="plugin=ui&action=list_job_shapes", name="aqua")
483
483
  def list_job_shapes(self, **kwargs) -> list:
484
- """Lists all availiable job shapes for the specified compartment.
484
+ """Lists all available job shapes for the specified compartment.
485
485
 
486
486
  Parameters
487
487
  ----------
488
488
  **kwargs
489
- Addtional arguments, such as `compartment_id`,
489
+ Additional arguments, such as `compartment_id`,
490
490
  for `list_job_shapes <https://docs.oracle.com/en-us/iaas/tools/python/2.122.0/api/data_science/client/oci.data_science.DataScienceClient.html#oci.data_science.DataScienceClient.list_job_shapes>`_
491
491
 
492
492
  Returns
@@ -500,6 +500,28 @@ class AquaUIApp(AquaApp):
500
500
  ).data
501
501
  return sanitize_response(oci_client=self.ds_client, response=res)
502
502
 
503
+ @telemetry(entry_point="plugin=ui&action=list_model_deployment_shapes", name="aqua")
504
+ def list_model_deployment_shapes(self, **kwargs) -> list:
505
+ """Lists all available shapes for model deployment in the specified compartment.
506
+
507
+ Parameters
508
+ ----------
509
+ **kwargs
510
+ Additional arguments, such as `compartment_id`,
511
+ for `list_model_deployment_shapes <https://docs.oracle.com/en-us/iaas/api/#/en/data-science/20190101/ModelDeploymentShapeSummary/ListModelDeploymentShapes>`_
512
+
513
+ Returns
514
+ -------
515
+ str has json representation of `oci.data_science.models.ModelDeploymentShapeSummary`."""
516
+ compartment_id = kwargs.pop("compartment_id", COMPARTMENT_OCID)
517
+ logger.info(
518
+ f"Loading model deployment shape summary from compartment: {compartment_id}"
519
+ )
520
+ res = self.ds_client.list_model_deployment_shapes(
521
+ compartment_id=compartment_id, **kwargs
522
+ ).data
523
+ return sanitize_response(oci_client=self.ds_client, response=res)
524
+
503
525
  @telemetry(entry_point="plugin=ui&action=list_vcn", name="aqua")
504
526
  def list_vcn(self, **kwargs) -> list:
505
527
  """Lists the virtual cloud networks (VCNs) in the specified compartment.
ads/common/auth.py CHANGED
@@ -424,7 +424,7 @@ def create_signer(
424
424
  "signer": signer,
425
425
  "client_kwargs": client_kwargs,
426
426
  }
427
- logger.info(f"Using authentication signer type {type(signer)}.")
427
+ logger.debug(f"Using authentication signer type {type(signer)}.")
428
428
  return signer_dict
429
429
  else:
430
430
  signer_args = dict(
@@ -492,7 +492,7 @@ def default_signer(client_kwargs: Optional[Dict] = None) -> Dict:
492
492
  **(client_kwargs or {}),
493
493
  },
494
494
  }
495
- logger.info(f"Using authentication signer type {type(signer)}.")
495
+ logger.debug(f"Using authentication signer type {type(signer)}.")
496
496
  return signer_dict
497
497
  else:
498
498
  signer_args = dict(
@@ -621,7 +621,7 @@ class APIKey(AuthSignerGenerator):
621
621
  )
622
622
 
623
623
  oci.config.validate_config(configuration)
624
- logger.info(f"Using 'api_key' authentication.")
624
+ logger.debug(f"Using 'api_key' authentication.")
625
625
  return {
626
626
  "config": configuration,
627
627
  "signer": oci.signer.Signer(
@@ -684,7 +684,7 @@ class ResourcePrincipal(AuthSignerGenerator):
684
684
  "signer": oci.auth.signers.get_resource_principals_signer(),
685
685
  "client_kwargs": self.client_kwargs,
686
686
  }
687
- logger.info(f"Using 'resource_principal' authentication.")
687
+ logger.debug(f"Using 'resource_principal' authentication.")
688
688
  return signer_dict
689
689
 
690
690
  @staticmethod
@@ -747,7 +747,7 @@ class InstancePrincipal(AuthSignerGenerator):
747
747
  ),
748
748
  "client_kwargs": self.client_kwargs,
749
749
  }
750
- logger.info(f"Using 'instance_principal' authentication.")
750
+ logger.debug(f"Using 'instance_principal' authentication.")
751
751
  return signer_dict
752
752
 
753
753
 
@@ -814,7 +814,7 @@ class SecurityToken(AuthSignerGenerator):
814
814
  oci.config.from_file(self.oci_config_location, self.oci_key_profile)
815
815
  )
816
816
 
817
- logger.info(f"Using 'security_token' authentication.")
817
+ logger.debug(f"Using 'security_token' authentication.")
818
818
 
819
819
  for parameter in self.SECURITY_TOKEN_REQUIRED:
820
820
  if parameter not in configuration:
@@ -883,7 +883,7 @@ class SecurityToken(AuthSignerGenerator):
883
883
  )
884
884
 
885
885
  date_time = datetime.fromtimestamp(time_expired).strftime("%Y-%m-%d %H:%M:%S")
886
- logger.info(f"Session is valid until {date_time}.")
886
+ logger.debug(f"Session is valid until {date_time}.")
887
887
 
888
888
  def _read_security_token_file(self, security_token_file: str) -> str:
889
889
  """Reads security token from file.
@@ -1020,10 +1020,10 @@ class OCIAuthContext:
1020
1020
  """
1021
1021
  if self.profile:
1022
1022
  ads.set_auth(auth=AuthType.API_KEY, profile=self.profile)
1023
- logger.info(f"OCI profile set to {self.profile}")
1023
+ logger.debug(f"OCI profile set to {self.profile}")
1024
1024
  else:
1025
1025
  ads.set_auth(auth=AuthType.RESOURCE_PRINCIPAL)
1026
- logger.info(f"OCI auth set to resource principal")
1026
+ logger.debug(f"OCI auth set to resource principal")
1027
1027
  return self
1028
1028
 
1029
1029
  def __exit__(self, exc_type, exc_val, exc_tb):
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
@@ -0,0 +1,15 @@
1
+ # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3
+
4
+
5
+ class Events:
6
+ KEY = "event_name"
7
+
8
+ EXCEPTION = "exception"
9
+ LLM_CALL = "llm_call"
10
+ TOOL_CALL = "tool_call"
11
+ NEW_AGENT = "new_agent"
12
+ NEW_CLIENT = "new_client"
13
+ RECEIVED_MESSAGE = "received_message"
14
+ SESSION_START = "logging_session_start"
15
+ SESSION_STOP = "logging_session_stop"
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3
+ import json
4
+ import logging
5
+ import os
6
+
7
+ from jinja2 import Environment, FileSystemLoader
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class BaseReport:
13
+ """Base class containing utilities for generating reports."""
14
+
15
+ @staticmethod
16
+ def format_json_string(s) -> str:
17
+ """Formats the JSON string in markdown."""
18
+ return f"```json\n{json.dumps(json.loads(s), indent=2)}\n```"
19
+
20
+ @staticmethod
21
+ def _parse_date_time(datetime_string: str):
22
+ """Parses a datetime string in the logs into date and time.
23
+ Keeps only the seconds in the time.
24
+ """
25
+ date_str, time_str = datetime_string.split(" ", 1)
26
+ time_str = time_str.split(".", 1)[0]
27
+ return date_str, time_str
28
+
29
+ @staticmethod
30
+ def _preview_message(message: str, max_length=30) -> str:
31
+ """Shows the beginning part of a string message."""
32
+ # Return the entire string if it is less than the max_length
33
+ if len(message) <= max_length:
34
+ return message
35
+ # Go backward until we find the first whitespace
36
+ idx = 30
37
+ while not message[idx].isspace() and idx > 0:
38
+ idx -= 1
39
+ # If we found a whitespace
40
+ if idx > 0:
41
+ return message[:idx] + "..."
42
+ # If we didn't find a whitespace
43
+ return message[:30] + "..."
44
+
45
+ @classmethod
46
+ def _render_template(cls, template_path, **kwargs) -> str:
47
+ """Render Jinja template with kwargs."""
48
+ template_dir = os.path.join(os.path.dirname(__file__), "templates")
49
+ environment = Environment(
50
+ loader=FileSystemLoader(template_dir), autoescape=True
51
+ )
52
+ template = environment.get_template(template_path)
53
+ try:
54
+ html = template.render(**kwargs)
55
+ except Exception:
56
+ logger.error(
57
+ "Unable to render template %s with data:\n%s",
58
+ template_path,
59
+ str(kwargs),
60
+ )
61
+ return cls._render_template(
62
+ template_path=template_path,
63
+ sender=kwargs.get("sender", "N/A"),
64
+ content="TEMPLATE RENDER ERROR",
65
+ timestamp=kwargs.get("timestamp", ""),
66
+ )
67
+ return html
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python
2
+ # Copyright (c) 2024 Oracle and/or its affiliates.
3
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
+ """Contains the data structure for logging and reporting."""
5
+ import copy
6
+ import json
7
+ from dataclasses import asdict, dataclass, field
8
+ from typing import Optional, Union
9
+
10
+ from ads.llm.autogen.constants import Events
11
+
12
+
13
+ @dataclass
14
+ class LogData:
15
+ """Base class for the data field of LogRecord."""
16
+
17
+ def to_dict(self):
18
+ """Convert the log data to dictionary."""
19
+ return asdict(self)
20
+
21
+
22
+ @dataclass
23
+ class LogRecord:
24
+ """Represents a log record.
25
+
26
+ The `data` field is for pre-defined structured data, which should be an instance of LogData.
27
+ The `kwargs` field is for freeform key value pairs.
28
+ """
29
+
30
+ session_id: str
31
+ thread_id: int
32
+ timestamp: str
33
+ event_name: str
34
+ source_id: Optional[int] = None
35
+ source_name: Optional[str] = None
36
+ # Structured data for specific type of logs
37
+ data: Optional[LogData] = None
38
+ # Freeform data
39
+ kwargs: dict = field(default_factory=dict)
40
+
41
+ def to_dict(self):
42
+ """Convert the log record to dictionary."""
43
+ return asdict(self)
44
+
45
+ def to_string(self):
46
+ """Serialize the log record to JSON string."""
47
+ return json.dumps(self.to_dict(), default=str)
48
+
49
+ @classmethod
50
+ def from_dict(cls, data: dict) -> "LogRecord":
51
+ """Initializes a LogRecord object from dictionary."""
52
+ event_mapping = {
53
+ Events.NEW_AGENT: AgentData,
54
+ Events.TOOL_CALL: ToolCallData,
55
+ Events.LLM_CALL: LLMCompletionData,
56
+ }
57
+ if Events.KEY not in data:
58
+ raise KeyError("event_name not found in data.")
59
+
60
+ data = copy.deepcopy(data)
61
+
62
+ event_name = data["event_name"]
63
+ if event_name in event_mapping and data.get("data"):
64
+ data["data"] = event_mapping[event_name](**data.pop("data"))
65
+
66
+ return cls(**data)
67
+
68
+
69
+ @dataclass
70
+ class AgentData(LogData):
71
+ """Represents agent log Data."""
72
+
73
+ agent_name: str
74
+ agent_class: str
75
+ agent_module: Optional[str] = None
76
+ is_manager: Optional[bool] = None
77
+
78
+
79
+ @dataclass
80
+ class LLMCompletionData(LogData):
81
+ """Represents LLM completion log data."""
82
+
83
+ invocation_id: str
84
+ request: dict
85
+ response: dict
86
+ start_time: str
87
+ end_time: str
88
+ cost: Optional[float] = None
89
+ is_cached: Optional[bool] = None
90
+
91
+
92
+ @dataclass
93
+ class ToolCallData(LogData):
94
+ """Represents tool call log data."""
95
+
96
+ tool_name: str
97
+ start_time: str
98
+ end_time: str
99
+ agent_name: str
100
+ agent_class: str
101
+ agent_module: Optional[str] = None
102
+ input_args: dict = field(default_factory=dict)
103
+ returns: Optional[Union[str, list, dict, tuple]] = None