nextmv 0.32.0__py3-none-any.whl → 0.33.0.dev0__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.
- nextmv/__about__.py +1 -1
- nextmv/cloud/__init__.py +7 -0
- nextmv/cloud/acceptance_test.py +60 -6
- nextmv/cloud/application.py +237 -46
- nextmv/cloud/ensemble.py +248 -0
- nextmv/local/executor.py +2 -1
- {nextmv-0.32.0.dist-info → nextmv-0.33.0.dev0.dist-info}/METADATA +1 -1
- {nextmv-0.32.0.dist-info → nextmv-0.33.0.dev0.dist-info}/RECORD +10 -9
- {nextmv-0.32.0.dist-info → nextmv-0.33.0.dev0.dist-info}/WHEEL +0 -0
- {nextmv-0.32.0.dist-info → nextmv-0.33.0.dev0.dist-info}/licenses/LICENSE +0 -0
nextmv/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "v0.
|
|
1
|
+
__version__ = "v0.33.0.dev0"
|
nextmv/cloud/__init__.py
CHANGED
|
@@ -47,6 +47,7 @@ from .acceptance_test import MetricParams as MetricParams
|
|
|
47
47
|
from .acceptance_test import MetricResult as MetricResult
|
|
48
48
|
from .acceptance_test import MetricStatistics as MetricStatistics
|
|
49
49
|
from .acceptance_test import MetricTolerance as MetricTolerance
|
|
50
|
+
from .acceptance_test import MetricToleranceType as MetricToleranceType
|
|
50
51
|
from .acceptance_test import MetricType as MetricType
|
|
51
52
|
from .acceptance_test import ResultStatistics as ResultStatistics
|
|
52
53
|
from .acceptance_test import StatisticType as StatisticType
|
|
@@ -63,6 +64,12 @@ from .batch_experiment import BatchExperimentRun as BatchExperimentRun
|
|
|
63
64
|
from .batch_experiment import ExperimentStatus as ExperimentStatus
|
|
64
65
|
from .client import Client as Client
|
|
65
66
|
from .client import get_size as get_size
|
|
67
|
+
from .ensemble import EnsembleDefinition as EnsembleDefinition
|
|
68
|
+
from .ensemble import EvaluationRule as EvaluationRule
|
|
69
|
+
from .ensemble import RuleObjective as RuleObjective
|
|
70
|
+
from .ensemble import RuleTolerance as RuleTolerance
|
|
71
|
+
from .ensemble import RuleToleranceType as RuleToleranceType
|
|
72
|
+
from .ensemble import RunGroup as RunGroup
|
|
66
73
|
from .input_set import InputSet as InputSet
|
|
67
74
|
from .input_set import ManagedInput as ManagedInput
|
|
68
75
|
from .instance import Instance as Instance
|
nextmv/cloud/acceptance_test.py
CHANGED
|
@@ -47,6 +47,7 @@ from typing import Optional
|
|
|
47
47
|
|
|
48
48
|
from nextmv.base_model import BaseModel
|
|
49
49
|
from nextmv.cloud.batch_experiment import ExperimentStatus
|
|
50
|
+
from nextmv.deprecated import deprecated
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
class MetricType(str, Enum):
|
|
@@ -214,6 +215,9 @@ class Comparison(str, Enum):
|
|
|
214
215
|
|
|
215
216
|
class ToleranceType(str, Enum):
|
|
216
217
|
"""
|
|
218
|
+
!!! warning
|
|
219
|
+
`ToleranceType` is deprecated, use `MetricToleranceType` instead.
|
|
220
|
+
|
|
217
221
|
Type of tolerance used for a metric.
|
|
218
222
|
|
|
219
223
|
You can import the `ToleranceType` class directly from `cloud`:
|
|
@@ -242,6 +246,57 @@ class ToleranceType(str, Enum):
|
|
|
242
246
|
<ToleranceType.absolute: 'absolute'>
|
|
243
247
|
"""
|
|
244
248
|
|
|
249
|
+
undefined = ""
|
|
250
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
251
|
+
Undefined tolerance type."""
|
|
252
|
+
absolute = "absolute"
|
|
253
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
254
|
+
Absolute tolerance type."""
|
|
255
|
+
relative = "relative"
|
|
256
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
257
|
+
Relative tolerance type."""
|
|
258
|
+
|
|
259
|
+
def __new__(cls, value: str):
|
|
260
|
+
"""Create a new ToleranceType instance and emit deprecation warning."""
|
|
261
|
+
deprecated(
|
|
262
|
+
"ToleranceType",
|
|
263
|
+
"ToleranceType is deprecated and will be removed in a future version. "
|
|
264
|
+
"Please use MetricToleranceType instead",
|
|
265
|
+
)
|
|
266
|
+
obj = str.__new__(cls, value)
|
|
267
|
+
obj._value_ = value
|
|
268
|
+
return obj
|
|
269
|
+
|
|
270
|
+
class MetricToleranceType(str, Enum):
|
|
271
|
+
"""
|
|
272
|
+
Type of tolerance used for a metric.
|
|
273
|
+
|
|
274
|
+
You can import the `MetricToleranceType` class directly from `cloud`:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from nextmv.cloud import MetricToleranceType
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This enumeration defines the different types of tolerances that can be used
|
|
281
|
+
when comparing metrics in acceptance tests.
|
|
282
|
+
|
|
283
|
+
Attributes
|
|
284
|
+
----------
|
|
285
|
+
undefined : str
|
|
286
|
+
Undefined tolerance type (empty string).
|
|
287
|
+
absolute : str
|
|
288
|
+
Absolute tolerance type, using a fixed value.
|
|
289
|
+
relative : str
|
|
290
|
+
Relative tolerance type, using a percentage.
|
|
291
|
+
|
|
292
|
+
Examples
|
|
293
|
+
--------
|
|
294
|
+
>>> from nextmv.cloud import MetricToleranceType
|
|
295
|
+
>>> tol_type = MetricToleranceType.absolute
|
|
296
|
+
>>> tol_type
|
|
297
|
+
<MetricToleranceType.absolute: 'absolute'>
|
|
298
|
+
"""
|
|
299
|
+
|
|
245
300
|
undefined = ""
|
|
246
301
|
"""Undefined tolerance type."""
|
|
247
302
|
absolute = "absolute"
|
|
@@ -249,7 +304,6 @@ class ToleranceType(str, Enum):
|
|
|
249
304
|
relative = "relative"
|
|
250
305
|
"""Relative tolerance type."""
|
|
251
306
|
|
|
252
|
-
|
|
253
307
|
class MetricTolerance(BaseModel):
|
|
254
308
|
"""
|
|
255
309
|
Tolerance used for a metric in an acceptance test.
|
|
@@ -265,22 +319,22 @@ class MetricTolerance(BaseModel):
|
|
|
265
319
|
|
|
266
320
|
Attributes
|
|
267
321
|
----------
|
|
268
|
-
type :
|
|
322
|
+
type : MetricToleranceType
|
|
269
323
|
Type of tolerance (absolute or relative).
|
|
270
324
|
value : float
|
|
271
325
|
Value of the tolerance.
|
|
272
326
|
|
|
273
327
|
Examples
|
|
274
328
|
--------
|
|
275
|
-
>>> from nextmv.cloud import MetricTolerance,
|
|
276
|
-
>>> tolerance = MetricTolerance(type=
|
|
329
|
+
>>> from nextmv.cloud import MetricTolerance, MetricToleranceType
|
|
330
|
+
>>> tolerance = MetricTolerance(type=MetricToleranceType.absolute, value=0.1)
|
|
277
331
|
>>> tolerance.type
|
|
278
|
-
<
|
|
332
|
+
<MetricToleranceType.absolute: 'absolute'>
|
|
279
333
|
>>> tolerance.value
|
|
280
334
|
0.1
|
|
281
335
|
"""
|
|
282
336
|
|
|
283
|
-
type:
|
|
337
|
+
type: MetricToleranceType
|
|
284
338
|
"""Type of tolerance."""
|
|
285
339
|
value: float
|
|
286
340
|
"""Value of the tolerance."""
|
nextmv/cloud/application.py
CHANGED
|
@@ -46,6 +46,11 @@ from nextmv.cloud.batch_experiment import (
|
|
|
46
46
|
to_runs,
|
|
47
47
|
)
|
|
48
48
|
from nextmv.cloud.client import Client, get_size
|
|
49
|
+
from nextmv.cloud.ensemble import (
|
|
50
|
+
EnsembleDefinition,
|
|
51
|
+
EvaluationRule,
|
|
52
|
+
RunGroup,
|
|
53
|
+
)
|
|
49
54
|
from nextmv.cloud.input_set import InputSet, ManagedInput
|
|
50
55
|
from nextmv.cloud.instance import Instance, InstanceConfiguration
|
|
51
56
|
from nextmv.cloud.scenario import Scenario, ScenarioInputType, _option_sets, _scenarios_by_id
|
|
@@ -129,6 +134,8 @@ class Application:
|
|
|
129
134
|
"""Base endpoint for the application."""
|
|
130
135
|
experiments_endpoint: str = "{base}/experiments"
|
|
131
136
|
"""Base endpoint for the experiments in the application."""
|
|
137
|
+
ensembles_endpoint: str = "{base}/ensembles"
|
|
138
|
+
"""Base endpoint for managing the ensemble definitions in the application"""
|
|
132
139
|
|
|
133
140
|
def __post_init__(self):
|
|
134
141
|
"""Initialize the endpoint and experiments_endpoint attributes.
|
|
@@ -138,6 +145,7 @@ class Application:
|
|
|
138
145
|
"""
|
|
139
146
|
self.endpoint = self.endpoint.format(id=self.id)
|
|
140
147
|
self.experiments_endpoint = self.experiments_endpoint.format(base=self.endpoint)
|
|
148
|
+
self.ensembles_endpoint = self.ensembles_endpoint.format(base=self.endpoint)
|
|
141
149
|
|
|
142
150
|
@classmethod
|
|
143
151
|
def new(
|
|
@@ -515,6 +523,30 @@ class Application:
|
|
|
515
523
|
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}",
|
|
516
524
|
)
|
|
517
525
|
|
|
526
|
+
def delete_ensemble_definition(self, ensemble_definition_id: str) -> None:
|
|
527
|
+
"""
|
|
528
|
+
Delete an ensemble definition.
|
|
529
|
+
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
ensemble_definition_id : str
|
|
533
|
+
ID of the ensemble definition to delete.
|
|
534
|
+
|
|
535
|
+
Raises
|
|
536
|
+
------
|
|
537
|
+
requests.HTTPError
|
|
538
|
+
If the response status code is not 2xx.
|
|
539
|
+
|
|
540
|
+
Examples
|
|
541
|
+
--------
|
|
542
|
+
>>> app.delete_ensemble_definition("development-ensemble-definition")
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
_ = self.client.request(
|
|
546
|
+
method="DELETE",
|
|
547
|
+
endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
|
|
548
|
+
)
|
|
549
|
+
|
|
518
550
|
def delete_scenario_test(self, scenario_test_id: str) -> None:
|
|
519
551
|
"""
|
|
520
552
|
Delete a scenario test.
|
|
@@ -563,6 +595,39 @@ class Application:
|
|
|
563
595
|
endpoint=f"{self.endpoint}/secrets/{secrets_collection_id}",
|
|
564
596
|
)
|
|
565
597
|
|
|
598
|
+
def ensemble_definition(self, ensemble_definition_id: str) -> EnsembleDefinition:
|
|
599
|
+
"""
|
|
600
|
+
Get an ensemble definition.
|
|
601
|
+
|
|
602
|
+
Parameters
|
|
603
|
+
----------
|
|
604
|
+
ensemble_definition_id : str
|
|
605
|
+
ID of the ensemble definition to retrieve.
|
|
606
|
+
|
|
607
|
+
Returns
|
|
608
|
+
-------
|
|
609
|
+
EnsembleDefintion
|
|
610
|
+
The requested ensemble definition details.
|
|
611
|
+
|
|
612
|
+
Raises
|
|
613
|
+
------
|
|
614
|
+
requests.HTTPError
|
|
615
|
+
If the response status code is not 2xx.
|
|
616
|
+
|
|
617
|
+
Examples
|
|
618
|
+
--------
|
|
619
|
+
>>> ensemble_definition = app.ensemble_definition("instance-123")
|
|
620
|
+
>>> print(ensemble_definition.name)
|
|
621
|
+
'Production Ensemble Definition'
|
|
622
|
+
"""
|
|
623
|
+
|
|
624
|
+
response = self.client.request(
|
|
625
|
+
method="GET",
|
|
626
|
+
endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
630
|
+
|
|
566
631
|
@staticmethod
|
|
567
632
|
def exists(client: Client, id: str) -> bool:
|
|
568
633
|
"""
|
|
@@ -748,6 +813,36 @@ class Application:
|
|
|
748
813
|
|
|
749
814
|
return [BatchExperimentMetadata.from_dict(batch_experiment) for batch_experiment in response.json()]
|
|
750
815
|
|
|
816
|
+
def list_ensemble_definitions(self) -> list[EnsembleDefinition]:
|
|
817
|
+
"""
|
|
818
|
+
List all ensemble_definitions.
|
|
819
|
+
|
|
820
|
+
Returns
|
|
821
|
+
-------
|
|
822
|
+
list[EnsembleDefinition]
|
|
823
|
+
List of all ensemble definitions associated with this application.
|
|
824
|
+
|
|
825
|
+
Raises
|
|
826
|
+
------
|
|
827
|
+
requests.HTTPError
|
|
828
|
+
If the response status code is not 2xx.
|
|
829
|
+
|
|
830
|
+
Examples
|
|
831
|
+
--------
|
|
832
|
+
>>> ensemble_definitions = app.list_ensemble_definitions()
|
|
833
|
+
>>> for ensemble_definition in ensemble_definitions:
|
|
834
|
+
... print(ensemble_definition.name)
|
|
835
|
+
'Development Ensemble Definition'
|
|
836
|
+
'Production Ensemble Definition'
|
|
837
|
+
"""
|
|
838
|
+
|
|
839
|
+
response = self.client.request(
|
|
840
|
+
method="GET",
|
|
841
|
+
endpoint=f"{self.ensembles_endpoint}",
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
return [EnsembleDefinition.from_dict(ensemble_definition) for ensemble_definition in response.json()["items"]]
|
|
845
|
+
|
|
751
846
|
def list_input_sets(self) -> list[InputSet]:
|
|
752
847
|
"""
|
|
753
848
|
List all input sets.
|
|
@@ -1314,6 +1409,53 @@ class Application:
|
|
|
1314
1409
|
|
|
1315
1410
|
return self.batch_experiment_with_polling(batch_id=batch_id, polling_options=polling_options)
|
|
1316
1411
|
|
|
1412
|
+
def new_ensemble_defintion(
|
|
1413
|
+
self,
|
|
1414
|
+
id: str,
|
|
1415
|
+
run_groups: list[RunGroup],
|
|
1416
|
+
rules: list[EvaluationRule],
|
|
1417
|
+
name: Optional[str] = None,
|
|
1418
|
+
description: Optional[str] = None,
|
|
1419
|
+
) -> EnsembleDefinition:
|
|
1420
|
+
"""
|
|
1421
|
+
Create a new ensemble definition.
|
|
1422
|
+
|
|
1423
|
+
Parameters
|
|
1424
|
+
----------
|
|
1425
|
+
id: str
|
|
1426
|
+
ID of the ensemble defintion.
|
|
1427
|
+
run_groups: list[RunGroup]
|
|
1428
|
+
Information to facilitate the execution of child runs.
|
|
1429
|
+
rules: list[EvaluationRule]
|
|
1430
|
+
Information to facilitate the selection of
|
|
1431
|
+
a result for the ensemble run from child runs.
|
|
1432
|
+
name: Optional[str]
|
|
1433
|
+
Name of the ensemble definition.
|
|
1434
|
+
description: Optional[str]
|
|
1435
|
+
Description of the ensemble definition.
|
|
1436
|
+
"""
|
|
1437
|
+
|
|
1438
|
+
if name is None:
|
|
1439
|
+
name = id
|
|
1440
|
+
if description is None:
|
|
1441
|
+
description = name
|
|
1442
|
+
|
|
1443
|
+
payload = {
|
|
1444
|
+
"id": id,
|
|
1445
|
+
"run_groups": [run_group.to_dict() for run_group in run_groups],
|
|
1446
|
+
"rules": [rule.to_dict() for rule in rules],
|
|
1447
|
+
"name": name,
|
|
1448
|
+
"description": description,
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
response = self.client.request(
|
|
1452
|
+
method="POST",
|
|
1453
|
+
endpoint=f"{self.ensembles_endpoint}",
|
|
1454
|
+
payload=payload,
|
|
1455
|
+
)
|
|
1456
|
+
|
|
1457
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
1458
|
+
|
|
1317
1459
|
def new_input_set(
|
|
1318
1460
|
self,
|
|
1319
1461
|
id: str,
|
|
@@ -2236,13 +2378,14 @@ class Application:
|
|
|
2236
2378
|
|
|
2237
2379
|
if id is None:
|
|
2238
2380
|
id = safe_id(prefix="version")
|
|
2381
|
+
if name is None:
|
|
2382
|
+
name = id
|
|
2239
2383
|
|
|
2240
2384
|
payload = {
|
|
2241
2385
|
"id": id,
|
|
2386
|
+
"name": name,
|
|
2242
2387
|
}
|
|
2243
2388
|
|
|
2244
|
-
if name is not None:
|
|
2245
|
-
payload["name"] = name
|
|
2246
2389
|
if description is not None:
|
|
2247
2390
|
payload["description"] = description
|
|
2248
2391
|
|
|
@@ -2934,6 +3077,98 @@ class Application:
|
|
|
2934
3077
|
output_dir_path=output_dir_path,
|
|
2935
3078
|
)
|
|
2936
3079
|
|
|
3080
|
+
def update_batch_experiment(
|
|
3081
|
+
self,
|
|
3082
|
+
batch_experiment_id: str,
|
|
3083
|
+
name: Optional[str] = None,
|
|
3084
|
+
description: Optional[str] = None,
|
|
3085
|
+
) -> BatchExperimentInformation:
|
|
3086
|
+
"""
|
|
3087
|
+
Update a batch experiment.
|
|
3088
|
+
|
|
3089
|
+
Parameters
|
|
3090
|
+
----------
|
|
3091
|
+
batch_experiment_id : str
|
|
3092
|
+
ID of the batch experiment to update.
|
|
3093
|
+
name : Optional[str], default=None
|
|
3094
|
+
Optional name of the batch experiment.
|
|
3095
|
+
description : Optional[str], default=None
|
|
3096
|
+
Optional description of the batch experiment.
|
|
3097
|
+
|
|
3098
|
+
Returns
|
|
3099
|
+
-------
|
|
3100
|
+
BatchExperimentInformation
|
|
3101
|
+
The information with the updated batch experiment.
|
|
3102
|
+
|
|
3103
|
+
Raises
|
|
3104
|
+
------
|
|
3105
|
+
requests.HTTPError
|
|
3106
|
+
If the response status code is not 2xx.
|
|
3107
|
+
"""
|
|
3108
|
+
|
|
3109
|
+
payload = {}
|
|
3110
|
+
|
|
3111
|
+
if name is not None:
|
|
3112
|
+
payload["name"] = name
|
|
3113
|
+
if description is not None:
|
|
3114
|
+
payload["description"] = description
|
|
3115
|
+
|
|
3116
|
+
response = self.client.request(
|
|
3117
|
+
method="PATCH",
|
|
3118
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
|
|
3119
|
+
payload=payload,
|
|
3120
|
+
)
|
|
3121
|
+
|
|
3122
|
+
return BatchExperimentInformation.from_dict(response.json())
|
|
3123
|
+
|
|
3124
|
+
def update_ensemble_definition(
|
|
3125
|
+
self,
|
|
3126
|
+
id: str,
|
|
3127
|
+
name: Optional[str] = None,
|
|
3128
|
+
description: Optional[str] = None,
|
|
3129
|
+
) -> EnsembleDefinition:
|
|
3130
|
+
"""
|
|
3131
|
+
Update an ensemble definition.
|
|
3132
|
+
|
|
3133
|
+
Parameters
|
|
3134
|
+
----------
|
|
3135
|
+
id : str
|
|
3136
|
+
ID of the ensemble definition to update.
|
|
3137
|
+
name : Optional[str], default=None
|
|
3138
|
+
Optional name of the ensemble definition.
|
|
3139
|
+
description : Optional[str], default=None
|
|
3140
|
+
Optional description of the ensemble definition.
|
|
3141
|
+
|
|
3142
|
+
Returns
|
|
3143
|
+
-------
|
|
3144
|
+
EnsembleDefinition
|
|
3145
|
+
The updated ensemble definition.
|
|
3146
|
+
|
|
3147
|
+
Raises
|
|
3148
|
+
------
|
|
3149
|
+
ValueError
|
|
3150
|
+
If neither name nor description is updated
|
|
3151
|
+
requests.HTTPError
|
|
3152
|
+
If the response status code is not 2xx.
|
|
3153
|
+
"""
|
|
3154
|
+
|
|
3155
|
+
payload = {}
|
|
3156
|
+
|
|
3157
|
+
if name is None and description is None:
|
|
3158
|
+
raise ValueError("Must define at least one value among name and description to modify")
|
|
3159
|
+
if name is not None:
|
|
3160
|
+
payload["name"] = name
|
|
3161
|
+
if description is not None:
|
|
3162
|
+
payload["description"] = description
|
|
3163
|
+
|
|
3164
|
+
response = self.client.request(
|
|
3165
|
+
method="PATCH",
|
|
3166
|
+
endpoint=f"{self.ensembles_endpoint}/{id}",
|
|
3167
|
+
payload=payload,
|
|
3168
|
+
)
|
|
3169
|
+
|
|
3170
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
3171
|
+
|
|
2937
3172
|
def update_instance(
|
|
2938
3173
|
self,
|
|
2939
3174
|
id: str,
|
|
@@ -2997,50 +3232,6 @@ class Application:
|
|
|
2997
3232
|
|
|
2998
3233
|
return Instance.from_dict(response.json())
|
|
2999
3234
|
|
|
3000
|
-
def update_batch_experiment(
|
|
3001
|
-
self,
|
|
3002
|
-
batch_experiment_id: str,
|
|
3003
|
-
name: Optional[str] = None,
|
|
3004
|
-
description: Optional[str] = None,
|
|
3005
|
-
) -> BatchExperimentInformation:
|
|
3006
|
-
"""
|
|
3007
|
-
Update a batch experiment.
|
|
3008
|
-
|
|
3009
|
-
Parameters
|
|
3010
|
-
----------
|
|
3011
|
-
batch_experiment_id : str
|
|
3012
|
-
ID of the batch experiment to update.
|
|
3013
|
-
name : Optional[str], default=None
|
|
3014
|
-
Optional name of the batch experiment.
|
|
3015
|
-
description : Optional[str], default=None
|
|
3016
|
-
Optional description of the batch experiment.
|
|
3017
|
-
|
|
3018
|
-
Returns
|
|
3019
|
-
-------
|
|
3020
|
-
BatchExperimentInformation
|
|
3021
|
-
The information with the updated batch experiment.
|
|
3022
|
-
|
|
3023
|
-
Raises
|
|
3024
|
-
------
|
|
3025
|
-
requests.HTTPError
|
|
3026
|
-
If the response status code is not 2xx.
|
|
3027
|
-
"""
|
|
3028
|
-
|
|
3029
|
-
payload = {}
|
|
3030
|
-
|
|
3031
|
-
if name is not None:
|
|
3032
|
-
payload["name"] = name
|
|
3033
|
-
if description is not None:
|
|
3034
|
-
payload["description"] = description
|
|
3035
|
-
|
|
3036
|
-
response = self.client.request(
|
|
3037
|
-
method="PATCH",
|
|
3038
|
-
endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
|
|
3039
|
-
payload=payload,
|
|
3040
|
-
)
|
|
3041
|
-
|
|
3042
|
-
return BatchExperimentInformation.from_dict(response.json())
|
|
3043
|
-
|
|
3044
3235
|
def update_managed_input(
|
|
3045
3236
|
self,
|
|
3046
3237
|
managed_input_id: str,
|
nextmv/cloud/ensemble.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classes for working with Nextmv Cloud Ensemble Runs.
|
|
3
|
+
|
|
4
|
+
This module provides classes for interacting with ensemble runs in Nextmv Cloud.
|
|
5
|
+
It details the core data structures for ensemble definitions.
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
RunGroup
|
|
10
|
+
A structure to group execution of child runs for an ensemble run.
|
|
11
|
+
RuleObjective
|
|
12
|
+
An enum that specifies the supported evaluation rule objectives.
|
|
13
|
+
ToleranceType
|
|
14
|
+
An enum that specifies the supported tolerance types for evaluation rules.
|
|
15
|
+
RuleTolerance
|
|
16
|
+
A structure for defining tolerance thresholds for an evaluation rule
|
|
17
|
+
EvaluationRule
|
|
18
|
+
A structure to evaluate run results for an ensemble run.
|
|
19
|
+
EnsembleDefinition
|
|
20
|
+
Representation of a Nextmv Cloud Ensemble Definition for an application.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from enum import Enum
|
|
25
|
+
from typing import Optional
|
|
26
|
+
|
|
27
|
+
from nextmv.base_model import BaseModel
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RunGroup(BaseModel):
|
|
31
|
+
"""A structure to group child runs for an ensemble run.
|
|
32
|
+
|
|
33
|
+
You can import the `RunGroup` class directly from `cloud`:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from nextmv.cloud import RunGroup
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This class represents a grouping of child runs that share a configuration
|
|
40
|
+
for ensemble run executions.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
id : str
|
|
45
|
+
The unique identifier of the run group.
|
|
46
|
+
instance_id : str
|
|
47
|
+
ID of the app instance that this run group executes on.
|
|
48
|
+
options : dict, optional
|
|
49
|
+
Runtime options/parameters for the application.
|
|
50
|
+
repetitions : int, optional
|
|
51
|
+
The number of times the run is to be repeated on the instance and with
|
|
52
|
+
the options defined in the run group
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
id: str
|
|
56
|
+
"""The unique identifier of the run group."""
|
|
57
|
+
instance_id: str
|
|
58
|
+
"""ID of the app instance that this run group executes on."""
|
|
59
|
+
options: Optional[dict] = None
|
|
60
|
+
"""Runtime options/parameters for the application."""
|
|
61
|
+
repetitions: Optional[int] = None
|
|
62
|
+
"""The number of times the run is to be repeated on the instance and with
|
|
63
|
+
the options defined in the run group"""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RuleObjective(str, Enum):
|
|
67
|
+
"""The value of this data determines how a value of a run is optimized to
|
|
68
|
+
determined which ensemble child run is the "best" for a given metric and
|
|
69
|
+
rule, as well as which other ones are within tolerance of that run for the
|
|
70
|
+
purposes of selecting a result for the ensemble run from among the child runs.
|
|
71
|
+
|
|
72
|
+
You can import the `RuleObjective` class directly from `cloud`:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from nextmv.cloud import RuleObjective
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This enum specifies the supported evaluation rule objectives.
|
|
79
|
+
|
|
80
|
+
Attributes
|
|
81
|
+
----------
|
|
82
|
+
MAXIMIZE : str
|
|
83
|
+
Maximize the value of the evaluated metric.
|
|
84
|
+
MINIMIZE : str
|
|
85
|
+
Minimize the value of the evaluated metric.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
MAXIMIZE = "maximize"
|
|
89
|
+
"""Maximize the value of the evaluated metric."""
|
|
90
|
+
MINIMIZE = "minimize"
|
|
91
|
+
"""Minimize the value of the evaluated metric."""
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class RuleToleranceType(str, Enum):
|
|
95
|
+
"""The type of comparison used to determine if a run metric is within
|
|
96
|
+
tolerance of a the "best" run for that rule and metric
|
|
97
|
+
|
|
98
|
+
You can import the `RuleToleranceType` class directly from `cloud`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from nextmv.cloud import RuleToleranceType
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This enum specifies the supported tolerance types.
|
|
105
|
+
|
|
106
|
+
Attributes
|
|
107
|
+
----------
|
|
108
|
+
ABSOLUTE : str
|
|
109
|
+
Uses the absolute difference between the value of the "best" run and
|
|
110
|
+
the run being evaluated for tolerance
|
|
111
|
+
RELATIVE : str
|
|
112
|
+
Uses the the percentage of the "best" run by which the run being
|
|
113
|
+
evaluted for tolerance differs. A value of `1` is 100%.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
ABSOLUTE = "absolute"
|
|
117
|
+
"""Uses the absolute difference between the value of the "best" run and
|
|
118
|
+
the run being evaluated for tolerance"""
|
|
119
|
+
RELATIVE = "relative"
|
|
120
|
+
"""Uses the the percentage of the "best" run by which the run being
|
|
121
|
+
evaluted for tolerance differs. A value of `1` is 100%."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class RuleTolerance(BaseModel):
|
|
125
|
+
"""A structure used to determine if a run is within tolerance of of the best
|
|
126
|
+
run (as determined by the objective of the `EvaluationRule` it is defined on).
|
|
127
|
+
|
|
128
|
+
You can import the `RuleTolerance` class directly from `cloud`:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from nextmv.cloud import RuleTolerance
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This class represents the tolerance on a particular evaluation rule by
|
|
135
|
+
which a child run may be selected as the result of an ensemble run.
|
|
136
|
+
|
|
137
|
+
value : float
|
|
138
|
+
The value within which runs can deviate from the "best" run
|
|
139
|
+
for that metric to be considered within tolerance of it.
|
|
140
|
+
type : ToleranceType
|
|
141
|
+
The method by which runs are determined to be within tolerance.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
value: float
|
|
145
|
+
"""The value within which runs can deviate from the "best" run
|
|
146
|
+
for that metric to be considered within tolerance of it."""
|
|
147
|
+
type: RuleToleranceType
|
|
148
|
+
"""The method by which runs are determined to be within tolerance."""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class EvaluationRule(BaseModel):
|
|
152
|
+
"""A structure to evaluate run results for an ensemble run.
|
|
153
|
+
|
|
154
|
+
You can import the `EvaluationRule` class directly from `cloud`:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from nextmv.cloud import EvaluationRule
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
This class represents a rule by which the child runs for an ensemble run
|
|
161
|
+
will be evaluated for the purpose of selecting an optimal result for the
|
|
162
|
+
ensemble run.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
id : str
|
|
167
|
+
The unique identifier of the evaluation rule.
|
|
168
|
+
statistics_path : str
|
|
169
|
+
The path within the statistics of a run output (conforming to Nextmv
|
|
170
|
+
statistics convention and flattened to a string starting with `$` and
|
|
171
|
+
delimited by `.` e.g. `$.result.value`.)
|
|
172
|
+
objective : RuleObjective
|
|
173
|
+
The objective by which runs are optimized for this rule
|
|
174
|
+
tolerance : RuleTolerance
|
|
175
|
+
The tolerance by which runs can be accepted as a potential result
|
|
176
|
+
for an evaluation rule
|
|
177
|
+
index : int, optional
|
|
178
|
+
The index (non-negative integer) of the evalutation rule. Lower indicies
|
|
179
|
+
are evaluated first.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
id: str
|
|
183
|
+
"""The unique identifier of the evaluation rule."""
|
|
184
|
+
statistics_path: str
|
|
185
|
+
"""The path within the statistics of a run output (conforming to Nextmv
|
|
186
|
+
statistics convention and flattened to a string starting with `$` and
|
|
187
|
+
delimited by `.` e.g. `$.result.value`.)"""
|
|
188
|
+
objective: RuleObjective
|
|
189
|
+
"""The objective by which runs are optimized for this rule"""
|
|
190
|
+
tolerance: RuleTolerance
|
|
191
|
+
"""The tolerance by which runs can be accepted as a potential result
|
|
192
|
+
for an evaluation rule"""
|
|
193
|
+
index: int
|
|
194
|
+
"""The index (non-negative integer) of the evalutation rule. Lower indicies
|
|
195
|
+
are evaluated first."""
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class EnsembleDefinition(BaseModel):
|
|
199
|
+
"""An ensemble definition for an application.
|
|
200
|
+
|
|
201
|
+
You can import the `EnsembleDefinition` class directly from `cloud`:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from nextmv.cloud import EnsembleDefinition
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
A Nextmv Cloud ensemble definition represents a structure by which an
|
|
208
|
+
application can coordinate and execute, and determine the optimal result of
|
|
209
|
+
an ensemble run.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
id : str
|
|
214
|
+
The unique identifier of the ensemble definition.
|
|
215
|
+
application_id : str
|
|
216
|
+
ID of the application that this ensemble definition belongs to.
|
|
217
|
+
name : str
|
|
218
|
+
Human-readable name of the ensemble definition.
|
|
219
|
+
description : str
|
|
220
|
+
Detailed description of the ensemble definition.
|
|
221
|
+
run_groups : list[RunGroup], optional
|
|
222
|
+
The run groups that structure the execution of an ensemble run
|
|
223
|
+
rules : list[EvaluationRule], optional
|
|
224
|
+
The rules by which ensemble child runs are evaluated
|
|
225
|
+
to find an optimal result.
|
|
226
|
+
created_at : datetime
|
|
227
|
+
Timestamp when the ensemble definition was created.
|
|
228
|
+
updated_at : datetime
|
|
229
|
+
Timestamp when the ensemble definition was last updated.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
id: str
|
|
233
|
+
"""The unique identifier of the ensemble definition."""
|
|
234
|
+
application_id: str
|
|
235
|
+
"""ID of the application that this ensemble definition belongs to."""
|
|
236
|
+
name: str = ""
|
|
237
|
+
"""Human-readable name of the ensemble definition."""
|
|
238
|
+
description: str = ""
|
|
239
|
+
"""Detailed description of the ensemble definition."""
|
|
240
|
+
run_groups: list[RunGroup]
|
|
241
|
+
"""The run groups that structure the execution of an ensemble run"""
|
|
242
|
+
rules: list[EvaluationRule]
|
|
243
|
+
"""The rules by which ensemble child runs are evaluated
|
|
244
|
+
to find an optimal result."""
|
|
245
|
+
created_at: datetime
|
|
246
|
+
"""Timestamp when the ensemble definition was created."""
|
|
247
|
+
updated_at: datetime
|
|
248
|
+
"""Timestamp when the ensemble definition was last updated."""
|
nextmv/local/executor.py
CHANGED
|
@@ -32,6 +32,7 @@ import json
|
|
|
32
32
|
import os
|
|
33
33
|
import shutil
|
|
34
34
|
import subprocess
|
|
35
|
+
import sys
|
|
35
36
|
import tempfile
|
|
36
37
|
from datetime import datetime, timezone
|
|
37
38
|
from typing import Any, Optional, Union
|
|
@@ -143,7 +144,7 @@ def execute_run(
|
|
|
143
144
|
# supporting a Python-first experience, so we are not summoning
|
|
144
145
|
# applications that are not Python-based.
|
|
145
146
|
entrypoint = os.path.join(temp_src, manifest.entrypoint)
|
|
146
|
-
args = [
|
|
147
|
+
args = [sys.executable, entrypoint] + options_args(options)
|
|
147
148
|
|
|
148
149
|
result = subprocess.run(
|
|
149
150
|
args,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
nextmv/__about__.py,sha256=
|
|
1
|
+
nextmv/__about__.py,sha256=swx5y44doM60f9hpn91hbwEUw-qTWVfvgrZ4zO7sCP0,29
|
|
2
2
|
nextmv/__entrypoint__.py,sha256=dA0iwwHtrq6Z9w9FxmxKLoBGLyhe7jWtUAU-Y3PEgHg,1094
|
|
3
3
|
nextmv/__init__.py,sha256=pW2HMcJnNSFqF_tj77TtqpddEeCosTKS7NxGNPDp7mU,3620
|
|
4
4
|
nextmv/_serialization.py,sha256=JlSl6BL0M2Esf7F89GsGIZ__Pp8RnFRNM0UxYhuuYU4,2853
|
|
@@ -14,12 +14,13 @@ nextmv/polling.py,sha256=nfefvWI1smm-lIzaXE-4DMlojp6KXIvVi88XLJYUmo8,9724
|
|
|
14
14
|
nextmv/run.py,sha256=lAWWkLml1C7wkXSvN8orBjudoz72Qv5ZCHCWY9e7xaY,45813
|
|
15
15
|
nextmv/safe.py,sha256=VAK4fGEurbLNji4Pg5Okga5XQSbI4aI9JJf95_68Z20,3867
|
|
16
16
|
nextmv/status.py,sha256=SCDLhh2om3yeO5FxO0x-_RShQsZNXEpjHNdCGdb3VUI,2787
|
|
17
|
-
nextmv/cloud/__init__.py,sha256=
|
|
18
|
-
nextmv/cloud/acceptance_test.py,sha256=
|
|
17
|
+
nextmv/cloud/__init__.py,sha256=2wI72lhWq81BYv1OpS0OOTT5-3sivpX0H4z5ANPoLMc,5051
|
|
18
|
+
nextmv/cloud/acceptance_test.py,sha256=ZvOaVzhy3sHb5Vi9uBGhZJPggLN0oEmEvLKyc830zQ8,27439
|
|
19
19
|
nextmv/cloud/account.py,sha256=jIdGNyI3l3dVh2PuriAwAOrEuWRM150WgzxcBMVBNRw,6058
|
|
20
|
-
nextmv/cloud/application.py,sha256=
|
|
20
|
+
nextmv/cloud/application.py,sha256=wieXFWijmCsgEDpmefLWs010BkHMbAdLLV7Cwmncu5I,140555
|
|
21
21
|
nextmv/cloud/batch_experiment.py,sha256=13ciRpgBabMMTyazfdfEAymD3rTPrTAAorECsANxxuA,10397
|
|
22
22
|
nextmv/cloud/client.py,sha256=E0DiUb377jvEnpXlRnfT1PGCI0Jm0lTUoX5VqeU91lk,18165
|
|
23
|
+
nextmv/cloud/ensemble.py,sha256=glrRgyRFcEH12fNUhEl1FOo6xOTDEaF478dxfX0wj2Y,8604
|
|
23
24
|
nextmv/cloud/input_set.py,sha256=NkzA6_hwgD-YwoirzwvZrObIoBTfurry7Os3jo4DyXc,4236
|
|
24
25
|
nextmv/cloud/instance.py,sha256=SS4tbp0LQMWDaeYpwcNxJei82oi_Hozv1t5i3QGjASY,4024
|
|
25
26
|
nextmv/cloud/package.py,sha256=cG75DptN4sxXaT8ruDh2EY2duaK5Jwg16X2LV0BP8ts,13021
|
|
@@ -38,12 +39,12 @@ nextmv/default_app/src/main.py,sha256=WWeN_xl_mcPhICl3rSCvdEjRkFXGmAnej88FhS-fAm
|
|
|
38
39
|
nextmv/default_app/src/visuals.py,sha256=WYK_YBnLmYo3TpVev1CpoNCuW5R7hk9QIkeCmvMn1Fs,1014
|
|
39
40
|
nextmv/local/__init__.py,sha256=6BsoqlK4dw6X11_uKzz9gBPfxKpdiol2FYO8R3X73SE,116
|
|
40
41
|
nextmv/local/application.py,sha256=qq14ihKxymg5NHqhU56qTu6lVmMYWLGYUJC4MrhWB68,45815
|
|
41
|
-
nextmv/local/executor.py,sha256=
|
|
42
|
+
nextmv/local/executor.py,sha256=ohAUrIRohcH_qGglK1hSFR0W6bPBSuMAcMwVkW5G4vM,24338
|
|
42
43
|
nextmv/local/geojson_handler.py,sha256=7FavJdkUonop-yskjis0x3qFGB8A5wZyoBUblw-bVhw,12540
|
|
43
44
|
nextmv/local/local.py,sha256=wUHuoAXqJIZpTJBh056hmQuiPEjlZvLTR2BC6Ino-WI,2619
|
|
44
45
|
nextmv/local/plotly_handler.py,sha256=bLb50e3AkVr_W-F6S7lXfeRdN60mG2jk3UElNmhoMWU,1930
|
|
45
46
|
nextmv/local/runner.py,sha256=hwkITHrQG_J9TzxufnaP1mjLWG-iSsNQD66UFZY4pp4,8602
|
|
46
|
-
nextmv-0.
|
|
47
|
-
nextmv-0.
|
|
48
|
-
nextmv-0.
|
|
49
|
-
nextmv-0.
|
|
47
|
+
nextmv-0.33.0.dev0.dist-info/METADATA,sha256=GU6x---pvDI4R1SBV9BW1J5fVLLPxllbrccj6kLyApA,16013
|
|
48
|
+
nextmv-0.33.0.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
49
|
+
nextmv-0.33.0.dev0.dist-info/licenses/LICENSE,sha256=ZIbK-sSWA-OZprjNbmJAglYRtl5_K4l9UwAV3PGJAPc,11349
|
|
50
|
+
nextmv-0.33.0.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|