digitalhub 0.9.2__py3-none-any.whl → 0.10.0b0__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.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +2 -3
- digitalhub/client/_base/client.py +3 -2
- digitalhub/client/dhcore/api_builder.py +5 -0
- digitalhub/client/dhcore/client.py +27 -399
- digitalhub/client/dhcore/configurator.py +339 -0
- digitalhub/client/dhcore/error_parser.py +107 -0
- digitalhub/client/dhcore/models.py +13 -23
- digitalhub/client/dhcore/utils.py +4 -44
- digitalhub/client/local/api_builder.py +9 -17
- digitalhub/client/local/client.py +12 -2
- digitalhub/client/local/enums.py +11 -0
- digitalhub/configurator/api.py +31 -0
- digitalhub/configurator/configurator.py +194 -0
- digitalhub/configurator/credentials_store.py +65 -0
- digitalhub/configurator/ini_module.py +74 -0
- digitalhub/entities/_base/_base/entity.py +2 -2
- digitalhub/entities/_base/material/entity.py +19 -6
- digitalhub/entities/_base/material/utils.py +2 -2
- digitalhub/entities/_commons/enums.py +1 -0
- digitalhub/entities/_commons/models.py +9 -0
- digitalhub/entities/_commons/utils.py +25 -0
- digitalhub/entities/_operations/processor.py +103 -107
- digitalhub/entities/artifact/crud.py +3 -3
- digitalhub/entities/dataitem/_base/entity.py +2 -2
- digitalhub/entities/dataitem/crud.py +3 -3
- digitalhub/entities/dataitem/table/entity.py +2 -2
- digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +43 -51
- digitalhub/entities/dataitem/utils.py +6 -3
- digitalhub/entities/model/_base/entity.py +172 -0
- digitalhub/entities/model/_base/spec.py +0 -10
- digitalhub/entities/model/_base/status.py +10 -0
- digitalhub/entities/model/crud.py +3 -3
- digitalhub/entities/model/huggingface/spec.py +6 -3
- digitalhub/entities/model/mlflow/models.py +2 -2
- digitalhub/entities/model/mlflow/spec.py +1 -3
- digitalhub/entities/model/mlflow/utils.py +44 -5
- digitalhub/entities/run/_base/entity.py +149 -0
- digitalhub/entities/run/_base/status.py +12 -0
- digitalhub/entities/task/_base/spec.py +2 -0
- digitalhub/entities/task/crud.py +4 -0
- digitalhub/readers/{_commons → pandas}/enums.py +4 -0
- digitalhub/readers/pandas/reader.py +58 -10
- digitalhub/stores/_base/store.py +1 -49
- digitalhub/stores/api.py +8 -33
- digitalhub/stores/builder.py +44 -161
- digitalhub/stores/local/store.py +4 -18
- digitalhub/stores/remote/store.py +3 -10
- digitalhub/stores/s3/configurator.py +107 -0
- digitalhub/stores/s3/enums.py +17 -0
- digitalhub/stores/s3/models.py +21 -0
- digitalhub/stores/s3/store.py +8 -28
- digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
- digitalhub/stores/sql/configurator.py +88 -0
- digitalhub/stores/sql/enums.py +16 -0
- digitalhub/stores/sql/models.py +24 -0
- digitalhub/stores/sql/store.py +14 -57
- digitalhub/utils/exceptions.py +6 -0
- digitalhub/utils/generic_utils.py +9 -8
- digitalhub/utils/uri_utils.py +1 -1
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/METADATA +5 -6
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/RECORD +66 -53
- test/local/imports/test_imports.py +0 -1
- digitalhub/client/dhcore/env.py +0 -23
- /digitalhub/{readers/_commons → configurator}/__init__.py +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/WHEEL +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,8 @@ import typing
|
|
|
4
4
|
|
|
5
5
|
from digitalhub.entities._base.material.entity import MaterialEntity
|
|
6
6
|
from digitalhub.entities._commons.enums import EntityTypes
|
|
7
|
+
from digitalhub.entities._commons.utils import validate_metric_value
|
|
8
|
+
from digitalhub.entities._operations.processor import processor
|
|
7
9
|
|
|
8
10
|
if typing.TYPE_CHECKING:
|
|
9
11
|
from digitalhub.entities._base.entity.metadata import Metadata
|
|
@@ -32,3 +34,173 @@ class Model(MaterialEntity):
|
|
|
32
34
|
super().__init__(project, name, uuid, kind, metadata, spec, status, user)
|
|
33
35
|
self.spec: ModelSpec
|
|
34
36
|
self.status: ModelStatus
|
|
37
|
+
|
|
38
|
+
# Initialize metrics
|
|
39
|
+
self._init_metrics()
|
|
40
|
+
|
|
41
|
+
def save(self, update: bool = False) -> Model:
|
|
42
|
+
"""
|
|
43
|
+
Save entity into backend.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
update : bool
|
|
48
|
+
Flag to indicate update.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
Model
|
|
53
|
+
Entity saved.
|
|
54
|
+
"""
|
|
55
|
+
obj: Model = super().save(update)
|
|
56
|
+
obj._get_metrics()
|
|
57
|
+
return obj
|
|
58
|
+
|
|
59
|
+
def log_metric(
|
|
60
|
+
self,
|
|
61
|
+
key: str,
|
|
62
|
+
value: list[float | int] | float | int,
|
|
63
|
+
overwrite: bool = False,
|
|
64
|
+
single_value: bool = False,
|
|
65
|
+
) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Log metric into entity status.
|
|
68
|
+
A metric is named by a key and value (single number or list of numbers).
|
|
69
|
+
The metric by default is put in a list or appended to an existing list.
|
|
70
|
+
If single_value is True, the value will be a single number.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
key : str
|
|
75
|
+
Key of the metric.
|
|
76
|
+
value : list[float | int] | float | int
|
|
77
|
+
Value of the metric.
|
|
78
|
+
overwrite : bool
|
|
79
|
+
If True, overwrite existing metric.
|
|
80
|
+
single_value : bool
|
|
81
|
+
If True, value is a single value.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
None
|
|
86
|
+
|
|
87
|
+
Examples
|
|
88
|
+
--------
|
|
89
|
+
Log a new value in a list
|
|
90
|
+
>>> entity.log_metric("loss", 0.002)
|
|
91
|
+
|
|
92
|
+
Append a new value in a list
|
|
93
|
+
>>> entity.log_metric("loss", 0.0019)
|
|
94
|
+
|
|
95
|
+
Log a list of values and append them to existing metric:
|
|
96
|
+
>>> entity.log_metric("loss", [0.0018, 0.0015])
|
|
97
|
+
|
|
98
|
+
Log a single value (not represented as list):
|
|
99
|
+
>>> entity.log_metric("accuracy", 0.9, single_value=True)
|
|
100
|
+
|
|
101
|
+
Log a list of values and overwrite existing metric:
|
|
102
|
+
>>> entity.log_metric("accuracy", [0.8, 0.9], overwrite=True)
|
|
103
|
+
"""
|
|
104
|
+
value = validate_metric_value(value)
|
|
105
|
+
|
|
106
|
+
if isinstance(value, list):
|
|
107
|
+
self._handle_metric_list(key, value, overwrite)
|
|
108
|
+
elif single_value:
|
|
109
|
+
self._handle_metric_single(key, value, overwrite)
|
|
110
|
+
else:
|
|
111
|
+
self._handle_metric_list_append(key, value, overwrite)
|
|
112
|
+
|
|
113
|
+
processor.update_metric(self.project, self.ENTITY_TYPE, self.id, key, self.status.metrics[key])
|
|
114
|
+
|
|
115
|
+
##############################
|
|
116
|
+
# Helper methods
|
|
117
|
+
##############################
|
|
118
|
+
|
|
119
|
+
def _init_metrics(self) -> None:
|
|
120
|
+
"""
|
|
121
|
+
Initialize metrics.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
None
|
|
126
|
+
"""
|
|
127
|
+
if self.status.metrics is None:
|
|
128
|
+
self.status.metrics = {}
|
|
129
|
+
|
|
130
|
+
def _get_metrics(self) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Get model metrics from backend.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
None
|
|
137
|
+
"""
|
|
138
|
+
self.status.metrics = processor.read_metrics(
|
|
139
|
+
project=self.project,
|
|
140
|
+
entity_type=self.ENTITY_TYPE,
|
|
141
|
+
entity_id=self.id,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def _handle_metric_single(self, key: str, value: float | int, overwrite: bool = False) -> None:
|
|
145
|
+
"""
|
|
146
|
+
Handle metric single value.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
key : str
|
|
151
|
+
Key of the metric.
|
|
152
|
+
value : float
|
|
153
|
+
Value of the metric.
|
|
154
|
+
overwrite : bool
|
|
155
|
+
If True, overwrite existing metric.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
None
|
|
160
|
+
"""
|
|
161
|
+
if key not in self.status.metrics or overwrite:
|
|
162
|
+
self.status.metrics[key] = value
|
|
163
|
+
|
|
164
|
+
def _handle_metric_list_append(self, key: str, value: float | int, overwrite: bool = False) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Handle metric list append.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
key : str
|
|
171
|
+
Key of the metric.
|
|
172
|
+
value : float
|
|
173
|
+
Value of the metric.
|
|
174
|
+
overwrite : bool
|
|
175
|
+
If True, overwrite existing metric.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
None
|
|
180
|
+
"""
|
|
181
|
+
if key not in self.status.metrics or overwrite:
|
|
182
|
+
self.status.metrics[key] = [value]
|
|
183
|
+
else:
|
|
184
|
+
self.status.metrics[key].append(value)
|
|
185
|
+
|
|
186
|
+
def _handle_metric_list(self, key: str, value: list[int | float], overwrite: bool = False) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Handle metric list.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
key : str
|
|
193
|
+
Key of the metric.
|
|
194
|
+
value : list[int | float]
|
|
195
|
+
Value of the metric.
|
|
196
|
+
overwrite : bool
|
|
197
|
+
If True, overwrite existing metric.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
None
|
|
202
|
+
"""
|
|
203
|
+
if key not in self.status.metrics or overwrite:
|
|
204
|
+
self.status.metrics[key] = value
|
|
205
|
+
else:
|
|
206
|
+
self.status.metrics[key].extend(value)
|
|
@@ -13,16 +13,12 @@ class ModelSpec(MaterialSpec):
|
|
|
13
13
|
path: str,
|
|
14
14
|
framework: str | None = None,
|
|
15
15
|
algorithm: str | None = None,
|
|
16
|
-
base_model: str | None = None,
|
|
17
16
|
parameters: dict | None = None,
|
|
18
|
-
metrics: dict | None = None,
|
|
19
17
|
) -> None:
|
|
20
18
|
self.path = path
|
|
21
19
|
self.framework = framework
|
|
22
20
|
self.algorithm = algorithm
|
|
23
|
-
self.base_model = base_model
|
|
24
21
|
self.parameters = parameters
|
|
25
|
-
self.metrics = metrics
|
|
26
22
|
|
|
27
23
|
|
|
28
24
|
class ModelValidator(MaterialValidator):
|
|
@@ -39,11 +35,5 @@ class ModelValidator(MaterialValidator):
|
|
|
39
35
|
algorithm: str = None
|
|
40
36
|
"""Model algorithm (e.g. 'resnet')."""
|
|
41
37
|
|
|
42
|
-
base_model: str = None
|
|
43
|
-
"""Base model."""
|
|
44
|
-
|
|
45
38
|
parameters: dict = None
|
|
46
39
|
"""Model validator."""
|
|
47
|
-
|
|
48
|
-
metrics: dict = None
|
|
49
|
-
"""Model metrics."""
|
|
@@ -7,3 +7,13 @@ class ModelStatus(MaterialStatus):
|
|
|
7
7
|
"""
|
|
8
8
|
ModelStatus status.
|
|
9
9
|
"""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
state: str,
|
|
14
|
+
message: str | None = None,
|
|
15
|
+
files: list[dict] | None = None,
|
|
16
|
+
metrics: dict | None = None,
|
|
17
|
+
):
|
|
18
|
+
super().__init__(state, message, files)
|
|
19
|
+
self.metrics = metrics
|
|
@@ -157,7 +157,7 @@ def get_model(
|
|
|
157
157
|
>>> project="my-project",
|
|
158
158
|
>>> entity_id="my-model-id")
|
|
159
159
|
"""
|
|
160
|
-
return processor.
|
|
160
|
+
return processor.read_context_entity(
|
|
161
161
|
identifier=identifier,
|
|
162
162
|
entity_type=ENTITY_TYPE,
|
|
163
163
|
project=project,
|
|
@@ -197,7 +197,7 @@ def get_model_versions(
|
|
|
197
197
|
>>> objs = get_model_versions("my-model-name",
|
|
198
198
|
>>> project="my-project")
|
|
199
199
|
"""
|
|
200
|
-
return processor.
|
|
200
|
+
return processor.read_context_entity_versions(
|
|
201
201
|
identifier=identifier,
|
|
202
202
|
entity_type=ENTITY_TYPE,
|
|
203
203
|
project=project,
|
|
@@ -225,7 +225,7 @@ def list_models(project: str, **kwargs) -> list[Model]:
|
|
|
225
225
|
--------
|
|
226
226
|
>>> objs = list_models(project="my-project")
|
|
227
227
|
"""
|
|
228
|
-
return processor.
|
|
228
|
+
return processor.list_context_entities(
|
|
229
229
|
project=project,
|
|
230
230
|
entity_type=ENTITY_TYPE,
|
|
231
231
|
**kwargs,
|
|
@@ -15,13 +15,13 @@ class ModelSpecHuggingface(ModelSpec):
|
|
|
15
15
|
path: str,
|
|
16
16
|
framework: str | None = None,
|
|
17
17
|
algorithm: str | None = None,
|
|
18
|
-
base_model: str | None = None,
|
|
19
18
|
parameters: dict | None = None,
|
|
20
|
-
|
|
19
|
+
base_model: str | None = None,
|
|
21
20
|
model_id: str | None = None,
|
|
22
21
|
model_revision: str = None,
|
|
23
22
|
) -> None:
|
|
24
|
-
super().__init__(path, framework, algorithm,
|
|
23
|
+
super().__init__(path, framework, algorithm, parameters)
|
|
24
|
+
self.base_model = base_model
|
|
25
25
|
self.model_id = model_id
|
|
26
26
|
self.model_revision = model_revision
|
|
27
27
|
|
|
@@ -31,6 +31,9 @@ class ModelValidatorHuggingface(ModelValidator):
|
|
|
31
31
|
ModelValidatorHuggingface validator.
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
+
base_model: str = None
|
|
35
|
+
"""Base model."""
|
|
36
|
+
|
|
34
37
|
placeholder_model_id: str = Field(default=None, alias="model_id")
|
|
35
38
|
"""Huggingface model id. If not specified, the model is loaded from the model path."""
|
|
36
39
|
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Signature(BaseModel):
|
|
@@ -26,7 +26,7 @@ class Dataset(BaseModel):
|
|
|
26
26
|
name: Optional[str] = None
|
|
27
27
|
digest: Optional[str] = None
|
|
28
28
|
profile: Optional[str] = None
|
|
29
|
-
|
|
29
|
+
dataset_schema: Optional[str] = None
|
|
30
30
|
source: Optional[str] = None
|
|
31
31
|
source_type: Optional[str] = None
|
|
32
32
|
|
|
@@ -16,15 +16,13 @@ class ModelSpecMlflow(ModelSpec):
|
|
|
16
16
|
path: str,
|
|
17
17
|
framework: str | None = None,
|
|
18
18
|
algorithm: str | None = None,
|
|
19
|
-
base_model: str | None = None,
|
|
20
19
|
parameters: dict | None = None,
|
|
21
|
-
metrics: dict | None = None,
|
|
22
20
|
flavor: str | None = None,
|
|
23
21
|
model_config: dict | None = None,
|
|
24
22
|
input_datasets: list[Dataset] | None = None,
|
|
25
23
|
signature: Signature = None,
|
|
26
24
|
) -> None:
|
|
27
|
-
super().__init__(path, framework, algorithm,
|
|
25
|
+
super().__init__(path, framework, algorithm, parameters)
|
|
28
26
|
self.flavor = flavor
|
|
29
27
|
self.model_config = model_config
|
|
30
28
|
self.input_datasets = input_datasets
|
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import typing
|
|
3
4
|
from urllib.parse import urlparse
|
|
4
5
|
|
|
5
6
|
import mlflow
|
|
6
7
|
|
|
7
8
|
from digitalhub.entities.model.mlflow.models import Dataset, Signature
|
|
8
9
|
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from mlflow.entities import Run
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_mlflow_run(run_id: str) -> Run:
|
|
15
|
+
"""
|
|
16
|
+
Get MLFlow run.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
run_id : str
|
|
21
|
+
The id of the mlflow run.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
Run
|
|
26
|
+
The extracted run.
|
|
27
|
+
"""
|
|
28
|
+
return mlflow.MlflowClient().get_run(run_id)
|
|
29
|
+
|
|
9
30
|
|
|
10
31
|
def from_mlflow_run(run_id: str) -> dict:
|
|
11
32
|
"""
|
|
@@ -23,13 +44,11 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
23
44
|
"""
|
|
24
45
|
|
|
25
46
|
# Get MLFlow run
|
|
26
|
-
|
|
27
|
-
run = client.get_run(run_id)
|
|
47
|
+
run = get_mlflow_run(run_id)
|
|
28
48
|
|
|
29
49
|
# Extract spec
|
|
30
50
|
data = run.data
|
|
31
51
|
parameters = data.params
|
|
32
|
-
metrics = data.metrics
|
|
33
52
|
source_path = urlparse(run.info.artifact_uri).path
|
|
34
53
|
model_uri = f"runs:/{run_id}/model"
|
|
35
54
|
model = mlflow.pyfunc.load_model(model_uri=model_uri)
|
|
@@ -56,7 +75,7 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
56
75
|
name=d.dataset.name,
|
|
57
76
|
digest=d.dataset.digest,
|
|
58
77
|
profile=d.dataset.profile,
|
|
59
|
-
|
|
78
|
+
dataset_schema=d.dataset.schema,
|
|
60
79
|
source=d.dataset.source,
|
|
61
80
|
source_type=d.dataset.source_type,
|
|
62
81
|
).to_dict()
|
|
@@ -72,7 +91,6 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
72
91
|
# common properties
|
|
73
92
|
model_params["framework"] = flavor
|
|
74
93
|
model_params["parameters"] = parameters
|
|
75
|
-
model_params["metrics"] = metrics
|
|
76
94
|
|
|
77
95
|
# specific to MLFlow
|
|
78
96
|
model_params["flavor"] = flavor
|
|
@@ -81,3 +99,24 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
81
99
|
model_params["signature"] = signature
|
|
82
100
|
|
|
83
101
|
return model_params
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_mlflow_model_metrics(run_id: str) -> dict:
|
|
105
|
+
"""
|
|
106
|
+
Get MLFlow model metrics for a given run id.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
run_id : str
|
|
111
|
+
The id of the mlflow run.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
dict
|
|
116
|
+
The extracted metrics.
|
|
117
|
+
"""
|
|
118
|
+
# Get MLFlow run
|
|
119
|
+
run = get_mlflow_run(run_id)
|
|
120
|
+
|
|
121
|
+
# Extract metrics
|
|
122
|
+
return run.data.metrics
|
|
@@ -5,6 +5,7 @@ import typing
|
|
|
5
5
|
|
|
6
6
|
from digitalhub.entities._base.unversioned.entity import UnversionedEntity
|
|
7
7
|
from digitalhub.entities._commons.enums import EntityTypes, State
|
|
8
|
+
from digitalhub.entities._commons.utils import validate_metric_value
|
|
8
9
|
from digitalhub.entities._operations.processor import processor
|
|
9
10
|
from digitalhub.factory.api import (
|
|
10
11
|
build_runtime,
|
|
@@ -45,6 +46,9 @@ class Run(UnversionedEntity):
|
|
|
45
46
|
self.spec: RunSpec
|
|
46
47
|
self.status: RunStatus
|
|
47
48
|
|
|
49
|
+
# Initialize metrics
|
|
50
|
+
self._init_metrics()
|
|
51
|
+
|
|
48
52
|
##############################
|
|
49
53
|
# Run Methods
|
|
50
54
|
##############################
|
|
@@ -161,6 +165,62 @@ class Run(UnversionedEntity):
|
|
|
161
165
|
if not self.spec.local_execution:
|
|
162
166
|
return processor.resume_run(self.project, self.ENTITY_TYPE, self.id)
|
|
163
167
|
|
|
168
|
+
def log_metric(
|
|
169
|
+
self,
|
|
170
|
+
key: str,
|
|
171
|
+
value: list[float | int] | float | int,
|
|
172
|
+
overwrite: bool = False,
|
|
173
|
+
single_value: bool = False,
|
|
174
|
+
) -> None:
|
|
175
|
+
"""
|
|
176
|
+
Log metric into entity status.
|
|
177
|
+
A metric is named by a key and value (single number or list of numbers).
|
|
178
|
+
The metric by default is put in a list or appended to an existing list.
|
|
179
|
+
If single_value is True, the value will be a single number.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
key : str
|
|
184
|
+
Key of the metric.
|
|
185
|
+
value : list[float | int] | float | int
|
|
186
|
+
Value of the metric.
|
|
187
|
+
overwrite : bool
|
|
188
|
+
If True, overwrite existing metric.
|
|
189
|
+
single_value : bool
|
|
190
|
+
If True, value is a single value.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
None
|
|
195
|
+
|
|
196
|
+
Examples
|
|
197
|
+
--------
|
|
198
|
+
Log a new value in a list
|
|
199
|
+
>>> entity.log_metric("loss", 0.002)
|
|
200
|
+
|
|
201
|
+
Append a new value in a list
|
|
202
|
+
>>> entity.log_metric("loss", 0.0019)
|
|
203
|
+
|
|
204
|
+
Log a list of values and append them to existing metric:
|
|
205
|
+
>>> entity.log_metric("loss", [0.0018, 0.0015])
|
|
206
|
+
|
|
207
|
+
Log a single value (not represented as list):
|
|
208
|
+
>>> entity.log_metric("accuracy", 0.9, single_value=True)
|
|
209
|
+
|
|
210
|
+
Log a list of values and overwrite existing metric:
|
|
211
|
+
>>> entity.log_metric("accuracy", [0.8, 0.9], overwrite=True)
|
|
212
|
+
"""
|
|
213
|
+
value = validate_metric_value(value)
|
|
214
|
+
|
|
215
|
+
if isinstance(value, list):
|
|
216
|
+
self._handle_metric_list(key, value, overwrite)
|
|
217
|
+
elif single_value:
|
|
218
|
+
self._handle_metric_single(key, value, overwrite)
|
|
219
|
+
else:
|
|
220
|
+
self._handle_metric_list_append(key, value, overwrite)
|
|
221
|
+
|
|
222
|
+
processor.update_metric(self.project, self.ENTITY_TYPE, self.id, key, self.status.metrics[key])
|
|
223
|
+
|
|
164
224
|
##############################
|
|
165
225
|
# Helpers
|
|
166
226
|
##############################
|
|
@@ -303,3 +363,92 @@ class Run(UnversionedEntity):
|
|
|
303
363
|
entity_type=EntityTypes.TASK.value,
|
|
304
364
|
project=self.project,
|
|
305
365
|
).to_dict()
|
|
366
|
+
|
|
367
|
+
def _init_metrics(self) -> None:
|
|
368
|
+
"""
|
|
369
|
+
Initialize metrics.
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
None
|
|
374
|
+
"""
|
|
375
|
+
if self.status.metrics is None:
|
|
376
|
+
self.status.metrics = {}
|
|
377
|
+
|
|
378
|
+
def _get_metrics(self) -> None:
|
|
379
|
+
"""
|
|
380
|
+
Get run metrics from backend.
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
None
|
|
385
|
+
"""
|
|
386
|
+
self.status.metrics = processor.read_metrics(
|
|
387
|
+
project=self.project,
|
|
388
|
+
entity_type=self.ENTITY_TYPE,
|
|
389
|
+
entity_id=self.id,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
def _handle_metric_single(self, key: str, value: float | int, overwrite: bool = False) -> None:
|
|
393
|
+
"""
|
|
394
|
+
Handle metric single value.
|
|
395
|
+
|
|
396
|
+
Parameters
|
|
397
|
+
----------
|
|
398
|
+
key : str
|
|
399
|
+
Key of the metric.
|
|
400
|
+
value : float
|
|
401
|
+
Value of the metric.
|
|
402
|
+
overwrite : bool
|
|
403
|
+
If True, overwrite existing metric.
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
None
|
|
408
|
+
"""
|
|
409
|
+
if key not in self.status.metrics or overwrite:
|
|
410
|
+
self.status.metrics[key] = value
|
|
411
|
+
|
|
412
|
+
def _handle_metric_list_append(self, key: str, value: float | int, overwrite: bool = False) -> None:
|
|
413
|
+
"""
|
|
414
|
+
Handle metric list append.
|
|
415
|
+
|
|
416
|
+
Parameters
|
|
417
|
+
----------
|
|
418
|
+
key : str
|
|
419
|
+
Key of the metric.
|
|
420
|
+
value : float
|
|
421
|
+
Value of the metric.
|
|
422
|
+
overwrite : bool
|
|
423
|
+
If True, overwrite existing metric.
|
|
424
|
+
|
|
425
|
+
Returns
|
|
426
|
+
-------
|
|
427
|
+
None
|
|
428
|
+
"""
|
|
429
|
+
if key not in self.status.metrics or overwrite:
|
|
430
|
+
self.status.metrics[key] = [value]
|
|
431
|
+
else:
|
|
432
|
+
self.status.metrics[key].append(value)
|
|
433
|
+
|
|
434
|
+
def _handle_metric_list(self, key: str, value: list[int | float], overwrite: bool = False) -> None:
|
|
435
|
+
"""
|
|
436
|
+
Handle metric list.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
key : str
|
|
441
|
+
Key of the metric.
|
|
442
|
+
value : list[int | float]
|
|
443
|
+
Value of the metric.
|
|
444
|
+
overwrite : bool
|
|
445
|
+
If True, overwrite existing metric.
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
None
|
|
450
|
+
"""
|
|
451
|
+
if key not in self.status.metrics or overwrite:
|
|
452
|
+
self.status.metrics[key] = value
|
|
453
|
+
else:
|
|
454
|
+
self.status.metrics[key].extend(value)
|
|
@@ -7,3 +7,15 @@ class RunStatus(Status):
|
|
|
7
7
|
"""
|
|
8
8
|
RunStatus status.
|
|
9
9
|
"""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
state: str,
|
|
14
|
+
message: str | None = None,
|
|
15
|
+
transitions: list[dict] | None = None,
|
|
16
|
+
k8s: dict | None = None,
|
|
17
|
+
metrics: dict | None = None,
|
|
18
|
+
**kwargs,
|
|
19
|
+
) -> None:
|
|
20
|
+
super().__init__(state, message, transitions, k8s, **kwargs)
|
|
21
|
+
self.metrics = metrics
|
digitalhub/entities/task/crud.py
CHANGED
|
@@ -19,6 +19,7 @@ def new_task(
|
|
|
19
19
|
uuid: str | None = None,
|
|
20
20
|
labels: list[str] | None = None,
|
|
21
21
|
function: str | None = None,
|
|
22
|
+
workflow: str | None = None,
|
|
22
23
|
**kwargs,
|
|
23
24
|
) -> Task:
|
|
24
25
|
"""
|
|
@@ -36,6 +37,8 @@ def new_task(
|
|
|
36
37
|
List of labels.
|
|
37
38
|
function : str
|
|
38
39
|
Name of the executable associated with the task.
|
|
40
|
+
workflow : str
|
|
41
|
+
Name of the workflow associated with the task.
|
|
39
42
|
**kwargs : dict
|
|
40
43
|
Spec keyword arguments.
|
|
41
44
|
|
|
@@ -56,6 +59,7 @@ def new_task(
|
|
|
56
59
|
uuid=uuid,
|
|
57
60
|
labels=labels,
|
|
58
61
|
function=function,
|
|
62
|
+
workflow=workflow,
|
|
59
63
|
**kwargs,
|
|
60
64
|
)
|
|
61
65
|
|