mlflow-optimization 0.1.0__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.
- mlflow_optimization/__init__.py +50 -0
- mlflow_optimization/_bootstrap.py +129 -0
- mlflow_optimization/common/diagnostics.py +19 -0
- mlflow_optimization/common/patching.py +82 -0
- mlflow_optimization/features/annotations/annotate.py +97 -0
- mlflow_optimization/features/annotations/types.py +22 -0
- mlflow_optimization/features/artifacts/policy.py +16 -0
- mlflow_optimization/features/artifacts/ports.py +17 -0
- mlflow_optimization/features/autolog/ports.py +49 -0
- mlflow_optimization/features/autolog/service.py +147 -0
- mlflow_optimization/features/autolog/types.py +31 -0
- mlflow_optimization/features/metadata/collect.py +52 -0
- mlflow_optimization/features/metadata/types.py +42 -0
- mlflow_optimization/features/metrics/normalize.py +121 -0
- mlflow_optimization/features/metrics/types.py +54 -0
- mlflow_optimization/features/outcomes/classify.py +53 -0
- mlflow_optimization/features/outcomes/types.py +40 -0
- mlflow_optimization/features/runs/ports.py +30 -0
- mlflow_optimization/features/runs/record.py +231 -0
- mlflow_optimization/features/runs/types.py +43 -0
- mlflow_optimization/integrations/cp_sat/__init__.py +5 -0
- mlflow_optimization/integrations/cp_sat/adapter.py +464 -0
- mlflow_optimization/integrations/gurobipy/__init__.py +5 -0
- mlflow_optimization/integrations/gurobipy/adapter.py +474 -0
- mlflow_optimization/integrations/mathopt/__init__.py +1 -0
- mlflow_optimization/integrations/mathopt/adapter.py +939 -0
- mlflow_optimization/integrations/mlflow/artifacts.py +44 -0
- mlflow_optimization/integrations/mlflow/runs.py +480 -0
- mlflow_optimization/integrations/pyomo/__init__.py +5 -0
- mlflow_optimization/integrations/pyomo/adapter.py +1499 -0
- mlflow_optimization/integrations/pyscipopt/__init__.py +5 -0
- mlflow_optimization/integrations/pyscipopt/adapter.py +657 -0
- mlflow_optimization/integrations/routing/__init__.py +5 -0
- mlflow_optimization/integrations/routing/adapter.py +429 -0
- mlflow_optimization/py.typed +0 -0
- mlflow_optimization-0.1.0.dist-info/METADATA +52 -0
- mlflow_optimization-0.1.0.dist-info/RECORD +40 -0
- mlflow_optimization-0.1.0.dist-info/WHEEL +4 -0
- mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-APACHE +176 -0
- mlflow_optimization-0.1.0.dist-info/licenses/LICENSE-MIT +21 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""MLflow implementation of the artifact storage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
import mlflow
|
|
8
|
+
from mlflow.tracking import MlflowClient
|
|
9
|
+
|
|
10
|
+
from mlflow_optimization.features.artifacts.ports import ArtifactStoreError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MlflowArtifactStore:
|
|
14
|
+
"""Run artifacts stored through the MLflow tracking client."""
|
|
15
|
+
|
|
16
|
+
def log_artifact(
|
|
17
|
+
self: MlflowArtifactStore,
|
|
18
|
+
run_id: str,
|
|
19
|
+
local_path: str,
|
|
20
|
+
artifact_path: str | None,
|
|
21
|
+
) -> None:
|
|
22
|
+
tracking_uri: str = _artifact_store_sdk_call(mlflow.get_tracking_uri)
|
|
23
|
+
tracking_client: MlflowClient = _artifact_store_sdk_call(
|
|
24
|
+
lambda: MlflowClient(tracking_uri=tracking_uri)
|
|
25
|
+
)
|
|
26
|
+
_artifact_store_sdk_call(
|
|
27
|
+
lambda: tracking_client.log_artifact(
|
|
28
|
+
run_id=run_id,
|
|
29
|
+
local_path=local_path,
|
|
30
|
+
artifact_path=artifact_path,
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _artifact_store_sdk_call[ReturnType](
|
|
36
|
+
mlflow_call: Callable[[], ReturnType],
|
|
37
|
+
) -> ReturnType:
|
|
38
|
+
"""An MLflow SDK call converted to an artifact-store error."""
|
|
39
|
+
try:
|
|
40
|
+
return mlflow_call()
|
|
41
|
+
except Exception as error:
|
|
42
|
+
raise ArtifactStoreError(
|
|
43
|
+
f"Failed while logging an artifact: {type(error).__name__}: {error}"
|
|
44
|
+
) from error
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"""MLflow implementation of the run storage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import zlib
|
|
7
|
+
from collections.abc import Callable, Mapping
|
|
8
|
+
from functools import partial
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import mlflow
|
|
12
|
+
from mlflow.entities import Metric, Param, Run, RunTag
|
|
13
|
+
from mlflow.protos.service_pb2 import LogBatch
|
|
14
|
+
from mlflow.tracking import MlflowClient
|
|
15
|
+
from mlflow.tracking.context.registry import resolve_tags
|
|
16
|
+
from mlflow.tracking.fluent import ActiveRun, _get_experiment_id
|
|
17
|
+
from mlflow.utils.mlflow_tags import MLFLOW_PARENT_RUN_ID, MLFLOW_RUN_NAME
|
|
18
|
+
from mlflow.utils.proto_json_utils import message_to_json
|
|
19
|
+
from mlflow.utils.validation import (
|
|
20
|
+
MAX_BATCH_LOG_REQUEST_SIZE,
|
|
21
|
+
MAX_ENTITY_KEY_LENGTH,
|
|
22
|
+
MAX_METRICS_PER_BATCH,
|
|
23
|
+
MAX_PARAM_VAL_LENGTH,
|
|
24
|
+
MAX_PARAMS_TAGS_PER_BATCH,
|
|
25
|
+
MAX_TAG_VAL_LENGTH,
|
|
26
|
+
path_not_unique,
|
|
27
|
+
validate_param_and_metric_name,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from mlflow_optimization.common.diagnostics import warn
|
|
31
|
+
from mlflow_optimization.features.runs.ports import RunStoreError
|
|
32
|
+
from mlflow_optimization.features.runs.types import RunReference, SolveRecord
|
|
33
|
+
|
|
34
|
+
_ENCODED_KEY_PREFIX: str = "_mlflow_key_"
|
|
35
|
+
_BATCH_LOG_REQUEST_SIZE_SAFETY_MARGIN: int = MAX_BATCH_LOG_REQUEST_SIZE // 10
|
|
36
|
+
_MAX_BATCH_LOG_REQUEST_SIZE_BUDGET: int = (
|
|
37
|
+
MAX_BATCH_LOG_REQUEST_SIZE - _BATCH_LOG_REQUEST_SIZE_SAFETY_MARGIN
|
|
38
|
+
)
|
|
39
|
+
_SOLVE_IDENTITY_TAG_KEYS: tuple[str, ...] = (
|
|
40
|
+
"optimization.solve_id",
|
|
41
|
+
"optimization.solve_index",
|
|
42
|
+
"optimization.error",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MlflowRunStore:
|
|
47
|
+
"""Solve runs stored through the MLflow tracking client."""
|
|
48
|
+
|
|
49
|
+
def active_run(self: MlflowRunStore) -> RunReference | None:
|
|
50
|
+
active_mlflow_run: ActiveRun | None = _run_store_sdk_call(
|
|
51
|
+
"reading the active run",
|
|
52
|
+
mlflow.active_run,
|
|
53
|
+
)
|
|
54
|
+
if active_mlflow_run is None:
|
|
55
|
+
return None
|
|
56
|
+
active_run_id: str = _run_store_sdk_call(
|
|
57
|
+
"reading the active run",
|
|
58
|
+
lambda: active_mlflow_run.info.run_id,
|
|
59
|
+
)
|
|
60
|
+
active_experiment_id: str = _run_store_sdk_call(
|
|
61
|
+
"reading the active run",
|
|
62
|
+
lambda: active_mlflow_run.info.experiment_id,
|
|
63
|
+
)
|
|
64
|
+
return RunReference(
|
|
65
|
+
run_id=active_run_id,
|
|
66
|
+
experiment_id=active_experiment_id,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def save(
|
|
70
|
+
self: MlflowRunStore, record: SolveRecord, parent: RunReference | None
|
|
71
|
+
) -> RunReference:
|
|
72
|
+
tracking_uri: str = _run_store_sdk_call(
|
|
73
|
+
"saving the run", mlflow.get_tracking_uri
|
|
74
|
+
)
|
|
75
|
+
tracking_client: MlflowClient = _run_store_sdk_call(
|
|
76
|
+
"saving the run",
|
|
77
|
+
lambda: MlflowClient(tracking_uri=tracking_uri),
|
|
78
|
+
)
|
|
79
|
+
experiment_id: str
|
|
80
|
+
if parent is None:
|
|
81
|
+
resolved_experiment_id: str | None = _run_store_sdk_call(
|
|
82
|
+
"saving the run",
|
|
83
|
+
_get_experiment_id,
|
|
84
|
+
)
|
|
85
|
+
if resolved_experiment_id is None:
|
|
86
|
+
raise RunStoreError(
|
|
87
|
+
"Failed while saving the run: MLflow did not resolve an experiment ID."
|
|
88
|
+
)
|
|
89
|
+
experiment_id = resolved_experiment_id
|
|
90
|
+
else:
|
|
91
|
+
experiment_id = parent.experiment_id
|
|
92
|
+
|
|
93
|
+
run_name: str = _truncate_tag_value(record.name)
|
|
94
|
+
resolved_tag_values: dict[str, Any] = dict(record.tags)
|
|
95
|
+
if parent is not None:
|
|
96
|
+
resolved_tag_values[MLFLOW_PARENT_RUN_ID] = parent.run_id
|
|
97
|
+
if run_name:
|
|
98
|
+
resolved_tag_values[MLFLOW_RUN_NAME] = run_name
|
|
99
|
+
context_tag_values: dict[str, Any] = _run_store_sdk_call(
|
|
100
|
+
"saving the run",
|
|
101
|
+
lambda: resolve_tags(resolved_tag_values),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
creation_tag_values: dict[str, str] = {}
|
|
105
|
+
for identity_tag_key in _SOLVE_IDENTITY_TAG_KEYS:
|
|
106
|
+
if identity_tag_key in context_tag_values:
|
|
107
|
+
creation_tag_values[identity_tag_key] = _truncate_tag_value(
|
|
108
|
+
str(context_tag_values[identity_tag_key])
|
|
109
|
+
)
|
|
110
|
+
if parent is not None:
|
|
111
|
+
creation_tag_values[MLFLOW_PARENT_RUN_ID] = parent.run_id
|
|
112
|
+
|
|
113
|
+
created_run: Run = _run_store_sdk_call(
|
|
114
|
+
"saving the run",
|
|
115
|
+
lambda: tracking_client.create_run(
|
|
116
|
+
experiment_id=experiment_id,
|
|
117
|
+
start_time=record.start_time_ms,
|
|
118
|
+
tags=creation_tag_values,
|
|
119
|
+
run_name=run_name,
|
|
120
|
+
),
|
|
121
|
+
)
|
|
122
|
+
created_run_id: str = _run_store_sdk_call(
|
|
123
|
+
"saving the run",
|
|
124
|
+
lambda: created_run.info.run_id,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
metric_entities: list[Metric] = []
|
|
129
|
+
for metric_key, metric_value in record.metrics.items():
|
|
130
|
+
encoded_metric_key: str | None = _encode_key(
|
|
131
|
+
metric_key, "saving the run"
|
|
132
|
+
)
|
|
133
|
+
if encoded_metric_key is None:
|
|
134
|
+
continue
|
|
135
|
+
metric_entities.append(
|
|
136
|
+
_run_store_sdk_call(
|
|
137
|
+
"saving the run",
|
|
138
|
+
partial(
|
|
139
|
+
Metric,
|
|
140
|
+
key=encoded_metric_key,
|
|
141
|
+
value=metric_value,
|
|
142
|
+
timestamp=record.end_time_ms,
|
|
143
|
+
step=0,
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
parameter_entities: list[Param] = []
|
|
149
|
+
for parameter_key, parameter_value in record.params.items():
|
|
150
|
+
encoded_parameter_key: str | None = _encode_key(
|
|
151
|
+
parameter_key,
|
|
152
|
+
"saving the run",
|
|
153
|
+
)
|
|
154
|
+
if encoded_parameter_key is None:
|
|
155
|
+
continue
|
|
156
|
+
parameter_entities.append(
|
|
157
|
+
_run_store_sdk_call(
|
|
158
|
+
"saving the run",
|
|
159
|
+
partial(
|
|
160
|
+
Param,
|
|
161
|
+
key=encoded_parameter_key,
|
|
162
|
+
value=parameter_value[:MAX_PARAM_VAL_LENGTH],
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
self._log_metric_batches(tracking_client, created_run_id, metric_entities)
|
|
168
|
+
self._log_parameter_batches(
|
|
169
|
+
tracking_client,
|
|
170
|
+
created_run_id,
|
|
171
|
+
parameter_entities,
|
|
172
|
+
)
|
|
173
|
+
self._log_tag_batches(
|
|
174
|
+
tracking_client,
|
|
175
|
+
created_run_id,
|
|
176
|
+
_make_tag_entities(context_tag_values, "saving the run"),
|
|
177
|
+
"saving the run",
|
|
178
|
+
)
|
|
179
|
+
finally:
|
|
180
|
+
_run_store_sdk_call(
|
|
181
|
+
"saving the run",
|
|
182
|
+
lambda: tracking_client.set_terminated(
|
|
183
|
+
run_id=created_run_id,
|
|
184
|
+
status=record.status.value,
|
|
185
|
+
end_time=record.end_time_ms,
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
created_experiment_id: str = _run_store_sdk_call(
|
|
189
|
+
"saving the run",
|
|
190
|
+
lambda: created_run.info.experiment_id,
|
|
191
|
+
)
|
|
192
|
+
return RunReference(
|
|
193
|
+
run_id=created_run_id,
|
|
194
|
+
experiment_id=created_experiment_id,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def log_metric(
|
|
198
|
+
self: MlflowRunStore, run: RunReference, key: str, value: float
|
|
199
|
+
) -> None:
|
|
200
|
+
tracking_uri: str = _run_store_sdk_call(
|
|
201
|
+
"logging a metric", mlflow.get_tracking_uri
|
|
202
|
+
)
|
|
203
|
+
tracking_client: MlflowClient = _run_store_sdk_call(
|
|
204
|
+
"logging a metric",
|
|
205
|
+
lambda: MlflowClient(tracking_uri=tracking_uri),
|
|
206
|
+
)
|
|
207
|
+
encoded_key: str | None = _encode_key(key, "logging a metric")
|
|
208
|
+
if encoded_key is None:
|
|
209
|
+
return
|
|
210
|
+
_run_store_sdk_call(
|
|
211
|
+
"logging a metric",
|
|
212
|
+
lambda: tracking_client.log_metric(
|
|
213
|
+
run_id=run.run_id,
|
|
214
|
+
key=encoded_key,
|
|
215
|
+
value=value,
|
|
216
|
+
synchronous=True,
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def set_tags(
|
|
221
|
+
self: MlflowRunStore, run: RunReference, tags: Mapping[str, str]
|
|
222
|
+
) -> None:
|
|
223
|
+
tracking_uri: str = _run_store_sdk_call("setting tags", mlflow.get_tracking_uri)
|
|
224
|
+
tracking_client: MlflowClient = _run_store_sdk_call(
|
|
225
|
+
"setting tags",
|
|
226
|
+
lambda: MlflowClient(tracking_uri=tracking_uri),
|
|
227
|
+
)
|
|
228
|
+
self._log_tag_batches(
|
|
229
|
+
tracking_client,
|
|
230
|
+
run.run_id,
|
|
231
|
+
_make_tag_entities(tags, "setting tags"),
|
|
232
|
+
"setting tags",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def _log_metric_batches(
|
|
236
|
+
self: MlflowRunStore,
|
|
237
|
+
tracking_client: MlflowClient,
|
|
238
|
+
run_id: str,
|
|
239
|
+
metric_entities: list[Metric],
|
|
240
|
+
) -> None:
|
|
241
|
+
current_batch: list[Metric] = []
|
|
242
|
+
for metric_entity in metric_entities:
|
|
243
|
+
candidate_batch: list[Metric] = [*current_batch, metric_entity]
|
|
244
|
+
if current_batch and (
|
|
245
|
+
len(current_batch) >= MAX_METRICS_PER_BATCH
|
|
246
|
+
or _run_store_sdk_call(
|
|
247
|
+
"saving the run",
|
|
248
|
+
partial(
|
|
249
|
+
_serialized_batch_request_size,
|
|
250
|
+
run_id,
|
|
251
|
+
candidate_batch,
|
|
252
|
+
[],
|
|
253
|
+
[],
|
|
254
|
+
),
|
|
255
|
+
)
|
|
256
|
+
> _MAX_BATCH_LOG_REQUEST_SIZE_BUDGET
|
|
257
|
+
):
|
|
258
|
+
_run_store_sdk_call(
|
|
259
|
+
"saving the run",
|
|
260
|
+
partial(
|
|
261
|
+
tracking_client.log_batch,
|
|
262
|
+
run_id=run_id,
|
|
263
|
+
metrics=current_batch,
|
|
264
|
+
synchronous=True,
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
current_batch = []
|
|
268
|
+
current_batch.append(metric_entity)
|
|
269
|
+
if current_batch:
|
|
270
|
+
_run_store_sdk_call(
|
|
271
|
+
"saving the run",
|
|
272
|
+
partial(
|
|
273
|
+
tracking_client.log_batch,
|
|
274
|
+
run_id=run_id,
|
|
275
|
+
metrics=current_batch,
|
|
276
|
+
synchronous=True,
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def _log_parameter_batches(
|
|
281
|
+
self: MlflowRunStore,
|
|
282
|
+
tracking_client: MlflowClient,
|
|
283
|
+
run_id: str,
|
|
284
|
+
parameter_entities: list[Param],
|
|
285
|
+
) -> None:
|
|
286
|
+
current_batch: list[Param] = []
|
|
287
|
+
for parameter_entity in parameter_entities:
|
|
288
|
+
candidate_batch: list[Param] = [*current_batch, parameter_entity]
|
|
289
|
+
if current_batch and (
|
|
290
|
+
len(current_batch) >= MAX_PARAMS_TAGS_PER_BATCH
|
|
291
|
+
or _run_store_sdk_call(
|
|
292
|
+
"saving the run",
|
|
293
|
+
partial(
|
|
294
|
+
_serialized_batch_request_size,
|
|
295
|
+
run_id,
|
|
296
|
+
[],
|
|
297
|
+
candidate_batch,
|
|
298
|
+
[],
|
|
299
|
+
),
|
|
300
|
+
)
|
|
301
|
+
> _MAX_BATCH_LOG_REQUEST_SIZE_BUDGET
|
|
302
|
+
):
|
|
303
|
+
_run_store_sdk_call(
|
|
304
|
+
"saving the run",
|
|
305
|
+
partial(
|
|
306
|
+
tracking_client.log_batch,
|
|
307
|
+
run_id=run_id,
|
|
308
|
+
params=current_batch,
|
|
309
|
+
synchronous=True,
|
|
310
|
+
),
|
|
311
|
+
)
|
|
312
|
+
current_batch = []
|
|
313
|
+
current_batch.append(parameter_entity)
|
|
314
|
+
if current_batch:
|
|
315
|
+
_run_store_sdk_call(
|
|
316
|
+
"saving the run",
|
|
317
|
+
partial(
|
|
318
|
+
tracking_client.log_batch,
|
|
319
|
+
run_id=run_id,
|
|
320
|
+
params=current_batch,
|
|
321
|
+
synchronous=True,
|
|
322
|
+
),
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
def _log_tag_batches(
|
|
326
|
+
self: MlflowRunStore,
|
|
327
|
+
tracking_client: MlflowClient,
|
|
328
|
+
run_id: str,
|
|
329
|
+
tag_entities: list[RunTag],
|
|
330
|
+
operation: str,
|
|
331
|
+
) -> None:
|
|
332
|
+
current_batch: list[RunTag] = []
|
|
333
|
+
for tag_entity in tag_entities:
|
|
334
|
+
candidate_batch: list[RunTag] = [*current_batch, tag_entity]
|
|
335
|
+
if current_batch and (
|
|
336
|
+
len(current_batch) >= MAX_PARAMS_TAGS_PER_BATCH
|
|
337
|
+
or _run_store_sdk_call(
|
|
338
|
+
operation,
|
|
339
|
+
partial(
|
|
340
|
+
_serialized_batch_request_size,
|
|
341
|
+
run_id,
|
|
342
|
+
[],
|
|
343
|
+
[],
|
|
344
|
+
candidate_batch,
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
> _MAX_BATCH_LOG_REQUEST_SIZE_BUDGET
|
|
348
|
+
):
|
|
349
|
+
_run_store_sdk_call(
|
|
350
|
+
operation,
|
|
351
|
+
partial(
|
|
352
|
+
tracking_client.log_batch,
|
|
353
|
+
run_id=run_id,
|
|
354
|
+
tags=current_batch,
|
|
355
|
+
synchronous=True,
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
current_batch = []
|
|
359
|
+
current_batch.append(tag_entity)
|
|
360
|
+
if current_batch:
|
|
361
|
+
_run_store_sdk_call(
|
|
362
|
+
operation,
|
|
363
|
+
partial(
|
|
364
|
+
tracking_client.log_batch,
|
|
365
|
+
run_id=run_id,
|
|
366
|
+
tags=current_batch,
|
|
367
|
+
synchronous=True,
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _encode_key(key: str, operation: str = "saving the run") -> str | None:
|
|
373
|
+
valid_key: bool = bool(
|
|
374
|
+
_run_store_sdk_call(
|
|
375
|
+
operation,
|
|
376
|
+
lambda: validate_param_and_metric_name(key),
|
|
377
|
+
)
|
|
378
|
+
)
|
|
379
|
+
unique_path: bool = not _run_store_sdk_call(
|
|
380
|
+
operation,
|
|
381
|
+
lambda: path_not_unique(key),
|
|
382
|
+
)
|
|
383
|
+
if (
|
|
384
|
+
valid_key
|
|
385
|
+
and unique_path
|
|
386
|
+
and len(key) <= MAX_ENTITY_KEY_LENGTH
|
|
387
|
+
and not key.startswith(_ENCODED_KEY_PREFIX)
|
|
388
|
+
):
|
|
389
|
+
return key
|
|
390
|
+
|
|
391
|
+
encoded_characters: list[str] = []
|
|
392
|
+
for character in key:
|
|
393
|
+
if character == "_":
|
|
394
|
+
encoded_characters.append("__")
|
|
395
|
+
elif character == "/":
|
|
396
|
+
encoded_characters.append("_x2F_")
|
|
397
|
+
elif _run_store_sdk_call(
|
|
398
|
+
operation,
|
|
399
|
+
partial(validate_param_and_metric_name, character),
|
|
400
|
+
):
|
|
401
|
+
encoded_characters.append(character)
|
|
402
|
+
else:
|
|
403
|
+
encoded_characters.append(f"_x{ord(character):X}_")
|
|
404
|
+
|
|
405
|
+
escaped_key: str = _ENCODED_KEY_PREFIX + "e_" + "".join(encoded_characters)
|
|
406
|
+
if len(escaped_key) <= MAX_ENTITY_KEY_LENGTH:
|
|
407
|
+
return escaped_key
|
|
408
|
+
|
|
409
|
+
key_bytes: bytes = key.encode("utf-8", errors="surrogatepass")
|
|
410
|
+
compressed_key_bytes: bytes = zlib.compress(key_bytes, level=9)
|
|
411
|
+
base64_key_bytes: bytes = base64.urlsafe_b64encode(compressed_key_bytes)
|
|
412
|
+
compressed_key: str = (
|
|
413
|
+
_ENCODED_KEY_PREFIX + "z_" + base64_key_bytes.decode("ascii").rstrip("=")
|
|
414
|
+
)
|
|
415
|
+
if len(compressed_key) > MAX_ENTITY_KEY_LENGTH:
|
|
416
|
+
warn(f"Skipping MLflow key {key!r} because it exceeds the tracker key limit.")
|
|
417
|
+
return None
|
|
418
|
+
return compressed_key
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _truncate_tag_value(value: str) -> str:
|
|
422
|
+
return value[:MAX_TAG_VAL_LENGTH]
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _make_tag_entities(tag_values: Mapping[str, Any], operation: str) -> list[RunTag]:
|
|
426
|
+
tag_entities: list[RunTag] = []
|
|
427
|
+
for tag_key, tag_value in tag_values.items():
|
|
428
|
+
encoded_tag_key: str | None = _encode_key(tag_key, operation)
|
|
429
|
+
if encoded_tag_key is None:
|
|
430
|
+
continue
|
|
431
|
+
truncated_tag_value: str = _truncate_tag_value(str(tag_value))
|
|
432
|
+
tag_entities.append(
|
|
433
|
+
_run_store_sdk_call(
|
|
434
|
+
operation,
|
|
435
|
+
partial(
|
|
436
|
+
RunTag,
|
|
437
|
+
key=encoded_tag_key,
|
|
438
|
+
value=truncated_tag_value,
|
|
439
|
+
),
|
|
440
|
+
)
|
|
441
|
+
)
|
|
442
|
+
return tag_entities
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _serialized_batch_request_size(
|
|
446
|
+
run_id: str,
|
|
447
|
+
metric_entities: list[Metric],
|
|
448
|
+
parameter_entities: list[Param],
|
|
449
|
+
tag_entities: list[RunTag],
|
|
450
|
+
) -> int:
|
|
451
|
+
metric_protocol_buffers: list[Any] = []
|
|
452
|
+
for metric_entity in metric_entities:
|
|
453
|
+
metric_protocol_buffers.append(metric_entity.to_proto())
|
|
454
|
+
parameter_protocol_buffers: list[Any] = []
|
|
455
|
+
for parameter_entity in parameter_entities:
|
|
456
|
+
parameter_protocol_buffers.append(parameter_entity.to_proto())
|
|
457
|
+
tag_protocol_buffers: list[Any] = []
|
|
458
|
+
for tag_entity in tag_entities:
|
|
459
|
+
tag_protocol_buffers.append(tag_entity.to_proto())
|
|
460
|
+
batch_message: LogBatch = LogBatch(
|
|
461
|
+
run_id=run_id,
|
|
462
|
+
metrics=metric_protocol_buffers,
|
|
463
|
+
params=parameter_protocol_buffers,
|
|
464
|
+
tags=tag_protocol_buffers,
|
|
465
|
+
)
|
|
466
|
+
serialized_request: str = message_to_json(batch_message)
|
|
467
|
+
return len(serialized_request.encode("utf-8"))
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _run_store_sdk_call[ReturnType](
|
|
471
|
+
operation: str,
|
|
472
|
+
mlflow_call: Callable[[], ReturnType],
|
|
473
|
+
) -> ReturnType:
|
|
474
|
+
"""An MLflow SDK call converted to a run-store error."""
|
|
475
|
+
try:
|
|
476
|
+
return mlflow_call()
|
|
477
|
+
except Exception as error:
|
|
478
|
+
raise RunStoreError(
|
|
479
|
+
f"Failed while {operation}: {type(error).__name__}: {error}"
|
|
480
|
+
) from error
|