oracle-ads 2.12.8__py3-none-any.whl → 2.12.10__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 (82) hide show
  1. ads/aqua/__init__.py +4 -3
  2. ads/aqua/app.py +40 -18
  3. ads/aqua/client/__init__.py +3 -0
  4. ads/aqua/client/client.py +799 -0
  5. ads/aqua/common/enums.py +3 -0
  6. ads/aqua/common/utils.py +62 -2
  7. ads/aqua/data.py +2 -19
  8. ads/aqua/evaluation/entities.py +6 -0
  9. ads/aqua/evaluation/evaluation.py +45 -15
  10. ads/aqua/extension/aqua_ws_msg_handler.py +14 -7
  11. ads/aqua/extension/base_handler.py +12 -9
  12. ads/aqua/extension/deployment_handler.py +8 -4
  13. ads/aqua/extension/finetune_handler.py +8 -14
  14. ads/aqua/extension/model_handler.py +30 -6
  15. ads/aqua/extension/ui_handler.py +13 -1
  16. ads/aqua/finetuning/constants.py +5 -2
  17. ads/aqua/finetuning/entities.py +73 -17
  18. ads/aqua/finetuning/finetuning.py +110 -82
  19. ads/aqua/model/entities.py +5 -1
  20. ads/aqua/model/model.py +230 -104
  21. ads/aqua/modeldeployment/deployment.py +35 -11
  22. ads/aqua/modeldeployment/entities.py +7 -4
  23. ads/aqua/ui.py +24 -2
  24. ads/cli.py +16 -8
  25. ads/common/auth.py +9 -9
  26. ads/llm/autogen/__init__.py +2 -0
  27. ads/llm/autogen/constants.py +15 -0
  28. ads/llm/autogen/reports/__init__.py +2 -0
  29. ads/llm/autogen/reports/base.py +67 -0
  30. ads/llm/autogen/reports/data.py +103 -0
  31. ads/llm/autogen/reports/session.py +526 -0
  32. ads/llm/autogen/reports/templates/chat_box.html +13 -0
  33. ads/llm/autogen/reports/templates/chat_box_lt.html +5 -0
  34. ads/llm/autogen/reports/templates/chat_box_rt.html +6 -0
  35. ads/llm/autogen/reports/utils.py +56 -0
  36. ads/llm/autogen/v02/__init__.py +4 -0
  37. ads/llm/autogen/{client_v02.py → v02/client.py} +23 -10
  38. ads/llm/autogen/v02/log_handlers/__init__.py +2 -0
  39. ads/llm/autogen/v02/log_handlers/oci_file_handler.py +83 -0
  40. ads/llm/autogen/v02/loggers/__init__.py +6 -0
  41. ads/llm/autogen/v02/loggers/metric_logger.py +320 -0
  42. ads/llm/autogen/v02/loggers/session_logger.py +580 -0
  43. ads/llm/autogen/v02/loggers/utils.py +86 -0
  44. ads/llm/autogen/v02/runtime_logging.py +163 -0
  45. ads/llm/guardrails/base.py +6 -5
  46. ads/llm/langchain/plugins/chat_models/oci_data_science.py +46 -20
  47. ads/llm/langchain/plugins/llms/oci_data_science_model_deployment_endpoint.py +38 -11
  48. ads/model/__init__.py +11 -13
  49. ads/model/artifact.py +47 -8
  50. ads/model/extractor/embedding_onnx_extractor.py +80 -0
  51. ads/model/framework/embedding_onnx_model.py +438 -0
  52. ads/model/generic_model.py +26 -24
  53. ads/model/model_metadata.py +8 -7
  54. ads/opctl/config/merger.py +13 -14
  55. ads/opctl/operator/common/operator_config.py +4 -4
  56. ads/opctl/operator/lowcode/common/transformations.py +50 -8
  57. ads/opctl/operator/lowcode/common/utils.py +22 -6
  58. ads/opctl/operator/lowcode/forecast/__main__.py +10 -0
  59. ads/opctl/operator/lowcode/forecast/const.py +3 -0
  60. ads/opctl/operator/lowcode/forecast/model/arima.py +19 -13
  61. ads/opctl/operator/lowcode/forecast/model/automlx.py +129 -36
  62. ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
  63. ads/opctl/operator/lowcode/forecast/model/base_model.py +58 -17
  64. ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +1 -1
  65. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +10 -3
  66. ads/opctl/operator/lowcode/forecast/model/prophet.py +25 -18
  67. ads/opctl/operator/lowcode/forecast/model_evaluator.py +3 -2
  68. ads/opctl/operator/lowcode/forecast/operator_config.py +31 -0
  69. ads/opctl/operator/lowcode/forecast/schema.yaml +76 -0
  70. ads/opctl/operator/lowcode/forecast/utils.py +8 -6
  71. ads/opctl/operator/lowcode/forecast/whatifserve/__init__.py +7 -0
  72. ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py +233 -0
  73. ads/opctl/operator/lowcode/forecast/whatifserve/score.py +238 -0
  74. ads/telemetry/base.py +18 -11
  75. ads/telemetry/client.py +33 -13
  76. ads/templates/schemas/openapi.json +1740 -0
  77. ads/templates/score_embedding_onnx.jinja2 +202 -0
  78. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/METADATA +11 -10
  79. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/RECORD +82 -56
  80. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/LICENSE.txt +0 -0
  81. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/WHEEL +0 -0
  82. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,7 @@
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
 
5
- import logging
6
5
  import shlex
7
6
  from typing import Dict, List, Optional, Union
8
7
 
@@ -110,6 +109,8 @@ class AquaDeploymentApp(AquaApp):
110
109
  private_endpoint_id: Optional[str] = None,
111
110
  container_image_uri: Optional[None] = None,
112
111
  cmd_var: List[str] = None,
112
+ freeform_tags: Optional[dict] = None,
113
+ defined_tags: Optional[dict] = None,
113
114
  ) -> "AquaDeployment":
114
115
  """
115
116
  Creates a new Aqua deployment
@@ -163,6 +164,10 @@ class AquaDeploymentApp(AquaApp):
163
164
  Required parameter for BYOC based deployments if this parameter was not set during model registration.
164
165
  cmd_var: List[str]
165
166
  The cmd of model deployment container runtime.
167
+ freeform_tags: dict
168
+ Freeform tags for the model deployment
169
+ defined_tags: dict
170
+ Defined tags for the model deployment
166
171
  Returns
167
172
  -------
168
173
  AquaDeployment
@@ -172,7 +177,11 @@ class AquaDeploymentApp(AquaApp):
172
177
  # TODO validate if the service model has no artifact and if it requires import step before deployment.
173
178
  # Create a model catalog entry in the user compartment
174
179
  aqua_model = AquaModelApp().create(
175
- model_id=model_id, compartment_id=compartment_id, project_id=project_id
180
+ model_id=model_id,
181
+ compartment_id=compartment_id,
182
+ project_id=project_id,
183
+ freeform_tags=freeform_tags,
184
+ defined_tags=defined_tags,
176
185
  )
177
186
 
178
187
  tags = {}
@@ -261,7 +270,7 @@ class AquaDeploymentApp(AquaApp):
261
270
  f"field. Either re-register the model with custom container URI, or set container_image_uri "
262
271
  f"parameter when creating this deployment."
263
272
  ) from err
264
- logging.info(
273
+ logger.info(
265
274
  f"Aqua Image used for deploying {aqua_model.id} : {container_image_uri}"
266
275
  )
267
276
 
@@ -272,14 +281,14 @@ class AquaDeploymentApp(AquaApp):
272
281
  default_cmd_var = shlex.split(cmd_var_string)
273
282
  if default_cmd_var:
274
283
  cmd_var = validate_cmd_var(default_cmd_var, cmd_var)
275
- logging.info(f"CMD used for deploying {aqua_model.id} :{cmd_var}")
284
+ logger.info(f"CMD used for deploying {aqua_model.id} :{cmd_var}")
276
285
  except ValueError:
277
- logging.debug(
286
+ logger.debug(
278
287
  f"CMD will be ignored for this deployment as {AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME} "
279
288
  f"key is not available in the custom metadata field for this model."
280
289
  )
281
290
  except Exception as e:
282
- logging.error(
291
+ logger.error(
283
292
  f"There was an issue processing CMD arguments. Error: {str(e)}"
284
293
  )
285
294
 
@@ -375,7 +384,7 @@ class AquaDeploymentApp(AquaApp):
375
384
  if key not in env_var:
376
385
  env_var.update(env)
377
386
 
378
- logging.info(f"Env vars used for deploying {aqua_model.id} :{env_var}")
387
+ logger.info(f"Env vars used for deploying {aqua_model.id} :{env_var}")
379
388
 
380
389
  # Start model deployment
381
390
  # configure model deployment infrastructure
@@ -418,20 +427,26 @@ class AquaDeploymentApp(AquaApp):
418
427
  if cmd_var:
419
428
  container_runtime.with_cmd(cmd_var)
420
429
 
430
+ tags = {**tags, **(freeform_tags or {})}
421
431
  # configure model deployment and deploy model on container runtime
422
432
  deployment = (
423
433
  ModelDeployment()
424
434
  .with_display_name(display_name)
425
435
  .with_description(description)
426
436
  .with_freeform_tags(**tags)
437
+ .with_defined_tags(**(defined_tags or {}))
427
438
  .with_infrastructure(infrastructure)
428
439
  .with_runtime(container_runtime)
429
440
  ).deploy(wait_for_completion=False)
430
441
 
442
+ deployment_id = deployment.dsc_model_deployment.id
443
+ logger.info(
444
+ f"Aqua model deployment {deployment_id} created for model {aqua_model.id}."
445
+ )
431
446
  model_type = (
432
447
  AQUA_MODEL_TYPE_CUSTOM if is_fine_tuned_model else AQUA_MODEL_TYPE_SERVICE
433
448
  )
434
- deployment_id = deployment.dsc_model_deployment.id
449
+
435
450
  # we arbitrarily choose last 8 characters of OCID to identify MD in telemetry
436
451
  telemetry_kwargs = {"ocid": get_ocid_substring(deployment_id, key_len=8)}
437
452
 
@@ -527,6 +542,9 @@ class AquaDeploymentApp(AquaApp):
527
542
  value=state,
528
543
  )
529
544
 
545
+ logger.info(
546
+ f"Fetched {len(results)} model deployments from compartment_id={compartment_id}."
547
+ )
530
548
  # tracks number of times deployment listing was called
531
549
  self.telemetry.record_event_async(category="aqua/deployment", action="list")
532
550
 
@@ -534,18 +552,21 @@ class AquaDeploymentApp(AquaApp):
534
552
 
535
553
  @telemetry(entry_point="plugin=deployment&action=delete", name="aqua")
536
554
  def delete(self, model_deployment_id: str):
555
+ logger.info(f"Deleting model deployment {model_deployment_id}.")
537
556
  return self.ds_client.delete_model_deployment(
538
557
  model_deployment_id=model_deployment_id
539
558
  ).data
540
559
 
541
560
  @telemetry(entry_point="plugin=deployment&action=deactivate", name="aqua")
542
561
  def deactivate(self, model_deployment_id: str):
562
+ logger.info(f"Deactivating model deployment {model_deployment_id}.")
543
563
  return self.ds_client.deactivate_model_deployment(
544
564
  model_deployment_id=model_deployment_id
545
565
  ).data
546
566
 
547
567
  @telemetry(entry_point="plugin=deployment&action=activate", name="aqua")
548
568
  def activate(self, model_deployment_id: str):
569
+ logger.info(f"Activating model deployment {model_deployment_id}.")
549
570
  return self.ds_client.activate_model_deployment(
550
571
  model_deployment_id=model_deployment_id
551
572
  ).data
@@ -567,6 +588,8 @@ class AquaDeploymentApp(AquaApp):
567
588
  AquaDeploymentDetail:
568
589
  The instance of the Aqua model deployment details.
569
590
  """
591
+ logger.info(f"Fetching model deployment details for {model_deployment_id}.")
592
+
570
593
  model_deployment = self.ds_client.get_model_deployment(
571
594
  model_deployment_id=model_deployment_id, **kwargs
572
595
  ).data
@@ -582,7 +605,8 @@ class AquaDeploymentApp(AquaApp):
582
605
 
583
606
  if not oci_aqua:
584
607
  raise AquaRuntimeError(
585
- f"Target deployment {model_deployment_id} is not Aqua deployment."
608
+ f"Target deployment {model_deployment_id} is not Aqua deployment as it does not contain "
609
+ f"{Tags.AQUA_TAG} tag."
586
610
  )
587
611
 
588
612
  log_id = ""
@@ -640,7 +664,7 @@ class AquaDeploymentApp(AquaApp):
640
664
  config = self.get_config(model_id, AQUA_MODEL_DEPLOYMENT_CONFIG)
641
665
  if not config:
642
666
  logger.debug(
643
- f"Deployment config for custom model: {model_id} is not available."
667
+ f"Deployment config for custom model: {model_id} is not available. Use defaults."
644
668
  )
645
669
  return config
646
670
 
@@ -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/cli.py CHANGED
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8 -*--
3
-
4
- # Copyright (c) 2021, 2024 Oracle and/or its affiliates.
2
+ # Copyright (c) 2021, 2025 Oracle and/or its affiliates.
5
3
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
4
 
5
+ import json
6
+ import logging
7
7
  import sys
8
8
  import traceback
9
- from dataclasses import is_dataclass
9
+ import uuid
10
10
 
11
11
  import fire
12
+ from pydantic import BaseModel
12
13
 
13
14
  from ads.common import logger
14
15
 
@@ -27,7 +28,7 @@ except Exception as ex:
27
28
  )
28
29
  logger.debug(ex)
29
30
  logger.debug(traceback.format_exc())
30
- exit()
31
+ sys.exit()
31
32
 
32
33
  # https://packaging.python.org/en/latest/guides/single-sourcing-package-version/#single-sourcing-the-package-version
33
34
  if sys.version_info >= (3, 8):
@@ -84,7 +85,13 @@ def serialize(data):
84
85
  The string representation of each dataclass object.
85
86
  """
86
87
  if isinstance(data, list):
87
- [print(str(item)) for item in data]
88
+ for item in data:
89
+ if isinstance(item, BaseModel):
90
+ print(json.dumps(item.dict(), indent=4))
91
+ else:
92
+ print(str(item))
93
+ elif isinstance(data, BaseModel):
94
+ print(json.dumps(data.dict(), indent=4))
88
95
  else:
89
96
  print(str(data))
90
97
 
@@ -122,8 +129,9 @@ def exit_program(ex: Exception, logger: "logging.Logger") -> None:
122
129
  ... exit_program(e, logger)
123
130
  """
124
131
 
125
- logger.debug(traceback.format_exc())
126
- logger.error(str(ex))
132
+ request_id = str(uuid.uuid4())
133
+ logger.debug(f"Error Request ID: {request_id}\nError: {traceback.format_exc()}")
134
+ logger.error(f"Error Request ID: {request_id}\n" f"Error: {str(ex)}")
127
135
 
128
136
  exit_code = getattr(ex, "exit_code", 1)
129
137
  logger.error(f"Exit code: {exit_code}")
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