Skip to content

Commit 90e01a4

Browse files
committed
Merge branch 'feature/aquav1.0.1' of https://github.com/oracle/accelerated-data-science into ODSC-55771/fix_telemetry_name
2 parents ff217d8 + a4e36a9 commit 90e01a4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2447
-162
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,4 @@ Consult the security guide [SECURITY.md](https://github.com/oracle/accelerated-d
181181

182182
## License
183183

184-
Copyright (c) 2020, 2022 Oracle and/or its affiliates. Licensed under the [Universal Permissive License v1.0](https://oss.oracle.com/licenses/upl/)
184+
Copyright (c) 2020, 2024 Oracle and/or its affiliates. Licensed under the [Universal Permissive License v1.0](https://oss.oracle.com/licenses/upl/)

ads/aqua/decorator.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
RequestException,
1818
ServiceError,
1919
)
20+
from tornado.web import HTTPError
2021

2122
from ads.aqua.exception import AquaError
2223
from ads.aqua.extension.base_handler import AquaAPIhandler
@@ -58,6 +59,7 @@ def inner_function(self: AquaAPIhandler, *args, **kwargs):
5859
except ServiceError as error:
5960
self.write_error(
6061
status_code=error.status or 500,
62+
message=error.message,
6163
reason=error.message,
6264
service_payload=error.args[0] if error.args else None,
6365
exc_info=sys.exc_info(),
@@ -91,6 +93,12 @@ def inner_function(self: AquaAPIhandler, *args, **kwargs):
9193
service_payload=error.service_payload,
9294
exc_info=sys.exc_info(),
9395
)
96+
except HTTPError as e:
97+
self.write_error(
98+
status_code=e.status_code,
99+
reason=e.log_message,
100+
exc_info=sys.exc_info(),
101+
)
94102
except Exception as ex:
95103
self.write_error(
96104
status_code=500,

ads/aqua/deployment.py

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -391,40 +391,30 @@ def create(
391391
.with_runtime(container_runtime)
392392
).deploy(wait_for_completion=False)
393393

394-
if is_fine_tuned_model:
395-
# tracks unique deployments that were created in the user compartment
396-
self.telemetry.record_event_async(
397-
category="aqua/custom/deployment", action="create", detail=model_name
398-
)
399-
# tracks the shape used for deploying the custom models
400-
self.telemetry.record_event_async(
401-
category="aqua/custom/deployment/create",
402-
action="shape",
403-
detail=instance_shape,
404-
)
405-
# tracks the shape used for deploying the custom models by name
406-
self.telemetry.record_event_async(
407-
category=f"aqua/custom/{model_name}/deployment/create",
408-
action="shape",
409-
detail=instance_shape,
410-
)
411-
else:
412-
# tracks unique deployments that were created in the user compartment
413-
self.telemetry.record_event_async(
414-
category="aqua/service/deployment", action="create", detail=model_name
415-
)
416-
# tracks the shape used for deploying the service models
417-
self.telemetry.record_event_async(
418-
category="aqua/service/deployment/create",
419-
action="shape",
420-
detail=instance_shape,
421-
)
422-
# tracks the shape used for deploying the service models by name
423-
self.telemetry.record_event_async(
424-
category=f"aqua/service/{model_name}/deployment/create",
425-
action="shape",
426-
detail=instance_shape,
427-
)
394+
model_type = "custom" if is_fine_tuned_model else "service"
395+
deployment_id = deployment.dsc_model_deployment.id
396+
telemetry_kwargs = (
397+
{"ocid": deployment_id[-8:]} if len(deployment_id) > 8 else {}
398+
)
399+
# tracks unique deployments that were created in the user compartment
400+
self.telemetry.record_event_async(
401+
category=f"aqua/{model_type}/deployment",
402+
action="create",
403+
detail=model_name,
404+
**telemetry_kwargs,
405+
)
406+
# tracks the shape used for deploying the custom or service models
407+
self.telemetry.record_event_async(
408+
category=f"aqua/{model_type}/deployment/create",
409+
action="shape",
410+
detail=instance_shape,
411+
)
412+
# tracks the shape used for deploying the custom or service models by name
413+
self.telemetry.record_event_async(
414+
category=f"aqua/{model_type}/{model_name}/deployment/create",
415+
action="shape",
416+
detail=instance_shape,
417+
)
428418

429419
return AquaDeployment.from_oci_model_deployment(
430420
deployment.dsc_model_deployment, self.region
@@ -471,6 +461,18 @@ def list(self, **kwargs) -> List["AquaDeployment"]:
471461
)
472462
)
473463

464+
# log telemetry if MD is in active or failed state
465+
deployment_id = model_deployment.id
466+
state = model_deployment.lifecycle_state.upper()
467+
if state in ["ACTIVE", "FAILED"]:
468+
# tracks unique deployments that were listed in the user compartment
469+
self.telemetry.record_event_async(
470+
category=f"aqua/deployment",
471+
action="list",
472+
detail=deployment_id[-8:] if len(deployment_id) > 8 else "",
473+
value=state,
474+
)
475+
474476
# tracks number of times deployment listing was called
475477
self.telemetry.record_event_async(category="aqua/deployment", action="list")
476478

ads/aqua/evaluation.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class EvaluationJobExitCode(Enum):
7979
SUCCESS = 0
8080
COMMON_ERROR = 1
8181

82-
# Configuration-related issues
82+
# Configuration-related issues 10-19
8383
INVALID_EVALUATION_CONFIG = 10
8484
EVALUATION_CONFIG_NOT_PROVIDED = 11
8585
INVALID_OUTPUT_DIR = 12
@@ -88,7 +88,7 @@ class EvaluationJobExitCode(Enum):
8888
INVALID_TARGET_EVALUATION_ID = 15
8989
INVALID_EVALUATION_CONFIG_VALIDATION = 16
9090

91-
# Evaluation process issues
91+
# Evaluation process issues 20-39
9292
OUTPUT_DIR_NOT_FOUND = 20
9393
INVALID_INPUT_DATASET = 21
9494
INPUT_DATA_NOT_FOUND = 22
@@ -101,6 +101,7 @@ class EvaluationJobExitCode(Enum):
101101
MODEL_INFERENCE_WRONG_RESPONSE_FORMAT = 29
102102
UNSUPPORTED_METRICS = 30
103103
METRIC_CALCULATION_FAILURE = 31
104+
EVALUATION_MODEL_CATALOG_RECORD_CREATION_FAILED = 32
104105

105106

106107
EVALUATION_JOB_EXIT_CODE_MESSAGE = {
@@ -125,6 +126,11 @@ class EvaluationJobExitCode(Enum):
125126
EvaluationJobExitCode.MODEL_INFERENCE_WRONG_RESPONSE_FORMAT.value: "Evaluation encountered unsupported, or unexpected model output, verify the target evaluation model is compatible and produces the correct format.",
126127
EvaluationJobExitCode.UNSUPPORTED_METRICS.value: "None of the provided metrics are supported by the framework.",
127128
EvaluationJobExitCode.METRIC_CALCULATION_FAILURE.value: "All attempted metric calculations were unsuccessful. Please review the metric configurations and input data.",
129+
EvaluationJobExitCode.EVALUATION_MODEL_CATALOG_RECORD_CREATION_FAILED.value: (
130+
"Failed to create a Model Catalog record for the evaluation. "
131+
"This could be due to missing required permissions. "
132+
"Please check the log for more information."
133+
),
128134
}
129135

130136

@@ -312,6 +318,8 @@ class CreateAquaEvaluationDetails(DataClassSerializable):
312318
The log id for the evaluation job infrastructure.
313319
metrics: (list, optional). Defaults to `None`.
314320
The metrics for the evaluation.
321+
force_overwrite: (bool, optional). Defaults to `False`.
322+
Whether to force overwrite the existing file in object storage.
315323
"""
316324

317325
evaluation_source_id: str
@@ -332,6 +340,7 @@ class CreateAquaEvaluationDetails(DataClassSerializable):
332340
log_group_id: Optional[str] = None
333341
log_id: Optional[str] = None
334342
metrics: Optional[List] = None
343+
force_overwrite: Optional[bool] = False
335344

336345

337346
class AquaEvaluationApp(AquaApp):
@@ -435,12 +444,12 @@ def create(
435444
src_uri=evaluation_dataset_path,
436445
dst_uri=dst_uri,
437446
auth=default_signer(),
438-
force_overwrite=False,
447+
force_overwrite=create_aqua_evaluation_details.force_overwrite,
439448
)
440449
except FileExistsError:
441450
raise AquaFileExistsError(
442451
f"Dataset {dataset_file} already exists in {create_aqua_evaluation_details.report_path}. "
443-
"Please use a new dataset file name or report path."
452+
"Please use a new dataset file name, report path or set `force_overwrite` as True."
444453
)
445454
logger.debug(
446455
f"Uploaded local file {evaluation_dataset_path} to object storage {dst_uri}."
@@ -878,13 +887,17 @@ def get(self, eval_id) -> AquaEvaluationDetail:
878887
loggroup_id = ""
879888

880889
loggroup_url = get_log_links(region=self.region, log_group_id=loggroup_id)
881-
log_url = get_log_links(
882-
region=self.region,
883-
log_group_id=loggroup_id,
884-
log_id=log_id,
885-
compartment_id=job_run_details.compartment_id,
886-
source_id=jobrun_id
887-
) if job_run_details else ""
890+
log_url = (
891+
get_log_links(
892+
region=self.region,
893+
log_group_id=loggroup_id,
894+
log_id=log_id,
895+
compartment_id=job_run_details.compartment_id,
896+
source_id=jobrun_id,
897+
)
898+
if job_run_details
899+
else ""
900+
)
888901

889902
log_name = None
890903
loggroup_name = None
@@ -945,6 +958,7 @@ def list(
945958
List[AquaEvaluationSummary]:
946959
The list of the `ads.aqua.evalution.AquaEvaluationSummary`.
947960
"""
961+
compartment_id = compartment_id or COMPARTMENT_OCID
948962
logger.info(f"Fetching evaluations from compartment {compartment_id}.")
949963
models = utils.query_resources(
950964
compartment_id=compartment_id,
@@ -960,7 +974,6 @@ def list(
960974
evaluations = []
961975
async_tasks = []
962976
for model in models:
963-
964977
if model.identifier in self._eval_cache.keys():
965978
logger.debug(f"Retrieving evaluation {model.identifier} from cache.")
966979
evaluations.append(self._eval_cache.get(model.identifier))
@@ -1078,13 +1091,17 @@ def get_status(self, eval_id: str) -> dict:
10781091
loggroup_id = ""
10791092

10801093
loggroup_url = get_log_links(region=self.region, log_group_id=loggroup_id)
1081-
log_url = get_log_links(
1082-
region=self.region,
1083-
log_group_id=loggroup_id,
1084-
log_id=log_id,
1085-
compartment_id=job_run_details.compartment_id,
1086-
source_id=jobrun_id
1087-
) if job_run_details else ""
1094+
log_url = (
1095+
get_log_links(
1096+
region=self.region,
1097+
log_group_id=loggroup_id,
1098+
log_id=log_id,
1099+
compartment_id=job_run_details.compartment_id,
1100+
source_id=jobrun_id,
1101+
)
1102+
if job_run_details
1103+
else ""
1104+
)
10881105

10891106
return dict(
10901107
id=eval_id,
@@ -1129,6 +1146,19 @@ def get_supported_metrics(self) -> dict:
11291146
),
11301147
"args": {},
11311148
},
1149+
{
1150+
"use_case": ["text_generation"],
1151+
"key": "bleu",
1152+
"name": "bleu",
1153+
"description": (
1154+
"BLEU (Bilingual Evaluation Understudy) is an algorithm for evaluating the "
1155+
"quality of text which has been machine-translated from one natural language to another. "
1156+
"Quality is considered to be the correspondence between a machine's output and that of a "
1157+
"human: 'the closer a machine translation is to a professional human translation, "
1158+
"the better it is'."
1159+
),
1160+
"args": {},
1161+
},
11321162
]
11331163

11341164
@telemetry(entry_point="plugin=evaluation&action=load_metrics", name="aqua")

ads/aqua/extension/base_handler.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
import traceback
99
import uuid
1010
from dataclasses import asdict, is_dataclass
11+
from http.client import responses
1112
from typing import Any
1213

1314
from notebook.base.handlers import APIHandler
14-
from tornado.web import HTTPError, Application
1515
from tornado import httputil
16-
from ads.telemetry.client import TelemetryClient
17-
from ads.config import AQUA_TELEMETRY_BUCKET, AQUA_TELEMETRY_BUCKET_NS
16+
from tornado.web import Application, HTTPError
17+
1818
from ads.aqua import logger
19+
from ads.config import AQUA_TELEMETRY_BUCKET, AQUA_TELEMETRY_BUCKET_NS
20+
from ads.telemetry.client import TelemetryClient
1921

2022

2123
class AquaAPIhandler(APIHandler):
@@ -66,12 +68,15 @@ def finish(self, payload=None): # pylint: disable=W0221
6668

6769
def write_error(self, status_code, **kwargs):
6870
"""AquaAPIhandler errors are JSON, not human pages."""
69-
7071
self.set_header("Content-Type", "application/json")
7172
reason = kwargs.get("reason")
7273
self.set_status(status_code, reason=reason)
7374
service_payload = kwargs.get("service_payload", {})
74-
message = self.get_default_error_messages(service_payload, str(status_code))
75+
default_msg = responses.get(status_code, "Unknown HTTP Error")
76+
message = self.get_default_error_messages(
77+
service_payload, str(status_code), kwargs.get("message", default_msg)
78+
)
79+
7580
reply = {
7681
"status": status_code,
7782
"message": message,
@@ -84,7 +89,7 @@ def write_error(self, status_code, **kwargs):
8489
e = exc_info[1]
8590
if isinstance(e, HTTPError):
8691
reply["message"] = e.log_message or message
87-
reply["reason"] = e.reason
92+
reply["reason"] = e.reason if e.reason else reply["reason"]
8893
reply["request_id"] = str(uuid.uuid4())
8994
else:
9095
reply["request_id"] = str(uuid.uuid4())
@@ -102,15 +107,18 @@ def write_error(self, status_code, **kwargs):
102107
self.finish(json.dumps(reply))
103108

104109
@staticmethod
105-
def get_default_error_messages(service_payload: dict, status_code: str):
110+
def get_default_error_messages(
111+
service_payload: dict,
112+
status_code: str,
113+
default_msg: str = "Unknown HTTP Error.",
114+
):
106115
"""Method that maps the error messages based on the operation performed or the status codes encountered."""
107116

108117
messages = {
109118
"400": "Something went wrong with your request.",
110119
"403": "We're having trouble processing your request with the information provided.",
111120
"404": "Authorization Failed: The resource you're looking for isn't accessible.",
112121
"408": "Server is taking too long to response, please try again.",
113-
"500": "An error occurred while creating the resource.",
114122
"create": "Authorization Failed: Could not create resource.",
115123
"get": "Authorization Failed: The resource you're looking for isn't accessible.",
116124
}
@@ -119,7 +127,7 @@ def get_default_error_messages(service_payload: dict, status_code: str):
119127
operation_name = service_payload["operation_name"]
120128
if operation_name:
121129
if operation_name.startswith("create"):
122-
return messages["create"]
130+
return messages["create"] + f" Operation Name: {operation_name}."
123131
elif operation_name.startswith("list") or operation_name.startswith(
124132
"get"
125133
):
@@ -128,7 +136,7 @@ def get_default_error_messages(service_payload: dict, status_code: str):
128136
if status_code in messages:
129137
return messages[status_code]
130138
else:
131-
return "Unknown HTTP Error."
139+
return default_msg
132140

133141

134142
# todo: remove after error handler is implemented

0 commit comments

Comments
 (0)