nextmv 0.31.0__py3-none-any.whl → 0.33.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.
- nextmv/__about__.py +1 -1
- nextmv/__init__.py +5 -9
- nextmv/cloud/__init__.py +7 -0
- nextmv/cloud/acceptance_test.py +69 -5
- nextmv/cloud/application.py +294 -98
- nextmv/cloud/batch_experiment.py +7 -1
- nextmv/cloud/ensemble.py +248 -0
- nextmv/cloud/package.py +62 -24
- nextmv/default_app/README.md +17 -2
- nextmv/default_app/app.yaml +1 -2
- nextmv/default_app/input.json +5 -0
- nextmv/default_app/main.py +37 -0
- nextmv/input.py +2 -8
- nextmv/local/application.py +250 -224
- nextmv/local/executor.py +9 -13
- nextmv/local/local.py +97 -0
- nextmv/local/runner.py +3 -41
- nextmv/manifest.py +61 -2
- nextmv/output.py +6 -26
- nextmv/run.py +476 -108
- {nextmv-0.31.0.dist-info → nextmv-0.33.0.dist-info}/METADATA +1 -1
- {nextmv-0.31.0.dist-info → nextmv-0.33.0.dist-info}/RECORD +24 -20
- {nextmv-0.31.0.dist-info → nextmv-0.33.0.dist-info}/WHEEL +0 -0
- {nextmv-0.31.0.dist-info → nextmv-0.33.0.dist-info}/licenses/LICENSE +0 -0
nextmv/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "v0.
|
|
1
|
+
__version__ = "v0.33.0"
|
nextmv/__init__.py
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
from .__about__ import __version__
|
|
4
4
|
from .base_model import BaseModel as BaseModel
|
|
5
5
|
from .base_model import from_dict as from_dict
|
|
6
|
-
from .input import DEFAULT_INPUT_JSON_FILE as DEFAULT_INPUT_JSON_FILE
|
|
7
|
-
from .input import INPUTS_KEY as INPUTS_KEY
|
|
8
6
|
from .input import DataFile as DataFile
|
|
9
7
|
from .input import Input as Input
|
|
10
8
|
from .input import InputFormat as InputFormat
|
|
@@ -23,6 +21,7 @@ from .manifest import Manifest as Manifest
|
|
|
23
21
|
from .manifest import ManifestBuild as ManifestBuild
|
|
24
22
|
from .manifest import ManifestOption as ManifestOption
|
|
25
23
|
from .manifest import ManifestPython as ManifestPython
|
|
24
|
+
from .manifest import ManifestPythonArch as ManifestPythonArch
|
|
26
25
|
from .manifest import ManifestPythonModel as ManifestPythonModel
|
|
27
26
|
from .manifest import ManifestRuntime as ManifestRuntime
|
|
28
27
|
from .manifest import ManifestType as ManifestType
|
|
@@ -31,13 +30,6 @@ from .model import ModelConfiguration as ModelConfiguration
|
|
|
31
30
|
from .options import Option as Option
|
|
32
31
|
from .options import Options as Options
|
|
33
32
|
from .options import Parameter as Parameter
|
|
34
|
-
from .output import ASSETS_KEY as ASSETS_KEY
|
|
35
|
-
from .output import DEFAULT_OUTPUT_JSON_FILE as DEFAULT_OUTPUT_JSON_FILE
|
|
36
|
-
from .output import LOGS_FILE as LOGS_FILE
|
|
37
|
-
from .output import LOGS_KEY as LOGS_KEY
|
|
38
|
-
from .output import OUTPUTS_KEY as OUTPUTS_KEY
|
|
39
|
-
from .output import SOLUTIONS_KEY as SOLUTIONS_KEY
|
|
40
|
-
from .output import STATISTICS_KEY as STATISTICS_KEY
|
|
41
33
|
from .output import Asset as Asset
|
|
42
34
|
from .output import DataPoint as DataPoint
|
|
43
35
|
from .output import LocalOutputWriter as LocalOutputWriter
|
|
@@ -66,13 +58,17 @@ from .run import Format as Format
|
|
|
66
58
|
from .run import FormatInput as FormatInput
|
|
67
59
|
from .run import FormatOutput as FormatOutput
|
|
68
60
|
from .run import Metadata as Metadata
|
|
61
|
+
from .run import OptionsSummaryItem as OptionsSummaryItem
|
|
62
|
+
from .run import Run as Run
|
|
69
63
|
from .run import RunConfiguration as RunConfiguration
|
|
70
64
|
from .run import RunInformation as RunInformation
|
|
65
|
+
from .run import RunInfoStatistics as RunInfoStatistics
|
|
71
66
|
from .run import RunLog as RunLog
|
|
72
67
|
from .run import RunQueuing as RunQueuing
|
|
73
68
|
from .run import RunResult as RunResult
|
|
74
69
|
from .run import RunType as RunType
|
|
75
70
|
from .run import RunTypeConfiguration as RunTypeConfiguration
|
|
71
|
+
from .run import StatisticsIndicator as StatisticsIndicator
|
|
76
72
|
from .run import TrackedRun as TrackedRun
|
|
77
73
|
from .run import TrackedRunStatus as TrackedRunStatus
|
|
78
74
|
from .run import run_duration as run_duration
|
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,66 @@ 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
|
+
|
|
260
|
+
# Override __getattribute__ to emit deprecation warnings when enum values are accessed
|
|
261
|
+
_original_getattribute = ToleranceType.__class__.__getattribute__
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _deprecated_getattribute(cls, name: str):
|
|
265
|
+
# Only emit deprecation warning if this is specifically the ToleranceType class
|
|
266
|
+
if cls is ToleranceType and name in ("undefined", "absolute", "relative"):
|
|
267
|
+
deprecated(
|
|
268
|
+
f"ToleranceType.{name}",
|
|
269
|
+
"ToleranceType is deprecated and will be removed in a future version. "
|
|
270
|
+
"Please use MetricToleranceType instead",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return _original_getattribute(cls, name)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
ToleranceType.__class__.__getattribute__ = _deprecated_getattribute
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class MetricToleranceType(str, Enum):
|
|
280
|
+
"""
|
|
281
|
+
Type of tolerance used for a metric.
|
|
282
|
+
|
|
283
|
+
You can import the `MetricToleranceType` class directly from `cloud`:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from nextmv.cloud import MetricToleranceType
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
This enumeration defines the different types of tolerances that can be used
|
|
290
|
+
when comparing metrics in acceptance tests.
|
|
291
|
+
|
|
292
|
+
Attributes
|
|
293
|
+
----------
|
|
294
|
+
undefined : str
|
|
295
|
+
Undefined tolerance type (empty string).
|
|
296
|
+
absolute : str
|
|
297
|
+
Absolute tolerance type, using a fixed value.
|
|
298
|
+
relative : str
|
|
299
|
+
Relative tolerance type, using a percentage.
|
|
300
|
+
|
|
301
|
+
Examples
|
|
302
|
+
--------
|
|
303
|
+
>>> from nextmv.cloud import MetricToleranceType
|
|
304
|
+
>>> tol_type = MetricToleranceType.absolute
|
|
305
|
+
>>> tol_type
|
|
306
|
+
<MetricToleranceType.absolute: 'absolute'>
|
|
307
|
+
"""
|
|
308
|
+
|
|
245
309
|
undefined = ""
|
|
246
310
|
"""Undefined tolerance type."""
|
|
247
311
|
absolute = "absolute"
|
|
@@ -265,22 +329,22 @@ class MetricTolerance(BaseModel):
|
|
|
265
329
|
|
|
266
330
|
Attributes
|
|
267
331
|
----------
|
|
268
|
-
type :
|
|
332
|
+
type : MetricToleranceType
|
|
269
333
|
Type of tolerance (absolute or relative).
|
|
270
334
|
value : float
|
|
271
335
|
Value of the tolerance.
|
|
272
336
|
|
|
273
337
|
Examples
|
|
274
338
|
--------
|
|
275
|
-
>>> from nextmv.cloud import MetricTolerance,
|
|
276
|
-
>>> tolerance = MetricTolerance(type=
|
|
339
|
+
>>> from nextmv.cloud import MetricTolerance, MetricToleranceType
|
|
340
|
+
>>> tolerance = MetricTolerance(type=MetricToleranceType.absolute, value=0.1)
|
|
277
341
|
>>> tolerance.type
|
|
278
|
-
<
|
|
342
|
+
<MetricToleranceType.absolute: 'absolute'>
|
|
279
343
|
>>> tolerance.value
|
|
280
344
|
0.1
|
|
281
345
|
"""
|
|
282
346
|
|
|
283
|
-
type:
|
|
347
|
+
type: MetricToleranceType
|
|
284
348
|
"""Type of tolerance."""
|
|
285
349
|
value: float
|
|
286
350
|
"""Value of the tolerance."""
|
nextmv/cloud/application.py
CHANGED
|
@@ -46,6 +46,7 @@ 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 EnsembleDefinition, EvaluationRule, RunGroup
|
|
49
50
|
from nextmv.cloud.input_set import InputSet, ManagedInput
|
|
50
51
|
from nextmv.cloud.instance import Instance, InstanceConfiguration
|
|
51
52
|
from nextmv.cloud.scenario import Scenario, ScenarioInputType, _option_sets, _scenarios_by_id
|
|
@@ -64,6 +65,7 @@ from nextmv.run import (
|
|
|
64
65
|
Format,
|
|
65
66
|
FormatInput,
|
|
66
67
|
FormatOutput,
|
|
68
|
+
Run,
|
|
67
69
|
RunConfiguration,
|
|
68
70
|
RunInformation,
|
|
69
71
|
RunLog,
|
|
@@ -128,6 +130,8 @@ class Application:
|
|
|
128
130
|
"""Base endpoint for the application."""
|
|
129
131
|
experiments_endpoint: str = "{base}/experiments"
|
|
130
132
|
"""Base endpoint for the experiments in the application."""
|
|
133
|
+
ensembles_endpoint: str = "{base}/ensembles"
|
|
134
|
+
"""Base endpoint for managing the ensemble definitions in the application"""
|
|
131
135
|
|
|
132
136
|
def __post_init__(self):
|
|
133
137
|
"""Initialize the endpoint and experiments_endpoint attributes.
|
|
@@ -137,6 +141,7 @@ class Application:
|
|
|
137
141
|
"""
|
|
138
142
|
self.endpoint = self.endpoint.format(id=self.id)
|
|
139
143
|
self.experiments_endpoint = self.experiments_endpoint.format(base=self.endpoint)
|
|
144
|
+
self.ensembles_endpoint = self.ensembles_endpoint.format(base=self.endpoint)
|
|
140
145
|
|
|
141
146
|
@classmethod
|
|
142
147
|
def new(
|
|
@@ -289,7 +294,8 @@ class Application:
|
|
|
289
294
|
|
|
290
295
|
def batch_experiment(self, batch_id: str) -> BatchExperiment:
|
|
291
296
|
"""
|
|
292
|
-
Get a batch experiment.
|
|
297
|
+
Get a batch experiment. This method also returns the runs of the batch
|
|
298
|
+
experiment under the `.runs` attribute.
|
|
293
299
|
|
|
294
300
|
Parameters
|
|
295
301
|
----------
|
|
@@ -318,7 +324,17 @@ class Application:
|
|
|
318
324
|
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}",
|
|
319
325
|
)
|
|
320
326
|
|
|
321
|
-
|
|
327
|
+
exp = BatchExperiment.from_dict(response.json())
|
|
328
|
+
|
|
329
|
+
runs_response = self.client.request(
|
|
330
|
+
method="GET",
|
|
331
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}/runs",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
runs = [Run.from_dict(run) for run in runs_response.json().get("runs", [])]
|
|
335
|
+
exp.runs = runs
|
|
336
|
+
|
|
337
|
+
return exp
|
|
322
338
|
|
|
323
339
|
def batch_experiment_metadata(self, batch_id: str) -> BatchExperimentMetadata:
|
|
324
340
|
"""
|
|
@@ -503,6 +519,30 @@ class Application:
|
|
|
503
519
|
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}",
|
|
504
520
|
)
|
|
505
521
|
|
|
522
|
+
def delete_ensemble_definition(self, ensemble_definition_id: str) -> None:
|
|
523
|
+
"""
|
|
524
|
+
Delete an ensemble definition.
|
|
525
|
+
|
|
526
|
+
Parameters
|
|
527
|
+
----------
|
|
528
|
+
ensemble_definition_id : str
|
|
529
|
+
ID of the ensemble definition to delete.
|
|
530
|
+
|
|
531
|
+
Raises
|
|
532
|
+
------
|
|
533
|
+
requests.HTTPError
|
|
534
|
+
If the response status code is not 2xx.
|
|
535
|
+
|
|
536
|
+
Examples
|
|
537
|
+
--------
|
|
538
|
+
>>> app.delete_ensemble_definition("development-ensemble-definition")
|
|
539
|
+
"""
|
|
540
|
+
|
|
541
|
+
_ = self.client.request(
|
|
542
|
+
method="DELETE",
|
|
543
|
+
endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
|
|
544
|
+
)
|
|
545
|
+
|
|
506
546
|
def delete_scenario_test(self, scenario_test_id: str) -> None:
|
|
507
547
|
"""
|
|
508
548
|
Delete a scenario test.
|
|
@@ -551,6 +591,39 @@ class Application:
|
|
|
551
591
|
endpoint=f"{self.endpoint}/secrets/{secrets_collection_id}",
|
|
552
592
|
)
|
|
553
593
|
|
|
594
|
+
def ensemble_definition(self, ensemble_definition_id: str) -> EnsembleDefinition:
|
|
595
|
+
"""
|
|
596
|
+
Get an ensemble definition.
|
|
597
|
+
|
|
598
|
+
Parameters
|
|
599
|
+
----------
|
|
600
|
+
ensemble_definition_id : str
|
|
601
|
+
ID of the ensemble definition to retrieve.
|
|
602
|
+
|
|
603
|
+
Returns
|
|
604
|
+
-------
|
|
605
|
+
EnsembleDefintion
|
|
606
|
+
The requested ensemble definition details.
|
|
607
|
+
|
|
608
|
+
Raises
|
|
609
|
+
------
|
|
610
|
+
requests.HTTPError
|
|
611
|
+
If the response status code is not 2xx.
|
|
612
|
+
|
|
613
|
+
Examples
|
|
614
|
+
--------
|
|
615
|
+
>>> ensemble_definition = app.ensemble_definition("instance-123")
|
|
616
|
+
>>> print(ensemble_definition.name)
|
|
617
|
+
'Production Ensemble Definition'
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
response = self.client.request(
|
|
621
|
+
method="GET",
|
|
622
|
+
endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
626
|
+
|
|
554
627
|
@staticmethod
|
|
555
628
|
def exists(client: Client, id: str) -> bool:
|
|
556
629
|
"""
|
|
@@ -736,6 +809,36 @@ class Application:
|
|
|
736
809
|
|
|
737
810
|
return [BatchExperimentMetadata.from_dict(batch_experiment) for batch_experiment in response.json()]
|
|
738
811
|
|
|
812
|
+
def list_ensemble_definitions(self) -> list[EnsembleDefinition]:
|
|
813
|
+
"""
|
|
814
|
+
List all ensemble_definitions.
|
|
815
|
+
|
|
816
|
+
Returns
|
|
817
|
+
-------
|
|
818
|
+
list[EnsembleDefinition]
|
|
819
|
+
List of all ensemble definitions associated with this application.
|
|
820
|
+
|
|
821
|
+
Raises
|
|
822
|
+
------
|
|
823
|
+
requests.HTTPError
|
|
824
|
+
If the response status code is not 2xx.
|
|
825
|
+
|
|
826
|
+
Examples
|
|
827
|
+
--------
|
|
828
|
+
>>> ensemble_definitions = app.list_ensemble_definitions()
|
|
829
|
+
>>> for ensemble_definition in ensemble_definitions:
|
|
830
|
+
... print(ensemble_definition.name)
|
|
831
|
+
'Development Ensemble Definition'
|
|
832
|
+
'Production Ensemble Definition'
|
|
833
|
+
"""
|
|
834
|
+
|
|
835
|
+
response = self.client.request(
|
|
836
|
+
method="GET",
|
|
837
|
+
endpoint=f"{self.ensembles_endpoint}",
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
return [EnsembleDefinition.from_dict(ensemble_definition) for ensemble_definition in response.json()["items"]]
|
|
841
|
+
|
|
739
842
|
def list_input_sets(self) -> list[InputSet]:
|
|
740
843
|
"""
|
|
741
844
|
List all input sets.
|
|
@@ -818,6 +921,28 @@ class Application:
|
|
|
818
921
|
|
|
819
922
|
return [ManagedInput.from_dict(managed_input) for managed_input in response.json()]
|
|
820
923
|
|
|
924
|
+
def list_runs(self) -> list[Run]:
|
|
925
|
+
"""
|
|
926
|
+
List all runs.
|
|
927
|
+
|
|
928
|
+
Returns
|
|
929
|
+
-------
|
|
930
|
+
list[Run]
|
|
931
|
+
List of runs.
|
|
932
|
+
|
|
933
|
+
Raises
|
|
934
|
+
------
|
|
935
|
+
requests.HTTPError
|
|
936
|
+
If the response status code is not 2xx.
|
|
937
|
+
"""
|
|
938
|
+
|
|
939
|
+
response = self.client.request(
|
|
940
|
+
method="GET",
|
|
941
|
+
endpoint=f"{self.endpoint}/runs",
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
return [Run.from_dict(run) for run in response.json().get("runs", [])]
|
|
945
|
+
|
|
821
946
|
def list_scenario_tests(self) -> list[BatchExperimentMetadata]:
|
|
822
947
|
"""
|
|
823
948
|
List all batch scenario tests. Scenario tests are based on the batch
|
|
@@ -1280,6 +1405,53 @@ class Application:
|
|
|
1280
1405
|
|
|
1281
1406
|
return self.batch_experiment_with_polling(batch_id=batch_id, polling_options=polling_options)
|
|
1282
1407
|
|
|
1408
|
+
def new_ensemble_defintion(
|
|
1409
|
+
self,
|
|
1410
|
+
id: str,
|
|
1411
|
+
run_groups: list[RunGroup],
|
|
1412
|
+
rules: list[EvaluationRule],
|
|
1413
|
+
name: Optional[str] = None,
|
|
1414
|
+
description: Optional[str] = None,
|
|
1415
|
+
) -> EnsembleDefinition:
|
|
1416
|
+
"""
|
|
1417
|
+
Create a new ensemble definition.
|
|
1418
|
+
|
|
1419
|
+
Parameters
|
|
1420
|
+
----------
|
|
1421
|
+
id: str
|
|
1422
|
+
ID of the ensemble defintion.
|
|
1423
|
+
run_groups: list[RunGroup]
|
|
1424
|
+
Information to facilitate the execution of child runs.
|
|
1425
|
+
rules: list[EvaluationRule]
|
|
1426
|
+
Information to facilitate the selection of
|
|
1427
|
+
a result for the ensemble run from child runs.
|
|
1428
|
+
name: Optional[str]
|
|
1429
|
+
Name of the ensemble definition.
|
|
1430
|
+
description: Optional[str]
|
|
1431
|
+
Description of the ensemble definition.
|
|
1432
|
+
"""
|
|
1433
|
+
|
|
1434
|
+
if name is None:
|
|
1435
|
+
name = id
|
|
1436
|
+
if description is None:
|
|
1437
|
+
description = name
|
|
1438
|
+
|
|
1439
|
+
payload = {
|
|
1440
|
+
"id": id,
|
|
1441
|
+
"run_groups": [run_group.to_dict() for run_group in run_groups],
|
|
1442
|
+
"rules": [rule.to_dict() for rule in rules],
|
|
1443
|
+
"name": name,
|
|
1444
|
+
"description": description,
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
response = self.client.request(
|
|
1448
|
+
method="POST",
|
|
1449
|
+
endpoint=f"{self.ensembles_endpoint}",
|
|
1450
|
+
payload=payload,
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
1454
|
+
|
|
1283
1455
|
def new_input_set(
|
|
1284
1456
|
self,
|
|
1285
1457
|
id: str,
|
|
@@ -2202,13 +2374,14 @@ class Application:
|
|
|
2202
2374
|
|
|
2203
2375
|
if id is None:
|
|
2204
2376
|
id = safe_id(prefix="version")
|
|
2377
|
+
if name is None:
|
|
2378
|
+
name = id
|
|
2205
2379
|
|
|
2206
2380
|
payload = {
|
|
2207
2381
|
"id": id,
|
|
2382
|
+
"name": name,
|
|
2208
2383
|
}
|
|
2209
2384
|
|
|
2210
|
-
if name is not None:
|
|
2211
|
-
payload["name"] = name
|
|
2212
2385
|
if description is not None:
|
|
2213
2386
|
payload["description"] = description
|
|
2214
2387
|
|
|
@@ -2759,7 +2932,7 @@ class Application:
|
|
|
2759
2932
|
Examples
|
|
2760
2933
|
--------
|
|
2761
2934
|
>>> from nextmv.cloud import Application
|
|
2762
|
-
>>> from nextmv
|
|
2935
|
+
>>> from nextmv import TrackedRun
|
|
2763
2936
|
>>> app = Application(id="app_123")
|
|
2764
2937
|
>>> tracked_run = TrackedRun(input={"data": [...]}, output={"solution": [...]})
|
|
2765
2938
|
>>> run_id = app.track_run(tracked_run)
|
|
@@ -2900,6 +3073,98 @@ class Application:
|
|
|
2900
3073
|
output_dir_path=output_dir_path,
|
|
2901
3074
|
)
|
|
2902
3075
|
|
|
3076
|
+
def update_batch_experiment(
|
|
3077
|
+
self,
|
|
3078
|
+
batch_experiment_id: str,
|
|
3079
|
+
name: Optional[str] = None,
|
|
3080
|
+
description: Optional[str] = None,
|
|
3081
|
+
) -> BatchExperimentInformation:
|
|
3082
|
+
"""
|
|
3083
|
+
Update a batch experiment.
|
|
3084
|
+
|
|
3085
|
+
Parameters
|
|
3086
|
+
----------
|
|
3087
|
+
batch_experiment_id : str
|
|
3088
|
+
ID of the batch experiment to update.
|
|
3089
|
+
name : Optional[str], default=None
|
|
3090
|
+
Optional name of the batch experiment.
|
|
3091
|
+
description : Optional[str], default=None
|
|
3092
|
+
Optional description of the batch experiment.
|
|
3093
|
+
|
|
3094
|
+
Returns
|
|
3095
|
+
-------
|
|
3096
|
+
BatchExperimentInformation
|
|
3097
|
+
The information with the updated batch experiment.
|
|
3098
|
+
|
|
3099
|
+
Raises
|
|
3100
|
+
------
|
|
3101
|
+
requests.HTTPError
|
|
3102
|
+
If the response status code is not 2xx.
|
|
3103
|
+
"""
|
|
3104
|
+
|
|
3105
|
+
payload = {}
|
|
3106
|
+
|
|
3107
|
+
if name is not None:
|
|
3108
|
+
payload["name"] = name
|
|
3109
|
+
if description is not None:
|
|
3110
|
+
payload["description"] = description
|
|
3111
|
+
|
|
3112
|
+
response = self.client.request(
|
|
3113
|
+
method="PATCH",
|
|
3114
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
|
|
3115
|
+
payload=payload,
|
|
3116
|
+
)
|
|
3117
|
+
|
|
3118
|
+
return BatchExperimentInformation.from_dict(response.json())
|
|
3119
|
+
|
|
3120
|
+
def update_ensemble_definition(
|
|
3121
|
+
self,
|
|
3122
|
+
id: str,
|
|
3123
|
+
name: Optional[str] = None,
|
|
3124
|
+
description: Optional[str] = None,
|
|
3125
|
+
) -> EnsembleDefinition:
|
|
3126
|
+
"""
|
|
3127
|
+
Update an ensemble definition.
|
|
3128
|
+
|
|
3129
|
+
Parameters
|
|
3130
|
+
----------
|
|
3131
|
+
id : str
|
|
3132
|
+
ID of the ensemble definition to update.
|
|
3133
|
+
name : Optional[str], default=None
|
|
3134
|
+
Optional name of the ensemble definition.
|
|
3135
|
+
description : Optional[str], default=None
|
|
3136
|
+
Optional description of the ensemble definition.
|
|
3137
|
+
|
|
3138
|
+
Returns
|
|
3139
|
+
-------
|
|
3140
|
+
EnsembleDefinition
|
|
3141
|
+
The updated ensemble definition.
|
|
3142
|
+
|
|
3143
|
+
Raises
|
|
3144
|
+
------
|
|
3145
|
+
ValueError
|
|
3146
|
+
If neither name nor description is updated
|
|
3147
|
+
requests.HTTPError
|
|
3148
|
+
If the response status code is not 2xx.
|
|
3149
|
+
"""
|
|
3150
|
+
|
|
3151
|
+
payload = {}
|
|
3152
|
+
|
|
3153
|
+
if name is None and description is None:
|
|
3154
|
+
raise ValueError("Must define at least one value among name and description to modify")
|
|
3155
|
+
if name is not None:
|
|
3156
|
+
payload["name"] = name
|
|
3157
|
+
if description is not None:
|
|
3158
|
+
payload["description"] = description
|
|
3159
|
+
|
|
3160
|
+
response = self.client.request(
|
|
3161
|
+
method="PATCH",
|
|
3162
|
+
endpoint=f"{self.ensembles_endpoint}/{id}",
|
|
3163
|
+
payload=payload,
|
|
3164
|
+
)
|
|
3165
|
+
|
|
3166
|
+
return EnsembleDefinition.from_dict(response.json())
|
|
3167
|
+
|
|
2903
3168
|
def update_instance(
|
|
2904
3169
|
self,
|
|
2905
3170
|
id: str,
|
|
@@ -2963,50 +3228,6 @@ class Application:
|
|
|
2963
3228
|
|
|
2964
3229
|
return Instance.from_dict(response.json())
|
|
2965
3230
|
|
|
2966
|
-
def update_batch_experiment(
|
|
2967
|
-
self,
|
|
2968
|
-
batch_experiment_id: str,
|
|
2969
|
-
name: Optional[str] = None,
|
|
2970
|
-
description: Optional[str] = None,
|
|
2971
|
-
) -> BatchExperimentInformation:
|
|
2972
|
-
"""
|
|
2973
|
-
Update a batch experiment.
|
|
2974
|
-
|
|
2975
|
-
Parameters
|
|
2976
|
-
----------
|
|
2977
|
-
batch_experiment_id : str
|
|
2978
|
-
ID of the batch experiment to update.
|
|
2979
|
-
name : Optional[str], default=None
|
|
2980
|
-
Optional name of the batch experiment.
|
|
2981
|
-
description : Optional[str], default=None
|
|
2982
|
-
Optional description of the batch experiment.
|
|
2983
|
-
|
|
2984
|
-
Returns
|
|
2985
|
-
-------
|
|
2986
|
-
BatchExperimentInformation
|
|
2987
|
-
The information with the updated batch experiment.
|
|
2988
|
-
|
|
2989
|
-
Raises
|
|
2990
|
-
------
|
|
2991
|
-
requests.HTTPError
|
|
2992
|
-
If the response status code is not 2xx.
|
|
2993
|
-
"""
|
|
2994
|
-
|
|
2995
|
-
payload = {}
|
|
2996
|
-
|
|
2997
|
-
if name is not None:
|
|
2998
|
-
payload["name"] = name
|
|
2999
|
-
if description is not None:
|
|
3000
|
-
payload["description"] = description
|
|
3001
|
-
|
|
3002
|
-
response = self.client.request(
|
|
3003
|
-
method="PATCH",
|
|
3004
|
-
endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
|
|
3005
|
-
payload=payload,
|
|
3006
|
-
)
|
|
3007
|
-
|
|
3008
|
-
return BatchExperimentInformation.from_dict(response.json())
|
|
3009
|
-
|
|
3010
3231
|
def update_managed_input(
|
|
3011
3232
|
self,
|
|
3012
3233
|
managed_input_id: str,
|
|
@@ -3647,65 +3868,40 @@ class Application:
|
|
|
3647
3868
|
"""
|
|
3648
3869
|
Auxiliary function to validate the directory path and configuration.
|
|
3649
3870
|
"""
|
|
3871
|
+
input_type = self.__get_input_type(configuration)
|
|
3650
3872
|
|
|
3651
|
-
|
|
3873
|
+
# If no explicit input type is defined, there is nothing to validate.
|
|
3874
|
+
if input_type is None:
|
|
3652
3875
|
return
|
|
3653
3876
|
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
)
|
|
3658
|
-
|
|
3659
|
-
config_format = self.__extract_config_format(configuration)
|
|
3660
|
-
|
|
3661
|
-
if config_format is None:
|
|
3877
|
+
# Validate that the input directory path is provided when explicitly required.
|
|
3878
|
+
dir_types = (InputFormat.MULTI_FILE, InputFormat.CSV_ARCHIVE)
|
|
3879
|
+
if input_type in dir_types and not input_dir_path:
|
|
3662
3880
|
raise ValueError(
|
|
3663
|
-
"If
|
|
3881
|
+
f"If RunConfiguration.format.format_input.input_type is set to {input_type}, "
|
|
3882
|
+
"then input_dir_path must be provided.",
|
|
3664
3883
|
)
|
|
3665
3884
|
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
"If dir_path is provided, RunConfiguration.format.format_input.input_type must be set to a valid type. "
|
|
3671
|
-
f"Valid types are: {[InputFormat.CSV_ARCHIVE, InputFormat.MULTI_FILE]}",
|
|
3672
|
-
)
|
|
3673
|
-
|
|
3674
|
-
def __extract_config_format(self, configuration: Union[RunConfiguration, dict[str, Any]]) -> Any:
|
|
3675
|
-
"""Extract format from configuration, handling both RunConfiguration objects and dicts."""
|
|
3676
|
-
if isinstance(configuration, RunConfiguration):
|
|
3677
|
-
return configuration.format
|
|
3678
|
-
|
|
3679
|
-
if isinstance(configuration, dict):
|
|
3680
|
-
config_format = configuration.get("format")
|
|
3681
|
-
if config_format is not None and isinstance(config_format, dict):
|
|
3682
|
-
return Format.from_dict(config_format) if hasattr(Format, "from_dict") else config_format
|
|
3683
|
-
|
|
3684
|
-
return config_format
|
|
3685
|
-
|
|
3686
|
-
raise ValueError("Configuration must be a RunConfiguration object or a dict.")
|
|
3687
|
-
|
|
3688
|
-
def __extract_input_type(self, config_format: Any) -> Any:
|
|
3689
|
-
"""Extract input type from config format."""
|
|
3690
|
-
if isinstance(config_format, dict):
|
|
3691
|
-
format_input = config_format.get("format_input") or config_format.get("input")
|
|
3692
|
-
if format_input is None:
|
|
3693
|
-
raise ValueError(
|
|
3694
|
-
"If dir_path is provided, RunConfiguration.format.format_input must also be provided.",
|
|
3695
|
-
)
|
|
3885
|
+
def __get_input_type(self, config: Union[RunConfiguration, dict[str, Any]]) -> Optional[InputFormat]:
|
|
3886
|
+
"""
|
|
3887
|
+
Auxiliary function to extract the input type from the run configuration.
|
|
3888
|
+
"""
|
|
3696
3889
|
|
|
3697
|
-
|
|
3698
|
-
|
|
3890
|
+
if config is None:
|
|
3891
|
+
return None
|
|
3699
3892
|
|
|
3700
|
-
|
|
3893
|
+
if isinstance(config, dict):
|
|
3894
|
+
config = RunConfiguration.from_dict(config)
|
|
3701
3895
|
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3896
|
+
if (
|
|
3897
|
+
isinstance(config, RunConfiguration)
|
|
3898
|
+
and config.format is not None
|
|
3899
|
+
and config.format.format_input is not None
|
|
3900
|
+
and config.format.format_input.input_type is not None
|
|
3901
|
+
):
|
|
3902
|
+
return config.format.format_input.input_type
|
|
3707
3903
|
|
|
3708
|
-
return
|
|
3904
|
+
return None
|
|
3709
3905
|
|
|
3710
3906
|
def __package_inputs(self, dir_path: str) -> str:
|
|
3711
3907
|
"""
|