Skip to content

Commit 8db09df

Browse files
Additional logging statements for AI Quick Actions operations (#1034)
2 parents fde6c46 + ed4098d commit 8db09df

File tree

12 files changed

+245
-133
lines changed

12 files changed

+245
-133
lines changed

ads/aqua/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python
2-
# Copyright (c) 2024 Oracle and/or its affiliates.
2+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
33
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
44

55
import json
@@ -298,7 +298,7 @@ def get_config(self, model_id: str, config_file_name: str) -> Dict:
298298
config = {}
299299
artifact_path = get_artifact_path(oci_model.custom_metadata_list)
300300
if not artifact_path:
301-
logger.error(
301+
logger.debug(
302302
f"Failed to get artifact path from custom metadata for the model: {model_id}"
303303
)
304304
return config

ads/aqua/common/utils.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python
2-
# Copyright (c) 2024 Oracle and/or its affiliates.
2+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
33
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
44
"""AQUA utils and constants."""
55

@@ -12,11 +12,12 @@
1212
import re
1313
import shlex
1414
import subprocess
15+
from dataclasses import fields
1516
from datetime import datetime, timedelta
1617
from functools import wraps
1718
from pathlib import Path
1819
from string import Template
19-
from typing import List, Union
20+
from typing import Any, List, Optional, Type, TypeVar, Union
2021

2122
import fsspec
2223
import oci
@@ -30,6 +31,7 @@
3031
)
3132
from oci.data_science.models import JobRun, Model
3233
from oci.object_storage.models import ObjectSummary
34+
from pydantic import BaseModel, ValidationError
3335

3436
from ads.aqua.common.enums import (
3537
InferenceContainerParamType,
@@ -74,6 +76,7 @@
7476
from ads.model import DataScienceModel, ModelVersionSet
7577

7678
logger = logging.getLogger("ads.aqua")
79+
T = TypeVar("T", bound=Union[BaseModel, Any])
7780

7881

7982
class LifecycleStatus(str, metaclass=ExtendedEnumMeta):
@@ -788,7 +791,9 @@ def get_ocid_substring(ocid: str, key_len: int) -> str:
788791
return ocid[-key_len:] if ocid and len(ocid) > key_len else ""
789792

790793

791-
def upload_folder(os_path: str, local_dir: str, model_name: str, exclude_pattern: str = None) -> str:
794+
def upload_folder(
795+
os_path: str, local_dir: str, model_name: str, exclude_pattern: str = None
796+
) -> str:
792797
"""Upload the local folder to the object storage
793798
794799
Args:
@@ -1159,3 +1164,49 @@ def validate_cmd_var(cmd_var: List[str], overrides: List[str]) -> List[str]:
11591164

11601165
combined_cmd_var = cmd_var + overrides
11611166
return combined_cmd_var
1167+
1168+
1169+
def validate_dataclass_params(dataclass_type: Type[T], **kwargs: Any) -> Optional[T]:
1170+
"""This method tries to initialize a dataclass with the provided keyword arguments. It handles
1171+
errors related to missing, unexpected or invalid arguments.
1172+
1173+
Parameters
1174+
----------
1175+
dataclass_type (Type[T]):
1176+
the dataclass type to instantiate.
1177+
kwargs (Any):
1178+
the keyword arguments to initialize the dataclass
1179+
Returns
1180+
-------
1181+
Optional[T]
1182+
instance of dataclass if successfully initialized
1183+
"""
1184+
1185+
try:
1186+
return dataclass_type(**kwargs)
1187+
except TypeError as ex:
1188+
error_message = str(ex)
1189+
allowed_params = ", ".join(
1190+
field.name for field in fields(dataclass_type)
1191+
).rstrip()
1192+
if "__init__() missing" in error_message:
1193+
missing_params = error_message.split("missing ")[1]
1194+
raise AquaValueError(
1195+
"Error: Missing required parameters: "
1196+
f"{missing_params}. Allowable parameters are: {allowed_params}."
1197+
) from ex
1198+
elif "__init__() got an unexpected keyword argument" in error_message:
1199+
unexpected_param = error_message.split("argument '")[1].rstrip("'")
1200+
raise AquaValueError(
1201+
"Error: Unexpected parameter: "
1202+
f"{unexpected_param}. Allowable parameters are: {allowed_params}."
1203+
) from ex
1204+
else:
1205+
raise AquaValueError(
1206+
"Invalid parameters. Allowable parameters are: " f"{allowed_params}."
1207+
) from ex
1208+
except ValidationError as ex:
1209+
custom_errors = {".".join(map(str, e["loc"])): e["msg"] for e in ex.errors()}
1210+
raise AquaValueError(
1211+
f"Invalid parameters. Error details: {custom_errors}."
1212+
) from ex

ads/aqua/evaluation/evaluation.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python
2-
# Copyright (c) 2024 Oracle and/or its affiliates.
2+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
33
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
44
import base64
55
import json
@@ -43,6 +43,7 @@
4343
get_container_image,
4444
is_valid_ocid,
4545
upload_local_to_os,
46+
validate_dataclass_params,
4647
)
4748
from ads.aqua.config.config import get_evaluation_service_config
4849
from ads.aqua.constants import (
@@ -155,16 +156,9 @@ def create(
155156
The instance of AquaEvaluationSummary.
156157
"""
157158
if not create_aqua_evaluation_details:
158-
try:
159-
create_aqua_evaluation_details = CreateAquaEvaluationDetails(**kwargs)
160-
except Exception as ex:
161-
custom_errors = {
162-
".".join(map(str, e["loc"])): e["msg"]
163-
for e in json.loads(ex.json())
164-
}
165-
raise AquaValueError(
166-
f"Invalid create evaluation parameters. Error details: {custom_errors}."
167-
) from ex
159+
create_aqua_evaluation_details = validate_dataclass_params(
160+
CreateAquaEvaluationDetails, **kwargs
161+
)
168162

169163
if not is_valid_ocid(create_aqua_evaluation_details.evaluation_source_id):
170164
raise AquaValueError(
@@ -199,11 +193,11 @@ def create(
199193
eval_inference_configuration = (
200194
container.spec.evaluation_configuration
201195
)
202-
except Exception:
196+
except Exception as ex:
203197
logger.debug(
204198
f"Could not load inference config details for the evaluation source id: "
205199
f"{create_aqua_evaluation_details.evaluation_source_id}. Please check if the container"
206-
f" runtime has the correct SMC image information."
200+
f" runtime has the correct SMC image information.\nError: {str(ex)}"
207201
)
208202
elif (
209203
DataScienceResource.MODEL
@@ -289,7 +283,7 @@ def create(
289283
f"Invalid experiment name. Please provide an experiment with `{Tags.AQUA_EVALUATION}` in tags."
290284
)
291285
except Exception:
292-
logger.debug(
286+
logger.info(
293287
f"Model version set {experiment_model_version_set_name} doesn't exist. "
294288
"Creating new model version set."
295289
)
@@ -711,21 +705,27 @@ def get(self, eval_id) -> AquaEvaluationDetail:
711705
try:
712706
log = utils.query_resource(log_id, return_all=False)
713707
log_name = log.display_name if log else ""
714-
except Exception:
708+
except Exception as ex:
709+
logger.debug(f"Failed to get associated log name. Error: {ex}")
715710
pass
716711

717712
if loggroup_id:
718713
try:
719714
loggroup = utils.query_resource(loggroup_id, return_all=False)
720715
loggroup_name = loggroup.display_name if loggroup else ""
721-
except Exception:
716+
except Exception as ex:
717+
logger.debug(f"Failed to get associated loggroup name. Error: {ex}")
722718
pass
723719

724720
try:
725721
introspection = json.loads(
726722
self._get_attribute_from_model_metadata(resource, "ArtifactTestResults")
727723
)
728-
except Exception:
724+
except Exception as ex:
725+
logger.debug(
726+
f"There was an issue loading the model attribute as json object for evaluation {eval_id}. "
727+
f"Setting introspection to empty.\n Error:{ex}"
728+
)
729729
introspection = {}
730730

731731
summary = AquaEvaluationDetail(
@@ -878,13 +878,13 @@ def get_status(self, eval_id: str) -> dict:
878878
try:
879879
log_id = job_run_details.log_details.log_id
880880
except Exception as e:
881-
logger.debug(f"Failed to get associated log. {str(e)}")
881+
logger.debug(f"Failed to get associated log.\nError: {str(e)}")
882882
log_id = ""
883883

884884
try:
885885
loggroup_id = job_run_details.log_details.log_group_id
886886
except Exception as e:
887-
logger.debug(f"Failed to get associated log. {str(e)}")
887+
logger.debug(f"Failed to get associated log.\nError: {str(e)}")
888888
loggroup_id = ""
889889

890890
loggroup_url = get_log_links(region=self.region, log_group_id=loggroup_id)
@@ -958,7 +958,7 @@ def load_metrics(self, eval_id: str) -> AquaEvalMetrics:
958958
)
959959
except Exception as e:
960960
logger.debug(
961-
"Failed to load `report.json` from evaluation artifact" f"{str(e)}"
961+
f"Failed to load `report.json` from evaluation artifact.\nError: {str(e)}"
962962
)
963963
json_report = {}
964964

@@ -1047,6 +1047,7 @@ def download_report(self, eval_id) -> AquaEvalReport:
10471047
return report
10481048

10491049
with tempfile.TemporaryDirectory() as temp_dir:
1050+
logger.info(f"Downloading evaluation artifact for {eval_id}.")
10501051
DataScienceModel.from_id(eval_id).download_artifact(
10511052
temp_dir,
10521053
auth=self._auth,
@@ -1200,6 +1201,7 @@ def _delete_job_and_model(job, model):
12001201
def load_evaluation_config(self, container: Optional[str] = None) -> Dict:
12011202
"""Loads evaluation config."""
12021203

1204+
logger.info("Loading evaluation container config.")
12031205
# retrieve the evaluation config by container family name
12041206
evaluation_config = get_evaluation_service_config(container)
12051207

@@ -1279,9 +1281,9 @@ def _get_source(
12791281
raise AquaRuntimeError(
12801282
f"Not supported source type: {resource_type}"
12811283
)
1282-
except Exception:
1284+
except Exception as ex:
12831285
logger.debug(
1284-
f"Failed to retrieve source information for evaluation {evaluation.identifier}."
1286+
f"Failed to retrieve source information for evaluation {evaluation.identifier}.\nError: {str(ex)}"
12851287
)
12861288
source_name = ""
12871289

ads/aqua/extension/aqua_ws_msg_handler.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*--
32

4-
# Copyright (c) 2024 Oracle and/or its affiliates.
3+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
54
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
65

76
import traceback
7+
import uuid
88
from abc import abstractmethod
99
from http.client import responses
1010
from typing import List
@@ -34,7 +34,7 @@ def __init__(self, message: str):
3434
self.telemetry = TelemetryClient(
3535
bucket=AQUA_TELEMETRY_BUCKET, namespace=AQUA_TELEMETRY_BUCKET_NS
3636
)
37-
except:
37+
except Exception:
3838
pass
3939

4040
@staticmethod
@@ -66,24 +66,31 @@ def write_error(self, status_code, **kwargs):
6666
"message": message,
6767
"service_payload": service_payload,
6868
"reason": reason,
69+
"request_id": str(uuid.uuid4()),
6970
}
7071
exc_info = kwargs.get("exc_info")
7172
if exc_info:
72-
logger.error("".join(traceback.format_exception(*exc_info)))
73+
logger.error(
74+
f"Error Request ID: {reply['request_id']}\n"
75+
f"Error: {''.join(traceback.format_exception(*exc_info))}"
76+
)
7377
e = exc_info[1]
7478
if isinstance(e, HTTPError):
7579
reply["message"] = e.log_message or message
7680
reply["reason"] = e.reason
77-
else:
78-
logger.warning(reply["message"])
81+
82+
logger.error(
83+
f"Error Request ID: {reply['request_id']}\n"
84+
f"Error: {reply['message']} {reply['reason']}"
85+
)
7986
# telemetry may not be present if there is an error while initializing
8087
if hasattr(self, "telemetry"):
8188
aqua_api_details = kwargs.get("aqua_api_details", {})
8289
self.telemetry.record_event_async(
8390
category="aqua/error",
8491
action=str(status_code),
8592
value=reason,
86-
**aqua_api_details
93+
**aqua_api_details,
8794
)
8895
response = AquaWsError(
8996
status=status_code,

ads/aqua/extension/base_handler.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
# Copyright (c) 2024 Oracle and/or its affiliates.
2+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
43
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
54

65

@@ -35,7 +34,7 @@ def __init__(
3534
self.telemetry = TelemetryClient(
3635
bucket=AQUA_TELEMETRY_BUCKET, namespace=AQUA_TELEMETRY_BUCKET_NS
3736
)
38-
except:
37+
except Exception:
3938
pass
4039

4140
@staticmethod
@@ -82,19 +81,23 @@ def write_error(self, status_code, **kwargs):
8281
"message": message,
8382
"service_payload": service_payload,
8483
"reason": reason,
84+
"request_id": str(uuid.uuid4()),
8585
}
8686
exc_info = kwargs.get("exc_info")
8787
if exc_info:
88-
logger.error("".join(traceback.format_exception(*exc_info)))
88+
logger.error(
89+
f"Error Request ID: {reply['request_id']}\n"
90+
f"Error: {''.join(traceback.format_exception(*exc_info))}"
91+
)
8992
e = exc_info[1]
9093
if isinstance(e, HTTPError):
9194
reply["message"] = e.log_message or message
9295
reply["reason"] = e.reason if e.reason else reply["reason"]
93-
reply["request_id"] = str(uuid.uuid4())
94-
else:
95-
reply["request_id"] = str(uuid.uuid4())
9696

97-
logger.warning(reply["message"])
97+
logger.error(
98+
f"Error Request ID: {reply['request_id']}\n"
99+
f"Error: {reply['message']} {reply['reason']}"
100+
)
98101

99102
# telemetry may not be present if there is an error while initializing
100103
if hasattr(self, "telemetry"):
@@ -103,7 +106,7 @@ def write_error(self, status_code, **kwargs):
103106
category="aqua/error",
104107
action=str(status_code),
105108
value=reason,
106-
**aqua_api_details
109+
**aqua_api_details,
107110
)
108111

109112
self.finish(json.dumps(reply))

0 commit comments

Comments
 (0)