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.
- ads/aqua/__init__.py +4 -3
- ads/aqua/app.py +40 -18
- ads/aqua/client/__init__.py +3 -0
- ads/aqua/client/client.py +799 -0
- ads/aqua/common/enums.py +3 -0
- ads/aqua/common/utils.py +62 -2
- ads/aqua/data.py +2 -19
- ads/aqua/evaluation/entities.py +6 -0
- ads/aqua/evaluation/evaluation.py +45 -15
- ads/aqua/extension/aqua_ws_msg_handler.py +14 -7
- ads/aqua/extension/base_handler.py +12 -9
- ads/aqua/extension/deployment_handler.py +8 -4
- ads/aqua/extension/finetune_handler.py +8 -14
- ads/aqua/extension/model_handler.py +30 -6
- ads/aqua/extension/ui_handler.py +13 -1
- ads/aqua/finetuning/constants.py +5 -2
- ads/aqua/finetuning/entities.py +73 -17
- ads/aqua/finetuning/finetuning.py +110 -82
- ads/aqua/model/entities.py +5 -1
- ads/aqua/model/model.py +230 -104
- ads/aqua/modeldeployment/deployment.py +35 -11
- ads/aqua/modeldeployment/entities.py +7 -4
- ads/aqua/ui.py +24 -2
- ads/cli.py +16 -8
- ads/common/auth.py +9 -9
- ads/llm/autogen/__init__.py +2 -0
- ads/llm/autogen/constants.py +15 -0
- ads/llm/autogen/reports/__init__.py +2 -0
- ads/llm/autogen/reports/base.py +67 -0
- ads/llm/autogen/reports/data.py +103 -0
- ads/llm/autogen/reports/session.py +526 -0
- ads/llm/autogen/reports/templates/chat_box.html +13 -0
- ads/llm/autogen/reports/templates/chat_box_lt.html +5 -0
- ads/llm/autogen/reports/templates/chat_box_rt.html +6 -0
- ads/llm/autogen/reports/utils.py +56 -0
- ads/llm/autogen/v02/__init__.py +4 -0
- ads/llm/autogen/{client_v02.py → v02/client.py} +23 -10
- ads/llm/autogen/v02/log_handlers/__init__.py +2 -0
- ads/llm/autogen/v02/log_handlers/oci_file_handler.py +83 -0
- ads/llm/autogen/v02/loggers/__init__.py +6 -0
- ads/llm/autogen/v02/loggers/metric_logger.py +320 -0
- ads/llm/autogen/v02/loggers/session_logger.py +580 -0
- ads/llm/autogen/v02/loggers/utils.py +86 -0
- ads/llm/autogen/v02/runtime_logging.py +163 -0
- ads/llm/guardrails/base.py +6 -5
- ads/llm/langchain/plugins/chat_models/oci_data_science.py +46 -20
- ads/llm/langchain/plugins/llms/oci_data_science_model_deployment_endpoint.py +38 -11
- ads/model/__init__.py +11 -13
- ads/model/artifact.py +47 -8
- ads/model/extractor/embedding_onnx_extractor.py +80 -0
- ads/model/framework/embedding_onnx_model.py +438 -0
- ads/model/generic_model.py +26 -24
- ads/model/model_metadata.py +8 -7
- ads/opctl/config/merger.py +13 -14
- ads/opctl/operator/common/operator_config.py +4 -4
- ads/opctl/operator/lowcode/common/transformations.py +50 -8
- ads/opctl/operator/lowcode/common/utils.py +22 -6
- ads/opctl/operator/lowcode/forecast/__main__.py +10 -0
- ads/opctl/operator/lowcode/forecast/const.py +3 -0
- ads/opctl/operator/lowcode/forecast/model/arima.py +19 -13
- ads/opctl/operator/lowcode/forecast/model/automlx.py +129 -36
- ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
- ads/opctl/operator/lowcode/forecast/model/base_model.py +58 -17
- ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +1 -1
- ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +10 -3
- ads/opctl/operator/lowcode/forecast/model/prophet.py +25 -18
- ads/opctl/operator/lowcode/forecast/model_evaluator.py +3 -2
- ads/opctl/operator/lowcode/forecast/operator_config.py +31 -0
- ads/opctl/operator/lowcode/forecast/schema.yaml +76 -0
- ads/opctl/operator/lowcode/forecast/utils.py +8 -6
- ads/opctl/operator/lowcode/forecast/whatifserve/__init__.py +7 -0
- ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py +233 -0
- ads/opctl/operator/lowcode/forecast/whatifserve/score.py +238 -0
- ads/telemetry/base.py +18 -11
- ads/telemetry/client.py +33 -13
- ads/templates/schemas/openapi.json +1740 -0
- ads/templates/score_embedding_onnx.jinja2 +202 -0
- {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/METADATA +11 -10
- {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/RECORD +82 -56
- {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/LICENSE.txt +0 -0
- {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/WHEEL +0 -0
- {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/entry_points.txt +0 -0
@@ -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
|
|
5
5
|
from typing import Optional
|
@@ -8,6 +8,9 @@ from urllib.parse import urlparse
|
|
8
8
|
from tornado.web import HTTPError
|
9
9
|
|
10
10
|
from ads.aqua.common.decorator import handle_exceptions
|
11
|
+
from ads.aqua.common.enums import (
|
12
|
+
CustomInferenceContainerTypeFamily,
|
13
|
+
)
|
11
14
|
from ads.aqua.common.errors import AquaRuntimeError, AquaValueError
|
12
15
|
from ads.aqua.common.utils import (
|
13
16
|
get_hf_model_info,
|
@@ -96,7 +99,7 @@ class AquaModelHandler(AquaAPIhandler):
|
|
96
99
|
)
|
97
100
|
|
98
101
|
@handle_exceptions
|
99
|
-
def post(self, *args, **kwargs):
|
102
|
+
def post(self, *args, **kwargs): # noqa: ARG002
|
100
103
|
"""
|
101
104
|
Handles post request for the registering any Aqua model.
|
102
105
|
Raises
|
@@ -128,15 +131,27 @@ class AquaModelHandler(AquaAPIhandler):
|
|
128
131
|
download_from_hf = (
|
129
132
|
str(input_data.get("download_from_hf", "false")).lower() == "true"
|
130
133
|
)
|
134
|
+
local_dir = input_data.get("local_dir")
|
135
|
+
cleanup_model_cache = (
|
136
|
+
str(input_data.get("cleanup_model_cache", "false")).lower() == "true"
|
137
|
+
)
|
131
138
|
inference_container_uri = input_data.get("inference_container_uri")
|
132
139
|
allow_patterns = input_data.get("allow_patterns")
|
133
140
|
ignore_patterns = input_data.get("ignore_patterns")
|
141
|
+
freeform_tags = input_data.get("freeform_tags")
|
142
|
+
defined_tags = input_data.get("defined_tags")
|
143
|
+
ignore_model_artifact_check = (
|
144
|
+
str(input_data.get("ignore_model_artifact_check", "false")).lower()
|
145
|
+
== "true"
|
146
|
+
)
|
134
147
|
|
135
148
|
return self.finish(
|
136
149
|
AquaModelApp().register(
|
137
150
|
model=model,
|
138
151
|
os_path=os_path,
|
139
152
|
download_from_hf=download_from_hf,
|
153
|
+
local_dir=local_dir,
|
154
|
+
cleanup_model_cache=cleanup_model_cache,
|
140
155
|
inference_container=inference_container,
|
141
156
|
finetuning_container=finetuning_container,
|
142
157
|
compartment_id=compartment_id,
|
@@ -145,6 +160,9 @@ class AquaModelHandler(AquaAPIhandler):
|
|
145
160
|
inference_container_uri=inference_container_uri,
|
146
161
|
allow_patterns=allow_patterns,
|
147
162
|
ignore_patterns=ignore_patterns,
|
163
|
+
freeform_tags=freeform_tags,
|
164
|
+
defined_tags=defined_tags,
|
165
|
+
ignore_model_artifact_check=ignore_model_artifact_check,
|
148
166
|
)
|
149
167
|
)
|
150
168
|
|
@@ -159,7 +177,9 @@ class AquaModelHandler(AquaAPIhandler):
|
|
159
177
|
raise HTTPError(400, Errors.NO_INPUT_DATA)
|
160
178
|
|
161
179
|
inference_container = input_data.get("inference_container")
|
180
|
+
inference_container_uri = input_data.get("inference_container_uri")
|
162
181
|
inference_containers = AquaModelApp.list_valid_inference_containers()
|
182
|
+
inference_containers.extend(CustomInferenceContainerTypeFamily.values())
|
163
183
|
if (
|
164
184
|
inference_container is not None
|
165
185
|
and inference_container not in inference_containers
|
@@ -170,10 +190,14 @@ class AquaModelHandler(AquaAPIhandler):
|
|
170
190
|
|
171
191
|
enable_finetuning = input_data.get("enable_finetuning")
|
172
192
|
task = input_data.get("task")
|
173
|
-
app=AquaModelApp()
|
193
|
+
app = AquaModelApp()
|
174
194
|
self.finish(
|
175
195
|
app.edit_registered_model(
|
176
|
-
id,
|
196
|
+
id,
|
197
|
+
inference_container,
|
198
|
+
inference_container_uri,
|
199
|
+
enable_finetuning,
|
200
|
+
task,
|
177
201
|
)
|
178
202
|
)
|
179
203
|
app.clear_model_details_cache(model_id=id)
|
@@ -218,7 +242,7 @@ class AquaHuggingFaceHandler(AquaAPIhandler):
|
|
218
242
|
return None
|
219
243
|
|
220
244
|
@handle_exceptions
|
221
|
-
def get(self, *args, **kwargs):
|
245
|
+
def get(self, *args, **kwargs): # noqa: ARG002
|
222
246
|
"""
|
223
247
|
Finds a list of matching models from hugging face based on query string provided from users.
|
224
248
|
|
@@ -239,7 +263,7 @@ class AquaHuggingFaceHandler(AquaAPIhandler):
|
|
239
263
|
return self.finish({"models": models})
|
240
264
|
|
241
265
|
@handle_exceptions
|
242
|
-
def post(self, *args, **kwargs):
|
266
|
+
def post(self, *args, **kwargs): # noqa: ARG002
|
243
267
|
"""Handles post request for the HF Models APIs
|
244
268
|
|
245
269
|
Raises
|
ads/aqua/extension/ui_handler.py
CHANGED
@@ -68,6 +68,8 @@ class AquaUIHandler(AquaAPIhandler):
|
|
68
68
|
return self.list_buckets()
|
69
69
|
elif paths.startswith("aqua/job/shapes"):
|
70
70
|
return self.list_job_shapes()
|
71
|
+
elif paths.startswith("aqua/modeldeployment/shapes"):
|
72
|
+
return self.list_model_deployment_shapes()
|
71
73
|
elif paths.startswith("aqua/vcn"):
|
72
74
|
return self.list_vcn()
|
73
75
|
elif paths.startswith("aqua/subnets"):
|
@@ -160,6 +162,15 @@ class AquaUIHandler(AquaAPIhandler):
|
|
160
162
|
AquaUIApp().list_job_shapes(compartment_id=compartment_id, **kwargs)
|
161
163
|
)
|
162
164
|
|
165
|
+
def list_model_deployment_shapes(self, **kwargs):
|
166
|
+
"""Lists model deployment shapes available in the specified compartment."""
|
167
|
+
compartment_id = self.get_argument("compartment_id", default=COMPARTMENT_OCID)
|
168
|
+
return self.finish(
|
169
|
+
AquaUIApp().list_model_deployment_shapes(
|
170
|
+
compartment_id=compartment_id, **kwargs
|
171
|
+
)
|
172
|
+
)
|
173
|
+
|
163
174
|
def list_vcn(self, **kwargs):
|
164
175
|
"""Lists the virtual cloud networks (VCNs) in the specified compartment."""
|
165
176
|
compartment_id = self.get_argument("compartment_id", default=COMPARTMENT_OCID)
|
@@ -255,8 +266,9 @@ class AquaCLIHandler(AquaAPIhandler):
|
|
255
266
|
__handlers__ = [
|
256
267
|
("logging/?([^/]*)", AquaUIHandler),
|
257
268
|
("compartments/?([^/]*)", AquaUIHandler),
|
258
|
-
# TODO: change url to evaluation/
|
269
|
+
# TODO: change url to evaluation/experiments/?([^/]*)
|
259
270
|
("experiment/?([^/]*)", AquaUIHandler),
|
271
|
+
("modeldeployment/?([^/]*)", AquaUIHandler),
|
260
272
|
("versionsets/?([^/]*)", AquaUIHandler),
|
261
273
|
("buckets/?([^/]*)", AquaUIHandler),
|
262
274
|
("job/shapes/?([^/]*)", AquaUIHandler),
|
ads/aqua/finetuning/constants.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
|
-
#
|
3
|
-
# Copyright (c) 2024 Oracle and/or its affiliates.
|
2
|
+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
|
4
3
|
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
5
4
|
|
6
5
|
from ads.common.extended_enum import ExtendedEnumMeta
|
@@ -17,4 +16,8 @@ class FineTuneCustomMetadata(str, metaclass=ExtendedEnumMeta):
|
|
17
16
|
SERVICE_MODEL_FINE_TUNE_CONTAINER = "finetune-container"
|
18
17
|
|
19
18
|
|
19
|
+
class FineTuningRestrictedParams(str, metaclass=ExtendedEnumMeta):
|
20
|
+
OPTIMIZER = "optimizer"
|
21
|
+
|
22
|
+
|
20
23
|
ENV_AQUA_FINE_TUNING_CONTAINER = "AQUA_FINE_TUNING_CONTAINER"
|
ads/aqua/finetuning/entities.py
CHANGED
@@ -1,18 +1,24 @@
|
|
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
|
-
from dataclasses import dataclass, field
|
5
|
-
from typing import List, Optional
|
6
4
|
|
7
|
-
|
8
|
-
from
|
5
|
+
import json
|
6
|
+
from typing import List, Literal, Optional, Union
|
9
7
|
|
8
|
+
from pydantic import Field, model_validator
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
from ads.aqua.common.errors import AquaValueError
|
11
|
+
from ads.aqua.config.utils.serializer import Serializable
|
12
|
+
from ads.aqua.data import AquaResourceIdentifier
|
13
|
+
from ads.aqua.finetuning.constants import FineTuningRestrictedParams
|
14
|
+
|
15
|
+
|
16
|
+
class AquaFineTuningParams(Serializable):
|
17
|
+
"""Class for maintaining aqua fine-tuning model parameters"""
|
18
|
+
|
19
|
+
epochs: Optional[int] = None
|
14
20
|
learning_rate: Optional[float] = None
|
15
|
-
sample_packing:
|
21
|
+
sample_packing: Union[bool, None, Literal["auto"]] = "auto"
|
16
22
|
batch_size: Optional[int] = (
|
17
23
|
None # make it batch_size for user, but internally this is micro_batch_size
|
18
24
|
)
|
@@ -22,21 +28,59 @@ class AquaFineTuningParams(DataClassSerializable):
|
|
22
28
|
lora_alpha: Optional[int] = None
|
23
29
|
lora_dropout: Optional[float] = None
|
24
30
|
lora_target_linear: Optional[bool] = None
|
25
|
-
lora_target_modules: Optional[List] = None
|
31
|
+
lora_target_modules: Optional[List[str]] = None
|
26
32
|
early_stopping_patience: Optional[int] = None
|
27
33
|
early_stopping_threshold: Optional[float] = None
|
28
34
|
|
35
|
+
class Config:
|
36
|
+
extra = "allow"
|
37
|
+
|
38
|
+
def to_dict(self) -> dict:
|
39
|
+
return json.loads(super().to_json(exclude_none=True))
|
40
|
+
|
41
|
+
@model_validator(mode="before")
|
42
|
+
@classmethod
|
43
|
+
def validate_restricted_fields(cls, data: dict):
|
44
|
+
# we may want to skip validation if loading data from config files instead of user entered parameters
|
45
|
+
validate = data.pop("_validate", True)
|
46
|
+
if not (validate and isinstance(data, dict)):
|
47
|
+
return data
|
48
|
+
restricted_params = [
|
49
|
+
param for param in data if param in FineTuningRestrictedParams.values()
|
50
|
+
]
|
51
|
+
if restricted_params:
|
52
|
+
raise AquaValueError(
|
53
|
+
f"Found restricted parameter name: {restricted_params}"
|
54
|
+
)
|
55
|
+
return data
|
29
56
|
|
30
|
-
@dataclass(repr=False)
|
31
|
-
class AquaFineTuningSummary(AquaJobSummary, DataClassSerializable):
|
32
|
-
parameters: AquaFineTuningParams = field(default_factory=AquaFineTuningParams)
|
33
57
|
|
58
|
+
class AquaFineTuningSummary(Serializable):
|
59
|
+
"""Represents a summary of Aqua Finetuning job."""
|
34
60
|
|
35
|
-
|
36
|
-
|
37
|
-
|
61
|
+
id: str
|
62
|
+
name: str
|
63
|
+
console_url: str
|
64
|
+
lifecycle_state: str
|
65
|
+
lifecycle_details: str
|
66
|
+
time_created: str
|
67
|
+
tags: dict
|
68
|
+
experiment: AquaResourceIdentifier = Field(default_factory=AquaResourceIdentifier)
|
69
|
+
source: AquaResourceIdentifier = Field(default_factory=AquaResourceIdentifier)
|
70
|
+
job: AquaResourceIdentifier = Field(default_factory=AquaResourceIdentifier)
|
71
|
+
parameters: AquaFineTuningParams = Field(default_factory=AquaFineTuningParams)
|
38
72
|
|
39
|
-
|
73
|
+
class Config:
|
74
|
+
extra = "ignore"
|
75
|
+
|
76
|
+
def to_dict(self) -> dict:
|
77
|
+
return json.loads(super().to_json(exclude_none=True))
|
78
|
+
|
79
|
+
|
80
|
+
class CreateFineTuningDetails(Serializable):
|
81
|
+
"""Class to create aqua model fine-tuning instance.
|
82
|
+
|
83
|
+
Properties
|
40
84
|
------
|
41
85
|
ft_source_id: str
|
42
86
|
The fine tuning source id. Must be model ocid.
|
@@ -78,8 +122,14 @@ class CreateFineTuningDetails(DataClassSerializable):
|
|
78
122
|
The log group id for fine tuning job infrastructure.
|
79
123
|
log_id: (str, optional). Defaults to `None`.
|
80
124
|
The log id for fine tuning job infrastructure.
|
125
|
+
watch_logs: (bool, optional). Defaults to `False`.
|
126
|
+
The flag to watch the job run logs when a fine-tuning job is created.
|
81
127
|
force_overwrite: (bool, optional). Defaults to `False`.
|
82
128
|
Whether to force overwrite the existing file in object storage.
|
129
|
+
freeform_tags: (dict, optional)
|
130
|
+
Freeform tags for the fine-tuning model
|
131
|
+
defined_tags: (dict, optional)
|
132
|
+
Defined tags for the fine-tuning model
|
83
133
|
"""
|
84
134
|
|
85
135
|
ft_source_id: str
|
@@ -100,4 +150,10 @@ class CreateFineTuningDetails(DataClassSerializable):
|
|
100
150
|
subnet_id: Optional[str] = None
|
101
151
|
log_id: Optional[str] = None
|
102
152
|
log_group_id: Optional[str] = None
|
153
|
+
watch_logs: Optional[bool] = False
|
103
154
|
force_overwrite: Optional[bool] = False
|
155
|
+
freeform_tags: Optional[dict] = None
|
156
|
+
defined_tags: Optional[dict] = None
|
157
|
+
|
158
|
+
class Config:
|
159
|
+
extra = "ignore"
|
@@ -1,10 +1,11 @@
|
|
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
5
|
import json
|
6
6
|
import os
|
7
|
-
|
7
|
+
import time
|
8
|
+
import traceback
|
8
9
|
from typing import Dict
|
9
10
|
|
10
11
|
from oci.data_science.models import (
|
@@ -12,12 +13,14 @@ from oci.data_science.models import (
|
|
12
13
|
UpdateModelDetails,
|
13
14
|
UpdateModelProvenanceDetails,
|
14
15
|
)
|
16
|
+
from pydantic import ValidationError
|
15
17
|
|
16
18
|
from ads.aqua import logger
|
17
19
|
from ads.aqua.app import AquaApp
|
18
20
|
from ads.aqua.common.enums import Resource, Tags
|
19
21
|
from ads.aqua.common.errors import AquaFileExistsError, AquaValueError
|
20
22
|
from ads.aqua.common.utils import (
|
23
|
+
build_pydantic_error_message,
|
21
24
|
get_container_image,
|
22
25
|
upload_local_to_os,
|
23
26
|
)
|
@@ -35,7 +38,11 @@ from ads.aqua.finetuning.constants import (
|
|
35
38
|
ENV_AQUA_FINE_TUNING_CONTAINER,
|
36
39
|
FineTuneCustomMetadata,
|
37
40
|
)
|
38
|
-
from ads.aqua.finetuning.entities import
|
41
|
+
from ads.aqua.finetuning.entities import (
|
42
|
+
AquaFineTuningParams,
|
43
|
+
AquaFineTuningSummary,
|
44
|
+
CreateFineTuningDetails,
|
45
|
+
)
|
39
46
|
from ads.common.auth import default_signer
|
40
47
|
from ads.common.object_storage_details import ObjectStorageDetails
|
41
48
|
from ads.common.utils import get_console_link
|
@@ -100,23 +107,11 @@ class AquaFineTuningApp(AquaApp):
|
|
100
107
|
if not create_fine_tuning_details:
|
101
108
|
try:
|
102
109
|
create_fine_tuning_details = CreateFineTuningDetails(**kwargs)
|
103
|
-
except:
|
104
|
-
|
105
|
-
field.name for field in fields(CreateFineTuningDetails)
|
106
|
-
).rstrip()
|
110
|
+
except ValidationError as ex:
|
111
|
+
custom_errors = build_pydantic_error_message(ex)
|
107
112
|
raise AquaValueError(
|
108
|
-
"Invalid
|
109
|
-
|
110
|
-
)
|
111
|
-
|
112
|
-
source = self.get_source(create_fine_tuning_details.ft_source_id)
|
113
|
-
|
114
|
-
# todo: revisit validation for fine tuned models
|
115
|
-
# if source.compartment_id != ODSC_MODEL_COMPARTMENT_OCID:
|
116
|
-
# raise AquaValueError(
|
117
|
-
# f"Fine tuning is only supported for Aqua service models in {ODSC_MODEL_COMPARTMENT_OCID}. "
|
118
|
-
# "Use a valid Aqua service model id instead."
|
119
|
-
# )
|
113
|
+
f"Invalid parameters for creating a fine-tuned model. Error details: {custom_errors}."
|
114
|
+
) from ex
|
120
115
|
|
121
116
|
target_compartment = (
|
122
117
|
create_fine_tuning_details.compartment_id or COMPARTMENT_OCID
|
@@ -148,29 +143,27 @@ class AquaFineTuningApp(AquaApp):
|
|
148
143
|
"Specify the subnet id via API or environment variable AQUA_JOB_SUBNET_ID."
|
149
144
|
)
|
150
145
|
|
151
|
-
if create_fine_tuning_details.replica > DEFAULT_FT_REPLICA
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
f"Logging is required for fine tuning if replica is larger than {DEFAULT_FT_REPLICA}."
|
158
|
-
)
|
159
|
-
|
160
|
-
ft_parameters = None
|
161
|
-
try:
|
162
|
-
ft_parameters = AquaFineTuningParams(
|
163
|
-
**create_fine_tuning_details.ft_parameters,
|
146
|
+
if create_fine_tuning_details.replica > DEFAULT_FT_REPLICA and not (
|
147
|
+
create_fine_tuning_details.log_id
|
148
|
+
and create_fine_tuning_details.log_group_id
|
149
|
+
):
|
150
|
+
raise AquaValueError(
|
151
|
+
f"Logging is required for fine tuning if replica is larger than {DEFAULT_FT_REPLICA}."
|
164
152
|
)
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
153
|
+
|
154
|
+
if create_fine_tuning_details.watch_logs and not (
|
155
|
+
create_fine_tuning_details.log_id
|
156
|
+
and create_fine_tuning_details.log_group_id
|
157
|
+
):
|
169
158
|
raise AquaValueError(
|
170
|
-
"
|
171
|
-
|
159
|
+
"Logging is required for fine tuning if watch_logs is set to True. "
|
160
|
+
"Please provide log_id and log_group_id with the request parameters."
|
172
161
|
)
|
173
162
|
|
163
|
+
ft_parameters = self._get_finetuning_params(
|
164
|
+
create_fine_tuning_details.ft_parameters
|
165
|
+
)
|
166
|
+
|
174
167
|
experiment_model_version_set_id = create_fine_tuning_details.experiment_id
|
175
168
|
experiment_model_version_set_name = create_fine_tuning_details.experiment_name
|
176
169
|
|
@@ -197,11 +190,11 @@ class AquaFineTuningApp(AquaApp):
|
|
197
190
|
auth=default_signer(),
|
198
191
|
force_overwrite=create_fine_tuning_details.force_overwrite,
|
199
192
|
)
|
200
|
-
except FileExistsError:
|
193
|
+
except FileExistsError as fe:
|
201
194
|
raise AquaFileExistsError(
|
202
195
|
f"Dataset {dataset_file} already exists in {create_fine_tuning_details.report_path}. "
|
203
196
|
"Please use a new dataset file name, report path or set `force_overwrite` as True."
|
204
|
-
)
|
197
|
+
) from fe
|
205
198
|
logger.debug(
|
206
199
|
f"Uploaded local file {ft_dataset_path} to object storage {dst_uri}."
|
207
200
|
)
|
@@ -222,8 +215,12 @@ class AquaFineTuningApp(AquaApp):
|
|
222
215
|
description=create_fine_tuning_details.experiment_description,
|
223
216
|
compartment_id=target_compartment,
|
224
217
|
project_id=target_project,
|
218
|
+
freeform_tags=create_fine_tuning_details.freeform_tags,
|
219
|
+
defined_tags=create_fine_tuning_details.defined_tags,
|
225
220
|
)
|
226
221
|
|
222
|
+
source = self.get_source(create_fine_tuning_details.ft_source_id)
|
223
|
+
|
227
224
|
ft_model_custom_metadata = ModelCustomMetadata()
|
228
225
|
ft_model_custom_metadata.add(
|
229
226
|
key=FineTuneCustomMetadata.FINE_TUNE_SOURCE,
|
@@ -272,6 +269,7 @@ class AquaFineTuningApp(AquaApp):
|
|
272
269
|
ft_job_freeform_tags = {
|
273
270
|
Tags.AQUA_TAG: UNKNOWN,
|
274
271
|
Tags.AQUA_FINE_TUNED_MODEL_TAG: f"{source.id}#{source.display_name}",
|
272
|
+
**(create_fine_tuning_details.freeform_tags or {}),
|
275
273
|
}
|
276
274
|
|
277
275
|
ft_job = Job(name=ft_model.display_name).with_infrastructure(
|
@@ -286,6 +284,7 @@ class AquaFineTuningApp(AquaApp):
|
|
286
284
|
or DEFAULT_FT_BLOCK_STORAGE_SIZE
|
287
285
|
)
|
288
286
|
.with_freeform_tag(**ft_job_freeform_tags)
|
287
|
+
.with_defined_tag(**(create_fine_tuning_details.defined_tags or {}))
|
289
288
|
)
|
290
289
|
|
291
290
|
if not subnet_id:
|
@@ -353,6 +352,7 @@ class AquaFineTuningApp(AquaApp):
|
|
353
352
|
ft_job_run = ft_job.run(
|
354
353
|
name=ft_model.display_name,
|
355
354
|
freeform_tags=ft_job_freeform_tags,
|
355
|
+
defined_tags=create_fine_tuning_details.defined_tags or {},
|
356
356
|
wait=False,
|
357
357
|
)
|
358
358
|
logger.debug(
|
@@ -372,24 +372,30 @@ class AquaFineTuningApp(AquaApp):
|
|
372
372
|
for metadata in ft_model_custom_metadata.to_dict()["data"]
|
373
373
|
]
|
374
374
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
375
|
+
model_freeform_tags = source.freeform_tags or {}
|
376
|
+
model_freeform_tags.pop(Tags.LICENSE, None)
|
377
|
+
model_freeform_tags.pop(Tags.BASE_MODEL_CUSTOM, None)
|
378
|
+
|
379
|
+
model_freeform_tags = {
|
380
|
+
**model_freeform_tags,
|
381
|
+
Tags.READY_TO_FINE_TUNE: "false",
|
382
|
+
Tags.AQUA_TAG: UNKNOWN,
|
383
|
+
Tags.AQUA_FINE_TUNED_MODEL_TAG: f"{source.id}#{source.display_name}",
|
384
|
+
**(create_fine_tuning_details.freeform_tags or {}),
|
385
|
+
}
|
386
|
+
model_defined_tags = create_fine_tuning_details.defined_tags or {}
|
380
387
|
|
381
388
|
self.update_model(
|
382
389
|
model_id=ft_model.id,
|
383
390
|
update_model_details=UpdateModelDetails(
|
384
391
|
custom_metadata_list=updated_custom_metadata_list,
|
385
|
-
freeform_tags=
|
386
|
-
|
387
|
-
f"{source.id}#{source.display_name}"
|
388
|
-
),
|
389
|
-
**source_freeform_tags,
|
390
|
-
},
|
392
|
+
freeform_tags=model_freeform_tags,
|
393
|
+
defined_tags=model_defined_tags,
|
391
394
|
),
|
392
395
|
)
|
396
|
+
logger.debug(
|
397
|
+
f"Successfully updated model custom metadata list and freeform tags for the model {ft_model.id}."
|
398
|
+
)
|
393
399
|
|
394
400
|
self.update_model_provenance(
|
395
401
|
model_id=ft_model.id,
|
@@ -397,6 +403,9 @@ class AquaFineTuningApp(AquaApp):
|
|
397
403
|
training_id=ft_job_run.id
|
398
404
|
),
|
399
405
|
)
|
406
|
+
logger.debug(
|
407
|
+
f"Successfully updated model provenance for the model {ft_model.id}."
|
408
|
+
)
|
400
409
|
|
401
410
|
# tracks the shape and replica used for fine-tuning the service models
|
402
411
|
telemetry_kwargs = (
|
@@ -424,6 +433,20 @@ class AquaFineTuningApp(AquaApp):
|
|
424
433
|
value=source.display_name,
|
425
434
|
)
|
426
435
|
|
436
|
+
if create_fine_tuning_details.watch_logs:
|
437
|
+
logger.info(
|
438
|
+
f"Watching fine-tuning job run logs for {ft_job_run.id}. Press Ctrl+C to stop watching logs.\n"
|
439
|
+
)
|
440
|
+
try:
|
441
|
+
ft_job_run.watch()
|
442
|
+
except KeyboardInterrupt:
|
443
|
+
logger.info(f"\nStopped watching logs for {ft_job_run.id}.\n")
|
444
|
+
time.sleep(1)
|
445
|
+
except Exception:
|
446
|
+
logger.debug(
|
447
|
+
f"Something unexpected occurred while watching logs.\n{traceback.format_exc()}"
|
448
|
+
)
|
449
|
+
|
427
450
|
return AquaFineTuningSummary(
|
428
451
|
id=ft_model.id,
|
429
452
|
name=ft_model.display_name,
|
@@ -462,17 +485,15 @@ class AquaFineTuningApp(AquaApp):
|
|
462
485
|
region=self.region,
|
463
486
|
),
|
464
487
|
),
|
465
|
-
tags=
|
466
|
-
aqua_finetuning
|
467
|
-
finetuning_job_id
|
468
|
-
finetuning_source
|
469
|
-
finetuning_experiment_id
|
470
|
-
|
471
|
-
|
472
|
-
key: value
|
473
|
-
for key, value in asdict(ft_parameters).items()
|
474
|
-
if value is not None
|
488
|
+
tags={
|
489
|
+
"aqua_finetuning": Tags.AQUA_FINE_TUNING,
|
490
|
+
"finetuning_job_id": ft_job.id,
|
491
|
+
"finetuning_source": source.id,
|
492
|
+
"finetuning_experiment_id": experiment_model_version_set_id,
|
493
|
+
**model_freeform_tags,
|
494
|
+
**model_defined_tags,
|
475
495
|
},
|
496
|
+
parameters=ft_parameters,
|
476
497
|
)
|
477
498
|
|
478
499
|
def _build_fine_tuning_runtime(
|
@@ -535,7 +556,7 @@ class AquaFineTuningApp(AquaApp):
|
|
535
556
|
) -> str:
|
536
557
|
"""Builds the oci launch cmd for fine tuning container runtime."""
|
537
558
|
oci_launch_cmd = f"--training_data {dataset_path} --output_dir {report_path} --val_set_size {val_set_size} "
|
538
|
-
for key, value in
|
559
|
+
for key, value in parameters.to_dict().items():
|
539
560
|
if value is not None:
|
540
561
|
if key == "batch_size":
|
541
562
|
oci_launch_cmd += f"--micro_{key} {value} "
|
@@ -574,7 +595,7 @@ class AquaFineTuningApp(AquaApp):
|
|
574
595
|
config = self.get_config(model_id, AQUA_MODEL_FINETUNING_CONFIG)
|
575
596
|
if not config:
|
576
597
|
logger.debug(
|
577
|
-
f"Fine-tuning config for custom model: {model_id} is not available."
|
598
|
+
f"Fine-tuning config for custom model: {model_id} is not available. Use defaults."
|
578
599
|
)
|
579
600
|
return config
|
580
601
|
|
@@ -600,15 +621,36 @@ class AquaFineTuningApp(AquaApp):
|
|
600
621
|
default_params = {"params": {}}
|
601
622
|
finetuning_config = self.get_finetuning_config(model_id)
|
602
623
|
config_parameters = finetuning_config.get("configuration", UNKNOWN_DICT)
|
603
|
-
dataclass_fields =
|
624
|
+
dataclass_fields = self._get_finetuning_params(
|
625
|
+
config_parameters, validate=False
|
626
|
+
).to_dict()
|
604
627
|
for name, value in config_parameters.items():
|
605
|
-
if name == "micro_batch_size":
|
606
|
-
name = "batch_size"
|
607
628
|
if name in dataclass_fields:
|
629
|
+
if name == "micro_batch_size":
|
630
|
+
name = "batch_size"
|
608
631
|
default_params["params"][name] = value
|
609
632
|
|
610
633
|
return default_params
|
611
634
|
|
635
|
+
@staticmethod
|
636
|
+
def _get_finetuning_params(
|
637
|
+
params: Dict = None, validate: bool = True
|
638
|
+
) -> AquaFineTuningParams:
|
639
|
+
"""
|
640
|
+
Get and validate the fine-tuning params, and return an error message if validation fails. In order to skip
|
641
|
+
@model_validator decorator's validation, pass validate=False.
|
642
|
+
"""
|
643
|
+
try:
|
644
|
+
finetuning_params = AquaFineTuningParams(
|
645
|
+
**{**params, **{"_validate": validate}}
|
646
|
+
)
|
647
|
+
except ValidationError as ex:
|
648
|
+
custom_errors = build_pydantic_error_message(ex)
|
649
|
+
raise AquaValueError(
|
650
|
+
f"Invalid finetuning parameters. Error details: {custom_errors}."
|
651
|
+
) from ex
|
652
|
+
return finetuning_params
|
653
|
+
|
612
654
|
def validate_finetuning_params(self, params: Dict = None) -> Dict:
|
613
655
|
"""Validate if the fine-tuning parameters passed by the user can be overridden. Parameter values are not
|
614
656
|
validated, only param keys are validated.
|
@@ -622,19 +664,5 @@ class AquaFineTuningApp(AquaApp):
|
|
622
664
|
-------
|
623
665
|
Return a list of restricted params.
|
624
666
|
"""
|
625
|
-
|
626
|
-
|
627
|
-
**params,
|
628
|
-
)
|
629
|
-
except Exception as e:
|
630
|
-
logger.debug(str(e))
|
631
|
-
allowed_fine_tuning_parameters = ", ".join(
|
632
|
-
f"{field.name} (required)" if field.default is MISSING else field.name
|
633
|
-
for field in fields(AquaFineTuningParams)
|
634
|
-
).rstrip()
|
635
|
-
raise AquaValueError(
|
636
|
-
f"Invalid fine tuning parameters. Allowable parameters are: "
|
637
|
-
f"{allowed_fine_tuning_parameters}."
|
638
|
-
)
|
639
|
-
|
640
|
-
return dict(valid=True)
|
667
|
+
self._get_finetuning_params(params or {})
|
668
|
+
return {"valid": True}
|
ads/aqua/model/entities.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
|
|
5
5
|
"""
|
@@ -283,6 +283,7 @@ class ImportModelDetails(CLIBuilderMixin):
|
|
283
283
|
os_path: str
|
284
284
|
download_from_hf: Optional[bool] = True
|
285
285
|
local_dir: Optional[str] = None
|
286
|
+
cleanup_model_cache: Optional[bool] = False
|
286
287
|
inference_container: Optional[str] = None
|
287
288
|
finetuning_container: Optional[str] = None
|
288
289
|
compartment_id: Optional[str] = None
|
@@ -291,6 +292,9 @@ class ImportModelDetails(CLIBuilderMixin):
|
|
291
292
|
inference_container_uri: Optional[str] = None
|
292
293
|
allow_patterns: Optional[List[str]] = None
|
293
294
|
ignore_patterns: Optional[List[str]] = None
|
295
|
+
freeform_tags: Optional[dict] = None
|
296
|
+
defined_tags: Optional[dict] = None
|
297
|
+
ignore_model_artifact_check: Optional[bool] = None
|
294
298
|
|
295
299
|
def __post_init__(self):
|
296
300
|
self._command = "model register"
|