nextmv 0.26.3__py3-none-any.whl → 0.28.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/__entrypoint__.py +3 -5
- nextmv/__init__.py +1 -0
- nextmv/base_model.py +52 -7
- nextmv/cloud/__init__.py +3 -0
- nextmv/cloud/acceptance_test.py +711 -20
- nextmv/cloud/account.py +152 -7
- nextmv/cloud/application.py +1231 -396
- nextmv/cloud/batch_experiment.py +133 -21
- nextmv/cloud/client.py +240 -46
- nextmv/cloud/input_set.py +96 -3
- nextmv/cloud/instance.py +89 -3
- nextmv/cloud/manifest.py +625 -87
- nextmv/cloud/package.py +2 -2
- nextmv/cloud/run.py +376 -45
- nextmv/cloud/safe.py +7 -7
- nextmv/cloud/scenario.py +205 -20
- nextmv/cloud/secrets.py +179 -6
- nextmv/cloud/status.py +95 -2
- nextmv/cloud/version.py +132 -4
- nextmv/deprecated.py +36 -2
- nextmv/input.py +300 -82
- nextmv/logger.py +71 -7
- nextmv/model.py +225 -58
- nextmv/options.py +301 -69
- nextmv/output.py +667 -236
- {nextmv-0.26.3.dist-info → nextmv-0.28.0.dist-info}/METADATA +1 -1
- nextmv-0.28.0.dist-info/RECORD +30 -0
- nextmv-0.26.3.dist-info/RECORD +0 -30
- {nextmv-0.26.3.dist-info → nextmv-0.28.0.dist-info}/WHEEL +0 -0
- {nextmv-0.26.3.dist-info → nextmv-0.28.0.dist-info}/licenses/LICENSE +0 -0
nextmv/cloud/manifest.py
CHANGED
|
@@ -1,4 +1,35 @@
|
|
|
1
|
-
"""Module with the logic for handling an app manifest.
|
|
1
|
+
"""Module with the logic for handling an app manifest.
|
|
2
|
+
|
|
3
|
+
This module provides classes and functions for managing Nextmv app manifests.
|
|
4
|
+
Manifest files (app.yaml) define how an application is built, run, and deployed
|
|
5
|
+
on the Nextmv Cloud platform.
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
ManifestType
|
|
10
|
+
Enum for application types based on programming language.
|
|
11
|
+
ManifestRuntime
|
|
12
|
+
Enum for runtime environments where apps run on Nextmv Cloud.
|
|
13
|
+
ManifestBuild
|
|
14
|
+
Class for build-specific attributes in the manifest.
|
|
15
|
+
ManifestPythonModel
|
|
16
|
+
Class for model-specific instructions for Python apps.
|
|
17
|
+
ManifestPython
|
|
18
|
+
Class for Python-specific instructions in the manifest.
|
|
19
|
+
ManifestOption
|
|
20
|
+
Class representing an option for the decision model in the manifest.
|
|
21
|
+
ManifestOptions
|
|
22
|
+
Class containing a list of options for the decision model.
|
|
23
|
+
ManifestConfiguration
|
|
24
|
+
Class for configuration settings for the decision model.
|
|
25
|
+
Manifest
|
|
26
|
+
Main class representing an app manifest for Nextmv Cloud.
|
|
27
|
+
|
|
28
|
+
Constants
|
|
29
|
+
--------
|
|
30
|
+
FILE_NAME
|
|
31
|
+
Name of the app manifest file.
|
|
32
|
+
"""
|
|
2
33
|
|
|
3
34
|
import os
|
|
4
35
|
from enum import Enum
|
|
@@ -11,13 +42,54 @@ from nextmv.base_model import BaseModel
|
|
|
11
42
|
from nextmv.model import _REQUIREMENTS_FILE, ModelConfiguration
|
|
12
43
|
from nextmv.options import Option, Options
|
|
13
44
|
|
|
14
|
-
|
|
15
|
-
"""Name of the app manifest file.
|
|
45
|
+
MANIFEST_FILE_NAME = "app.yaml"
|
|
46
|
+
"""Name of the app manifest file.
|
|
47
|
+
|
|
48
|
+
This constant defines the standard filename for Nextmv app manifest files.
|
|
49
|
+
|
|
50
|
+
You can import the `FILE_NAME` constant directly from `cloud`:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from nextmv.cloud import FILE_NAME
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Notes
|
|
57
|
+
-----
|
|
58
|
+
All Nextmv applications must include an app.yaml file for proper deployment.
|
|
59
|
+
"""
|
|
16
60
|
|
|
17
61
|
|
|
18
62
|
class ManifestType(str, Enum):
|
|
19
|
-
"""
|
|
20
|
-
language.
|
|
63
|
+
"""
|
|
64
|
+
Type of application in the manifest, based on the programming language.
|
|
65
|
+
|
|
66
|
+
You can import the `ManifestType` class directly from `cloud`:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from nextmv.cloud import ManifestType
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This enum defines the supported programming languages for applications
|
|
73
|
+
that can be deployed on Nextmv Cloud.
|
|
74
|
+
|
|
75
|
+
Attributes
|
|
76
|
+
----------
|
|
77
|
+
PYTHON : str
|
|
78
|
+
Python format, used for Python applications.
|
|
79
|
+
GO : str
|
|
80
|
+
Go format, used for Go applications.
|
|
81
|
+
JAVA : str
|
|
82
|
+
Java format, used for Java applications.
|
|
83
|
+
|
|
84
|
+
Examples
|
|
85
|
+
--------
|
|
86
|
+
>>> from nextmv.cloud import ManifestType
|
|
87
|
+
>>> manifest_type = ManifestType.PYTHON
|
|
88
|
+
>>> manifest_type
|
|
89
|
+
<ManifestType.PYTHON: 'python'>
|
|
90
|
+
>>> str(manifest_type)
|
|
91
|
+
'python'
|
|
92
|
+
"""
|
|
21
93
|
|
|
22
94
|
PYTHON = "python"
|
|
23
95
|
"""Python format"""
|
|
@@ -28,7 +100,41 @@ class ManifestType(str, Enum):
|
|
|
28
100
|
|
|
29
101
|
|
|
30
102
|
class ManifestRuntime(str, Enum):
|
|
31
|
-
"""
|
|
103
|
+
"""
|
|
104
|
+
Runtime (environment) where the app will be run on Nextmv Cloud.
|
|
105
|
+
|
|
106
|
+
You can import the `ManifestRuntime` class directly from `cloud`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from nextmv.cloud import ManifestRuntime
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This enum defines the supported runtime environments for applications
|
|
113
|
+
that can be deployed on Nextmv Cloud.
|
|
114
|
+
|
|
115
|
+
Attributes
|
|
116
|
+
----------
|
|
117
|
+
DEFAULT : str
|
|
118
|
+
This runtime is used to run compiled applications such as Go binaries.
|
|
119
|
+
PYTHON : str
|
|
120
|
+
This runtime is used as the basis for all other Python runtimes and
|
|
121
|
+
Python applications.
|
|
122
|
+
JAVA : str
|
|
123
|
+
This runtime is used to run Java applications.
|
|
124
|
+
PYOMO : str
|
|
125
|
+
This runtime provisions Python packages to run Pyomo applications.
|
|
126
|
+
HEXALY : str
|
|
127
|
+
This runtime provisions Python packages to run Hexaly applications.
|
|
128
|
+
|
|
129
|
+
Examples
|
|
130
|
+
--------
|
|
131
|
+
>>> from nextmv.cloud import ManifestRuntime
|
|
132
|
+
>>> runtime = ManifestRuntime.PYTHON
|
|
133
|
+
>>> runtime
|
|
134
|
+
<ManifestRuntime.PYTHON: 'ghcr.io/nextmv-io/runtime/python:3.11'>
|
|
135
|
+
>>> str(runtime)
|
|
136
|
+
'ghcr.io/nextmv-io/runtime/python:3.11'
|
|
137
|
+
"""
|
|
32
138
|
|
|
33
139
|
DEFAULT = "ghcr.io/nextmv-io/runtime/default:latest"
|
|
34
140
|
"""This runtime is used to run compiled applications such as Go binaries."""
|
|
@@ -49,19 +155,48 @@ class ManifestRuntime(str, Enum):
|
|
|
49
155
|
|
|
50
156
|
|
|
51
157
|
class ManifestBuild(BaseModel):
|
|
52
|
-
"""
|
|
158
|
+
"""
|
|
159
|
+
Build-specific attributes.
|
|
160
|
+
|
|
161
|
+
You can import the `ManifestBuild` class directly from `cloud`:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from nextmv.cloud import ManifestBuild
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
command : Optional[str], default=None
|
|
170
|
+
The command to run to build the app. This command will be executed
|
|
171
|
+
without a shell, i.e., directly. The command must exit with a status of
|
|
172
|
+
0 to continue the push process of the app to Nextmv Cloud. This command
|
|
173
|
+
is executed prior to the pre-push command.
|
|
174
|
+
environment : Optional[dict[str, Any]], default=None
|
|
175
|
+
Environment variables to set when running the build command given as
|
|
176
|
+
key-value pairs.
|
|
177
|
+
|
|
178
|
+
Examples
|
|
179
|
+
--------
|
|
180
|
+
>>> from nextmv.cloud import ManifestBuild
|
|
181
|
+
>>> build_config = ManifestBuild(
|
|
182
|
+
... command="make build",
|
|
183
|
+
... environment={"DEBUG": "true"}
|
|
184
|
+
... )
|
|
185
|
+
>>> build_config.command
|
|
186
|
+
'make build'
|
|
187
|
+
"""
|
|
53
188
|
|
|
54
189
|
command: Optional[str] = None
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
a shell, i.e., directly. The command
|
|
58
|
-
continue the push process of the app to
|
|
59
|
-
executed prior to the pre-push command.
|
|
190
|
+
"""The command to run to build the app.
|
|
191
|
+
|
|
192
|
+
This command will be executed without a shell, i.e., directly. The command
|
|
193
|
+
must exit with a status of 0 to continue the push process of the app to
|
|
194
|
+
Nextmv Cloud. This command is executed prior to the pre-push command.
|
|
60
195
|
"""
|
|
61
196
|
environment: Optional[dict[str, Any]] = None
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
key-value pairs.
|
|
197
|
+
"""Environment variables to set when running the build command.
|
|
198
|
+
|
|
199
|
+
Given as key-value pairs.
|
|
65
200
|
"""
|
|
66
201
|
|
|
67
202
|
def environment_to_dict(self) -> dict[str, str]:
|
|
@@ -71,8 +206,18 @@ class ManifestBuild(BaseModel):
|
|
|
71
206
|
Returns
|
|
72
207
|
-------
|
|
73
208
|
dict[str, str]
|
|
74
|
-
The environment variables as a dictionary.
|
|
75
|
-
|
|
209
|
+
The environment variables as a dictionary of string key-value pairs.
|
|
210
|
+
Returns an empty dictionary if no environment variables are set.
|
|
211
|
+
|
|
212
|
+
Examples
|
|
213
|
+
--------
|
|
214
|
+
>>> from nextmv.cloud import ManifestBuild
|
|
215
|
+
>>> build_config = ManifestBuild(environment={"COUNT": 1, "NAME": "test"})
|
|
216
|
+
>>> build_config.environment_to_dict()
|
|
217
|
+
{'COUNT': '1', 'NAME': 'test'}
|
|
218
|
+
>>> build_config_empty = ManifestBuild()
|
|
219
|
+
>>> build_config_empty.environment_to_dict()
|
|
220
|
+
{}
|
|
76
221
|
"""
|
|
77
222
|
|
|
78
223
|
if self.environment is None:
|
|
@@ -82,7 +227,35 @@ class ManifestBuild(BaseModel):
|
|
|
82
227
|
|
|
83
228
|
|
|
84
229
|
class ManifestPythonModel(BaseModel):
|
|
85
|
-
"""
|
|
230
|
+
"""
|
|
231
|
+
Model-specific instructions for a Python app.
|
|
232
|
+
|
|
233
|
+
You can import the `ManifestPythonModel` class directly from `cloud`:
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from nextmv.cloud import ManifestPythonModel
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
name : str
|
|
242
|
+
The name of the decision model.
|
|
243
|
+
options : Optional[list[dict[str, Any]]], default=None
|
|
244
|
+
Options for the decision model. This is a data representation of the
|
|
245
|
+
`nextmv.Options` class. It consists of a list of dicts. Each dict
|
|
246
|
+
represents the `nextmv.Option` class. It is used to be able to
|
|
247
|
+
reconstruct an Options object from data when loading a decision model.
|
|
248
|
+
|
|
249
|
+
Examples
|
|
250
|
+
--------
|
|
251
|
+
>>> from nextmv.cloud import ManifestPythonModel
|
|
252
|
+
>>> python_model_config = ManifestPythonModel(
|
|
253
|
+
... name="routing_model",
|
|
254
|
+
... options=[{"name": "max_vehicles", "type": "int", "default": 10}]
|
|
255
|
+
... )
|
|
256
|
+
>>> python_model_config.name
|
|
257
|
+
'routing_model'
|
|
258
|
+
"""
|
|
86
259
|
|
|
87
260
|
name: str
|
|
88
261
|
"""The name of the decision model."""
|
|
@@ -96,26 +269,97 @@ class ManifestPythonModel(BaseModel):
|
|
|
96
269
|
|
|
97
270
|
|
|
98
271
|
class ManifestPython(BaseModel):
|
|
99
|
-
"""
|
|
272
|
+
"""
|
|
273
|
+
Python-specific instructions.
|
|
274
|
+
|
|
275
|
+
You can import the `ManifestPython` class directly from `cloud`:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
from nextmv.cloud import ManifestPython
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
pip_requirements : Optional[str], default=None
|
|
284
|
+
Path to a requirements.txt file containing (additional) Python
|
|
285
|
+
dependencies that will be bundled with the app.
|
|
286
|
+
Aliases: `pip-requirements`.
|
|
287
|
+
model : Optional[ManifestPythonModel], default=None
|
|
288
|
+
Information about an encoded decision model as handled via mlflow. This
|
|
289
|
+
information is used to load the decision model from the app bundle.
|
|
290
|
+
|
|
291
|
+
Examples
|
|
292
|
+
--------
|
|
293
|
+
>>> from nextmv.cloud import ManifestPython, ManifestPythonModel
|
|
294
|
+
>>> python_config = ManifestPython(
|
|
295
|
+
... pip_requirements="requirements.txt",
|
|
296
|
+
... model=ManifestPythonModel(name="my_model")
|
|
297
|
+
... )
|
|
298
|
+
>>> python_config.pip_requirements
|
|
299
|
+
'requirements.txt'
|
|
300
|
+
"""
|
|
100
301
|
|
|
101
302
|
pip_requirements: Optional[str] = Field(
|
|
102
303
|
serialization_alias="pip-requirements",
|
|
103
304
|
validation_alias=AliasChoices("pip-requirements", "pip_requirements"),
|
|
104
305
|
default=None,
|
|
105
306
|
)
|
|
106
|
-
"""
|
|
107
|
-
|
|
108
|
-
dependencies that will be bundled with the
|
|
307
|
+
"""Path to a requirements.txt file.
|
|
308
|
+
|
|
309
|
+
Contains (additional) Python dependencies that will be bundled with the
|
|
310
|
+
app.
|
|
109
311
|
"""
|
|
110
312
|
model: Optional[ManifestPythonModel] = None
|
|
111
|
-
"""
|
|
112
|
-
|
|
113
|
-
information is used to load the decision model
|
|
313
|
+
"""Information about an encoded decision model.
|
|
314
|
+
|
|
315
|
+
As handled via mlflow. This information is used to load the decision model
|
|
316
|
+
from the app bundle.
|
|
114
317
|
"""
|
|
115
318
|
|
|
116
319
|
|
|
117
320
|
class ManifestOption(BaseModel):
|
|
118
|
-
"""
|
|
321
|
+
"""
|
|
322
|
+
An option for the decision model that is recorded in the manifest.
|
|
323
|
+
|
|
324
|
+
You can import the `ManifestOption` class directly from `cloud`:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from nextmv.cloud import ManifestOption
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
name : str
|
|
333
|
+
The name of the option.
|
|
334
|
+
option_type : str
|
|
335
|
+
The type of the option. This is a string representation of the
|
|
336
|
+
`nextmv.Option` class (e.g., "string", "int", "bool", "float").
|
|
337
|
+
Aliases: `type`.
|
|
338
|
+
default : Optional[Any], default=None
|
|
339
|
+
The default value of the option.
|
|
340
|
+
description : Optional[str], default=""
|
|
341
|
+
The description of the option.
|
|
342
|
+
required : bool, default=False
|
|
343
|
+
Whether the option is required or not.
|
|
344
|
+
additional_attributes : Optional[dict[str, Any]], default=None
|
|
345
|
+
Optional additional attributes for the option. The Nextmv Cloud may
|
|
346
|
+
perform validation on these attributes. For example, the maximum
|
|
347
|
+
length of a string or the maximum value of an integer. These
|
|
348
|
+
additional attributes will be shown in the help message of the
|
|
349
|
+
`Options`.
|
|
350
|
+
|
|
351
|
+
Examples
|
|
352
|
+
--------
|
|
353
|
+
>>> from nextmv.cloud import ManifestOption
|
|
354
|
+
>>> option = ManifestOption(
|
|
355
|
+
... name="solve.duration",
|
|
356
|
+
... option_type="string",
|
|
357
|
+
... default="30s",
|
|
358
|
+
... description="Maximum duration for the solver."
|
|
359
|
+
... )
|
|
360
|
+
>>> option.name
|
|
361
|
+
'solve.duration'
|
|
362
|
+
"""
|
|
119
363
|
|
|
120
364
|
name: str
|
|
121
365
|
"""The name of the option"""
|
|
@@ -123,7 +367,7 @@ class ManifestOption(BaseModel):
|
|
|
123
367
|
serialization_alias="type",
|
|
124
368
|
validation_alias=AliasChoices("type", "option_type"),
|
|
125
369
|
)
|
|
126
|
-
"""The type of the option"""
|
|
370
|
+
"""The type of the option (e.g., "string", "int", "bool", "float)."""
|
|
127
371
|
|
|
128
372
|
default: Optional[Any] = None
|
|
129
373
|
"""The default value of the option"""
|
|
@@ -131,8 +375,13 @@ class ManifestOption(BaseModel):
|
|
|
131
375
|
"""The description of the option"""
|
|
132
376
|
required: bool = False
|
|
133
377
|
"""Whether the option is required or not"""
|
|
134
|
-
|
|
135
|
-
"""
|
|
378
|
+
additional_attributes: Optional[dict[str, Any]] = None
|
|
379
|
+
"""Optional additional attributes for the option.
|
|
380
|
+
|
|
381
|
+
The Nextmv Cloud may perform validation on these attributes. For example,
|
|
382
|
+
the maximum length of a string or the maximum value of an integer. These
|
|
383
|
+
additional attributes will be shown in the help message of the `Options`.
|
|
384
|
+
"""
|
|
136
385
|
|
|
137
386
|
@classmethod
|
|
138
387
|
def from_option(cls, option: Option) -> "ManifestOption":
|
|
@@ -141,21 +390,37 @@ class ManifestOption(BaseModel):
|
|
|
141
390
|
|
|
142
391
|
Parameters
|
|
143
392
|
----------
|
|
144
|
-
option: Option
|
|
393
|
+
option : nextmv.options.Option
|
|
145
394
|
The option to convert.
|
|
146
395
|
|
|
147
396
|
Returns
|
|
148
397
|
-------
|
|
149
398
|
ManifestOption
|
|
150
399
|
The converted option.
|
|
400
|
+
|
|
401
|
+
Raises
|
|
402
|
+
------
|
|
403
|
+
ValueError
|
|
404
|
+
If the `option.option_type` is unknown.
|
|
405
|
+
|
|
406
|
+
Examples
|
|
407
|
+
--------
|
|
408
|
+
>>> from nextmv.options import Option
|
|
409
|
+
>>> from nextmv.cloud import ManifestOption
|
|
410
|
+
>>> sdk_option = Option(name="max_stops", option_type=int, default=100)
|
|
411
|
+
>>> manifest_opt = ManifestOption.from_option(sdk_option)
|
|
412
|
+
>>> manifest_opt.name
|
|
413
|
+
'max_stops'
|
|
414
|
+
>>> manifest_opt.option_type
|
|
415
|
+
'int'
|
|
151
416
|
"""
|
|
152
417
|
option_type = option.option_type
|
|
153
418
|
if option_type is str:
|
|
154
419
|
option_type = "string"
|
|
155
420
|
elif option_type is bool:
|
|
156
|
-
option_type = "
|
|
421
|
+
option_type = "bool"
|
|
157
422
|
elif option_type is int:
|
|
158
|
-
option_type = "
|
|
423
|
+
option_type = "int"
|
|
159
424
|
elif option_type is float:
|
|
160
425
|
option_type = "float"
|
|
161
426
|
else:
|
|
@@ -167,7 +432,7 @@ class ManifestOption(BaseModel):
|
|
|
167
432
|
default=option.default,
|
|
168
433
|
description=option.description,
|
|
169
434
|
required=option.required,
|
|
170
|
-
|
|
435
|
+
additional_attributes=option.additional_attributes,
|
|
171
436
|
)
|
|
172
437
|
|
|
173
438
|
def to_option(self) -> Option:
|
|
@@ -176,16 +441,31 @@ class ManifestOption(BaseModel):
|
|
|
176
441
|
|
|
177
442
|
Returns
|
|
178
443
|
-------
|
|
179
|
-
Option
|
|
444
|
+
nextmv.options.Option
|
|
180
445
|
The converted option.
|
|
446
|
+
|
|
447
|
+
Raises
|
|
448
|
+
------
|
|
449
|
+
ValueError
|
|
450
|
+
If the `self.option_type` is unknown.
|
|
451
|
+
|
|
452
|
+
Examples
|
|
453
|
+
--------
|
|
454
|
+
>>> from nextmv.cloud import ManifestOption
|
|
455
|
+
>>> manifest_opt = ManifestOption(name="max_stops", option_type="int", default=100)
|
|
456
|
+
>>> sdk_option = manifest_opt.to_option()
|
|
457
|
+
>>> sdk_option.name
|
|
458
|
+
'max_stops'
|
|
459
|
+
>>> sdk_option.option_type
|
|
460
|
+
<class 'int'>
|
|
181
461
|
"""
|
|
182
462
|
|
|
183
463
|
option_type_string = self.option_type
|
|
184
464
|
if option_type_string == "string":
|
|
185
465
|
option_type = str
|
|
186
|
-
elif option_type_string == "
|
|
466
|
+
elif option_type_string == "bool":
|
|
187
467
|
option_type = bool
|
|
188
|
-
elif option_type_string == "
|
|
468
|
+
elif option_type_string == "int":
|
|
189
469
|
option_type = int
|
|
190
470
|
elif option_type_string == "float":
|
|
191
471
|
option_type = float
|
|
@@ -198,62 +478,190 @@ class ManifestOption(BaseModel):
|
|
|
198
478
|
default=self.default,
|
|
199
479
|
description=self.description,
|
|
200
480
|
required=self.required,
|
|
201
|
-
|
|
481
|
+
additional_attributes=self.additional_attributes,
|
|
202
482
|
)
|
|
203
483
|
|
|
204
484
|
|
|
485
|
+
class ManifestOptions(BaseModel):
|
|
486
|
+
"""
|
|
487
|
+
Options for the decision model.
|
|
488
|
+
|
|
489
|
+
You can import the `ManifestOptions` class directly from `cloud`:
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
from nextmv.cloud import ManifestOptions
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Parameters
|
|
496
|
+
----------
|
|
497
|
+
strict : Optional[bool], default=False
|
|
498
|
+
If strict is set to `True`, only the listed options will be allowed.
|
|
499
|
+
items : Optional[list[ManifestOption]], default=None
|
|
500
|
+
The actual list of options for the decision model. An option
|
|
501
|
+
is a parameter that configures the decision model.
|
|
502
|
+
|
|
503
|
+
Examples
|
|
504
|
+
--------
|
|
505
|
+
>>> from nextmv.cloud import ManifestOptions, ManifestOption
|
|
506
|
+
>>> options_config = ManifestOptions(
|
|
507
|
+
... strict=True,
|
|
508
|
+
... items=[
|
|
509
|
+
... ManifestOption(name="timeout", option_type="int", default=60),
|
|
510
|
+
... ManifestOption(name="vehicle_capacity", option_type="float", default=100.0)
|
|
511
|
+
... ]
|
|
512
|
+
... )
|
|
513
|
+
>>> options_config.strict
|
|
514
|
+
True
|
|
515
|
+
>>> len(options_config.items)
|
|
516
|
+
2
|
|
517
|
+
"""
|
|
518
|
+
|
|
519
|
+
strict: Optional[bool] = False
|
|
520
|
+
"""If strict is set to `True`, only the listed options will be allowed."""
|
|
521
|
+
items: Optional[list[ManifestOption]] = None
|
|
522
|
+
"""The actual list of options for the decision model.
|
|
523
|
+
|
|
524
|
+
An option is a parameter that configures the decision model.
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
class ManifestConfiguration(BaseModel):
|
|
529
|
+
"""
|
|
530
|
+
Configuration for the decision model.
|
|
531
|
+
|
|
532
|
+
You can import the `ManifestConfiguration` class directly from `cloud`:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
from nextmv.cloud import ManifestConfiguration
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Parameters
|
|
539
|
+
----------
|
|
540
|
+
options : ManifestOptions
|
|
541
|
+
Options for the decision model.
|
|
542
|
+
|
|
543
|
+
Examples
|
|
544
|
+
--------
|
|
545
|
+
>>> from nextmv.cloud import ManifestConfiguration, ManifestOptions, ManifestOption
|
|
546
|
+
>>> model_config = ManifestConfiguration(
|
|
547
|
+
... options=ManifestOptions(
|
|
548
|
+
... items=[ManifestOption(name="debug_mode", option_type="bool", default=False)]
|
|
549
|
+
... )
|
|
550
|
+
... )
|
|
551
|
+
>>> model_config.options.items[0].name
|
|
552
|
+
'debug_mode'
|
|
553
|
+
"""
|
|
554
|
+
|
|
555
|
+
options: ManifestOptions
|
|
556
|
+
"""Options for the decision model."""
|
|
557
|
+
|
|
558
|
+
|
|
205
559
|
class Manifest(BaseModel):
|
|
206
560
|
"""
|
|
561
|
+
Represents an app manifest (`app.yaml`) for Nextmv Cloud.
|
|
562
|
+
|
|
563
|
+
You can import the `Manifest` class directly from `cloud`:
|
|
564
|
+
|
|
565
|
+
```python
|
|
566
|
+
from nextmv.cloud import Manifest
|
|
567
|
+
```
|
|
568
|
+
|
|
207
569
|
An application that runs on the Nextmv Platform must contain a file named
|
|
208
570
|
`app.yaml` which is known as the app manifest. This file is used to specify
|
|
209
571
|
the execution environment for the app.
|
|
210
572
|
|
|
211
573
|
This class represents the app manifest and allows you to load it from a
|
|
212
574
|
file or create it programmatically.
|
|
575
|
+
|
|
576
|
+
Parameters
|
|
577
|
+
----------
|
|
578
|
+
files : list[str]
|
|
579
|
+
The files to include (or exclude) in the app. This is mandatory.
|
|
580
|
+
runtime : ManifestRuntime, default=ManifestRuntime.PYTHON
|
|
581
|
+
The runtime to use for the app, it provides the environment
|
|
582
|
+
in which the app runs. This is mandatory.
|
|
583
|
+
type : ManifestType, default=ManifestType.PYTHON
|
|
584
|
+
Type of application, based on the programming language. This is
|
|
585
|
+
mandatory.
|
|
586
|
+
build : Optional[ManifestBuild], default=None
|
|
587
|
+
Build-specific attributes. The `build.command` to run to build
|
|
588
|
+
the app. This command will be executed without a shell, i.e., directly.
|
|
589
|
+
The command must exit with a status of 0 to continue the push process of
|
|
590
|
+
the app to Nextmv Cloud. This command is executed prior to the pre-push
|
|
591
|
+
command. The `build.environment` is used to set environment variables when
|
|
592
|
+
running the build command given as key-value pairs.
|
|
593
|
+
pre_push : Optional[str], default=None
|
|
594
|
+
A command to run before the app is pushed to the Nextmv Cloud.
|
|
595
|
+
This command can be used to compile a binary, run tests or similar tasks.
|
|
596
|
+
One difference with what is specified under build, is that the command
|
|
597
|
+
will be executed via the shell (i.e., `bash -c` on Linux & macOS or
|
|
598
|
+
`cmd /c` on Windows). The command must exit with a status of 0 to
|
|
599
|
+
continue the push process. This command is executed just before the app
|
|
600
|
+
gets bundled and pushed (after the build command).
|
|
601
|
+
Aliases: `pre-push`.
|
|
602
|
+
python : Optional[ManifestPython], default=None
|
|
603
|
+
Only for Python apps. Contains further Python-specific
|
|
604
|
+
attributes.
|
|
605
|
+
configuration : Optional[ManifestConfiguration], default=None
|
|
606
|
+
A list of options for the decision model. An option is a
|
|
607
|
+
parameter that configures the decision model.
|
|
608
|
+
|
|
609
|
+
Examples
|
|
610
|
+
--------
|
|
611
|
+
>>> from nextmv.cloud import Manifest, ManifestRuntime, ManifestType
|
|
612
|
+
>>> manifest = Manifest(
|
|
613
|
+
... files=["main.py", "model_logic/"],
|
|
614
|
+
... runtime=ManifestRuntime.PYTHON,
|
|
615
|
+
... type=ManifestType.PYTHON,
|
|
616
|
+
... )
|
|
617
|
+
>>> manifest.files
|
|
618
|
+
['main.py', 'model_logic/']
|
|
213
619
|
"""
|
|
214
620
|
|
|
215
621
|
files: list[str]
|
|
216
|
-
"""
|
|
622
|
+
"""The files to include (or exclude) in the app. This is mandatory."""
|
|
217
623
|
|
|
218
624
|
runtime: ManifestRuntime = ManifestRuntime.PYTHON
|
|
219
|
-
"""
|
|
220
|
-
|
|
221
|
-
which the app runs.
|
|
625
|
+
"""The runtime to use for the app.
|
|
626
|
+
|
|
627
|
+
It provides the environment in which the app runs. This is mandatory.
|
|
222
628
|
"""
|
|
223
629
|
type: ManifestType = ManifestType.PYTHON
|
|
224
|
-
"""
|
|
630
|
+
"""Type of application, based on the programming language. This is mandatory."""
|
|
225
631
|
build: Optional[ManifestBuild] = None
|
|
226
|
-
"""
|
|
227
|
-
|
|
228
|
-
app. This command will be executed
|
|
229
|
-
command must exit with a status of 0
|
|
230
|
-
app to Nextmv Cloud. This command is
|
|
231
|
-
command. The build.environment is used to
|
|
232
|
-
running the build command given as key-value
|
|
632
|
+
"""Build-specific attributes.
|
|
633
|
+
|
|
634
|
+
The `build.command` to run to build the app. This command will be executed
|
|
635
|
+
without a shell, i.e., directly. The command must exit with a status of 0
|
|
636
|
+
to continue the push process of the app to Nextmv Cloud. This command is
|
|
637
|
+
executed prior to the pre-push command. The `build.environment` is used to
|
|
638
|
+
set environment variables when running the build command given as key-value
|
|
639
|
+
pairs.
|
|
233
640
|
"""
|
|
234
641
|
pre_push: Optional[str] = Field(
|
|
235
642
|
serialization_alias="pre-push",
|
|
236
643
|
validation_alias=AliasChoices("pre-push", "pre_push"),
|
|
237
644
|
default=None,
|
|
238
645
|
)
|
|
239
|
-
"""
|
|
240
|
-
|
|
646
|
+
"""A command to run before the app is pushed to the Nextmv Cloud.
|
|
647
|
+
|
|
241
648
|
This command can be used to compile a binary, run tests or similar tasks.
|
|
242
649
|
One difference with what is specified under build, is that the command will
|
|
243
|
-
be executed via the shell (i.e., bash -c on Linux & macOS or cmd /c on
|
|
650
|
+
be executed via the shell (i.e., `bash -c` on Linux & macOS or `cmd /c` on
|
|
244
651
|
Windows). The command must exit with a status of 0 to continue the push
|
|
245
652
|
process. This command is executed just before the app gets bundled and
|
|
246
653
|
pushed (after the build command).
|
|
247
654
|
"""
|
|
248
655
|
python: Optional[ManifestPython] = None
|
|
656
|
+
"""Python-specific attributes.
|
|
657
|
+
|
|
658
|
+
Only for Python apps. Contains further Python-specific attributes.
|
|
249
659
|
"""
|
|
250
|
-
Optional
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
Optional. A list of options for the decision model. An option is a
|
|
256
|
-
parameter that configures the decision model.
|
|
660
|
+
configuration: Optional[ManifestConfiguration] = None
|
|
661
|
+
"""Configuration for the decision model.
|
|
662
|
+
|
|
663
|
+
A list of options for the decision model. An option is a parameter that
|
|
664
|
+
configures the decision model.
|
|
257
665
|
"""
|
|
258
666
|
|
|
259
667
|
@classmethod
|
|
@@ -261,19 +669,44 @@ class Manifest(BaseModel):
|
|
|
261
669
|
"""
|
|
262
670
|
Load a manifest from a YAML file.
|
|
263
671
|
|
|
672
|
+
The YAML file is expected to be named `app.yaml` and located in the
|
|
673
|
+
specified directory.
|
|
674
|
+
|
|
264
675
|
Parameters
|
|
265
676
|
----------
|
|
266
|
-
dirpath: str
|
|
267
|
-
Path to the directory containing the app.yaml file.
|
|
677
|
+
dirpath : str
|
|
678
|
+
Path to the directory containing the `app.yaml` file.
|
|
268
679
|
|
|
269
680
|
Returns
|
|
270
681
|
-------
|
|
271
682
|
Manifest
|
|
272
683
|
The loaded manifest.
|
|
273
684
|
|
|
685
|
+
Raises
|
|
686
|
+
------
|
|
687
|
+
FileNotFoundError
|
|
688
|
+
If the `app.yaml` file is not found in `dirpath`.
|
|
689
|
+
yaml.YAMLError
|
|
690
|
+
If there is an error parsing the YAML file.
|
|
691
|
+
|
|
692
|
+
Examples
|
|
693
|
+
--------
|
|
694
|
+
Assuming an `app.yaml` file exists in `./my_app_dir`:
|
|
695
|
+
|
|
696
|
+
```yaml
|
|
697
|
+
# ./my_app_dir/app.yaml
|
|
698
|
+
files:
|
|
699
|
+
- main.py
|
|
700
|
+
runtime: ghcr.io/nextmv-io/runtime/python:3.11
|
|
701
|
+
type: python
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
>>> from nextmv.cloud import Manifest
|
|
705
|
+
>>> # manifest = Manifest.from_yaml("./my_app_dir") # This would be run
|
|
706
|
+
>>> # assert manifest.type == "python"
|
|
274
707
|
"""
|
|
275
708
|
|
|
276
|
-
with open(os.path.join(dirpath,
|
|
709
|
+
with open(os.path.join(dirpath, MANIFEST_FILE_NAME)) as file:
|
|
277
710
|
raw_manifest = yaml.safe_load(file)
|
|
278
711
|
|
|
279
712
|
return cls.from_dict(raw_manifest)
|
|
@@ -282,51 +715,119 @@ class Manifest(BaseModel):
|
|
|
282
715
|
"""
|
|
283
716
|
Write the manifest to a YAML file.
|
|
284
717
|
|
|
718
|
+
The manifest will be written to a file named `app.yaml` in the
|
|
719
|
+
specified directory.
|
|
720
|
+
|
|
285
721
|
Parameters
|
|
286
722
|
----------
|
|
287
|
-
dirpath: str
|
|
288
|
-
Path to the directory where the app.yaml file will be written.
|
|
289
|
-
|
|
723
|
+
dirpath : str
|
|
724
|
+
Path to the directory where the `app.yaml` file will be written.
|
|
725
|
+
|
|
726
|
+
Raises
|
|
727
|
+
------
|
|
728
|
+
IOError
|
|
729
|
+
If there is an error writing the file.
|
|
730
|
+
yaml.YAMLError
|
|
731
|
+
If there is an error serializing the manifest to YAML.
|
|
732
|
+
|
|
733
|
+
Examples
|
|
734
|
+
--------
|
|
735
|
+
>>> from nextmv.cloud import Manifest
|
|
736
|
+
>>> manifest = Manifest(files=["solver.py"], type="python")
|
|
737
|
+
>>> # manifest.to_yaml("./output_dir") # This would create ./output_dir/app.yaml
|
|
290
738
|
"""
|
|
291
739
|
|
|
292
|
-
with open(os.path.join(dirpath,
|
|
740
|
+
with open(os.path.join(dirpath, MANIFEST_FILE_NAME), "w") as file:
|
|
293
741
|
yaml.dump(self.to_dict(), file)
|
|
294
742
|
|
|
295
|
-
def extract_options(self) -> Options:
|
|
743
|
+
def extract_options(self) -> Optional[Options]:
|
|
296
744
|
"""
|
|
297
745
|
Convert the manifest options to a `nextmv.Options` object.
|
|
298
746
|
|
|
747
|
+
If the manifest does not have valid options defined in
|
|
748
|
+
`.configuration.options.items`, this method returns `None`.
|
|
749
|
+
|
|
299
750
|
Returns
|
|
300
751
|
-------
|
|
301
|
-
Options
|
|
302
|
-
The
|
|
752
|
+
Optional[nextmv.options.Options]
|
|
753
|
+
The options extracted from the manifest. If no options are found,
|
|
754
|
+
`None` is returned.
|
|
755
|
+
|
|
756
|
+
Examples
|
|
757
|
+
--------
|
|
758
|
+
>>> from nextmv.cloud import Manifest, ManifestConfiguration, ManifestOptions, ManifestOption
|
|
759
|
+
>>> manifest = Manifest(
|
|
760
|
+
... files=["main.py"],
|
|
761
|
+
... configuration=ManifestConfiguration(
|
|
762
|
+
... options=ManifestOptions(
|
|
763
|
+
... items=[
|
|
764
|
+
... ManifestOption(name="duration", option_type="string", default="10s")
|
|
765
|
+
... ]
|
|
766
|
+
... )
|
|
767
|
+
... )
|
|
768
|
+
... )
|
|
769
|
+
>>> sdk_options = manifest.extract_options()
|
|
770
|
+
>>> sdk_options.get_option("duration").default
|
|
771
|
+
'10s'
|
|
772
|
+
>>> empty_manifest = Manifest(files=["main.py"])
|
|
773
|
+
>>> empty_manifest.extract_options() is None
|
|
774
|
+
True
|
|
303
775
|
"""
|
|
304
776
|
|
|
305
|
-
if self.options is None:
|
|
306
|
-
|
|
777
|
+
if self.configuration is None or self.configuration.options is None or self.configuration.options.items is None:
|
|
778
|
+
return None
|
|
307
779
|
|
|
308
|
-
options = [option.to_option() for option in self.options]
|
|
780
|
+
options = [option.to_option() for option in self.configuration.options.items]
|
|
309
781
|
|
|
310
782
|
return Options(*options)
|
|
311
783
|
|
|
312
784
|
@classmethod
|
|
313
|
-
def from_model_configuration(
|
|
785
|
+
def from_model_configuration(
|
|
786
|
+
cls,
|
|
787
|
+
model_configuration: ModelConfiguration,
|
|
788
|
+
) -> "Manifest":
|
|
314
789
|
"""
|
|
315
|
-
Create a Python manifest from a
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
`nextmv.Model
|
|
319
|
-
|
|
790
|
+
Create a Python manifest from a `nextmv.model.ModelConfiguration`.
|
|
791
|
+
|
|
792
|
+
Note that the `ModelConfiguration` is almost always used in
|
|
793
|
+
conjunction with the `nextmv.Model` class. If you are not
|
|
794
|
+
implementing an instance of `nextmv.Model`, consider using the
|
|
795
|
+
`from_options` method instead to initialize the manifest with the
|
|
796
|
+
options of the model.
|
|
797
|
+
|
|
798
|
+
The resulting manifest will have:
|
|
799
|
+
|
|
800
|
+
- `files` set to `["main.py", f"{model_configuration.name}/**"]`
|
|
801
|
+
- `runtime` set to `ManifestRuntime.PYTHON`
|
|
802
|
+
- `type` set to `ManifestType.PYTHON`
|
|
803
|
+
- `python.pip_requirements` set to the default requirements file name.
|
|
804
|
+
- `python.model.name` set to `model_configuration.name`.
|
|
805
|
+
- `python.model.options` populated from `model_configuration.options`.
|
|
806
|
+
- `configuration.options` populated from `model_configuration.options`.
|
|
320
807
|
|
|
321
808
|
Parameters
|
|
322
809
|
----------
|
|
323
|
-
model_configuration: ModelConfiguration
|
|
810
|
+
model_configuration : nextmv.model.ModelConfiguration
|
|
324
811
|
The model configuration.
|
|
325
812
|
|
|
326
813
|
Returns
|
|
327
814
|
-------
|
|
328
815
|
Manifest
|
|
329
816
|
The Python manifest.
|
|
817
|
+
|
|
818
|
+
Examples
|
|
819
|
+
--------
|
|
820
|
+
>>> from nextmv.model import ModelConfiguration, Options, Option
|
|
821
|
+
>>> from nextmv.cloud import Manifest
|
|
822
|
+
>>> opts = Options(Option(name="vehicle_count", option_type=int, default=5))
|
|
823
|
+
>>> mc = ModelConfiguration(name="vehicle_router", options=opts)
|
|
824
|
+
>>> manifest = Manifest.from_model_configuration(mc)
|
|
825
|
+
>>> manifest.python.model.name
|
|
826
|
+
'vehicle_router'
|
|
827
|
+
>>> manifest.files
|
|
828
|
+
['main.py', 'vehicle_router/**']
|
|
829
|
+
>>> manifest.configuration.options.items[0].name
|
|
830
|
+
'vehicle_count'
|
|
330
831
|
"""
|
|
331
832
|
|
|
332
833
|
manifest_python_dict = {
|
|
@@ -348,28 +849,60 @@ class Manifest(BaseModel):
|
|
|
348
849
|
)
|
|
349
850
|
|
|
350
851
|
if model_configuration.options is not None:
|
|
351
|
-
manifest.
|
|
852
|
+
manifest.configuration = ManifestConfiguration(
|
|
853
|
+
options=ManifestOptions(
|
|
854
|
+
strict=False,
|
|
855
|
+
items=[ManifestOption.from_option(opt) for opt in model_configuration.options.options],
|
|
856
|
+
),
|
|
857
|
+
)
|
|
352
858
|
|
|
353
859
|
return manifest
|
|
354
860
|
|
|
355
861
|
@classmethod
|
|
356
862
|
def from_options(cls, options: Options) -> "Manifest":
|
|
357
863
|
"""
|
|
358
|
-
Create a basic Python manifest from `Options`.
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
864
|
+
Create a basic Python manifest from `nextmv.options.Options`.
|
|
865
|
+
|
|
866
|
+
If you have more files than just a `main.py`, make sure you modify
|
|
867
|
+
the `.files` attribute of the resulting manifest. This method assumes
|
|
868
|
+
that requirements are specified in a `requirements.txt` file. You may
|
|
869
|
+
also specify a different requirements file once you instantiate the
|
|
870
|
+
manifest.
|
|
871
|
+
|
|
872
|
+
The resulting manifest will have:
|
|
873
|
+
- `files` set to `["main.py"]`
|
|
874
|
+
- `runtime` set to `ManifestRuntime.PYTHON`
|
|
875
|
+
- `type` set to `ManifestType.PYTHON`
|
|
876
|
+
- `python.pip_requirements` set to `"requirements.txt"`.
|
|
877
|
+
- `configuration.options` populated from the provided `options`.
|
|
363
878
|
|
|
364
879
|
Parameters
|
|
365
880
|
----------
|
|
366
|
-
options: Options
|
|
881
|
+
options : nextmv.options.Options
|
|
367
882
|
The options to include in the manifest.
|
|
368
883
|
|
|
369
884
|
Returns
|
|
370
885
|
-------
|
|
371
886
|
Manifest
|
|
372
887
|
The manifest with the given options.
|
|
888
|
+
|
|
889
|
+
Examples
|
|
890
|
+
--------
|
|
891
|
+
>>> from nextmv.options import Options, Option
|
|
892
|
+
>>> from nextmv.cloud import Manifest
|
|
893
|
+
>>> opts = Options(
|
|
894
|
+
... Option(name="max_runtime", option_type=str, default="60s"),
|
|
895
|
+
... Option(name="use_heuristic", option_type=bool, default=True)
|
|
896
|
+
... )
|
|
897
|
+
>>> manifest = Manifest.from_options(opts)
|
|
898
|
+
>>> manifest.files
|
|
899
|
+
['main.py']
|
|
900
|
+
>>> manifest.python.pip_requirements
|
|
901
|
+
'requirements.txt'
|
|
902
|
+
>>> len(manifest.configuration.options.items)
|
|
903
|
+
2
|
|
904
|
+
>>> manifest.configuration.options.items[0].name
|
|
905
|
+
'max_runtime'
|
|
373
906
|
"""
|
|
374
907
|
|
|
375
908
|
manifest = cls(
|
|
@@ -377,7 +910,12 @@ class Manifest(BaseModel):
|
|
|
377
910
|
runtime=ManifestRuntime.PYTHON,
|
|
378
911
|
type=ManifestType.PYTHON,
|
|
379
912
|
python=ManifestPython(pip_requirements="requirements.txt"),
|
|
380
|
-
|
|
913
|
+
configuration=ManifestConfiguration(
|
|
914
|
+
options=ManifestOptions(
|
|
915
|
+
strict=False,
|
|
916
|
+
items=[ManifestOption.from_option(opt) for opt in options.options],
|
|
917
|
+
),
|
|
918
|
+
),
|
|
381
919
|
)
|
|
382
920
|
|
|
383
921
|
return manifest
|