nextmv 0.18.0__py3-none-any.whl → 1.0.0.dev2__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 +8 -13
- nextmv/__init__.py +53 -0
- nextmv/_serialization.py +96 -0
- nextmv/base_model.py +54 -9
- nextmv/cli/CONTRIBUTING.md +511 -0
- nextmv/cli/__init__.py +0 -0
- nextmv/cli/cloud/__init__.py +47 -0
- nextmv/cli/cloud/acceptance/__init__.py +27 -0
- nextmv/cli/cloud/acceptance/create.py +393 -0
- nextmv/cli/cloud/acceptance/delete.py +68 -0
- nextmv/cli/cloud/acceptance/get.py +104 -0
- nextmv/cli/cloud/acceptance/list.py +62 -0
- nextmv/cli/cloud/acceptance/update.py +95 -0
- nextmv/cli/cloud/account/__init__.py +28 -0
- nextmv/cli/cloud/account/create.py +83 -0
- nextmv/cli/cloud/account/delete.py +60 -0
- nextmv/cli/cloud/account/get.py +66 -0
- nextmv/cli/cloud/account/update.py +70 -0
- nextmv/cli/cloud/app/__init__.py +35 -0
- nextmv/cli/cloud/app/create.py +141 -0
- nextmv/cli/cloud/app/delete.py +58 -0
- nextmv/cli/cloud/app/exists.py +44 -0
- nextmv/cli/cloud/app/get.py +66 -0
- nextmv/cli/cloud/app/list.py +61 -0
- nextmv/cli/cloud/app/push.py +137 -0
- nextmv/cli/cloud/app/update.py +124 -0
- nextmv/cli/cloud/batch/__init__.py +29 -0
- nextmv/cli/cloud/batch/create.py +454 -0
- nextmv/cli/cloud/batch/delete.py +68 -0
- nextmv/cli/cloud/batch/get.py +104 -0
- nextmv/cli/cloud/batch/list.py +63 -0
- nextmv/cli/cloud/batch/metadata.py +66 -0
- nextmv/cli/cloud/batch/update.py +95 -0
- nextmv/cli/cloud/data/__init__.py +26 -0
- nextmv/cli/cloud/data/upload.py +162 -0
- nextmv/cli/cloud/ensemble/__init__.py +31 -0
- nextmv/cli/cloud/ensemble/create.py +414 -0
- nextmv/cli/cloud/ensemble/delete.py +67 -0
- nextmv/cli/cloud/ensemble/get.py +65 -0
- nextmv/cli/cloud/ensemble/update.py +103 -0
- nextmv/cli/cloud/input_set/__init__.py +30 -0
- nextmv/cli/cloud/input_set/create.py +170 -0
- nextmv/cli/cloud/input_set/get.py +63 -0
- nextmv/cli/cloud/input_set/list.py +63 -0
- nextmv/cli/cloud/input_set/update.py +123 -0
- nextmv/cli/cloud/instance/__init__.py +35 -0
- nextmv/cli/cloud/instance/create.py +290 -0
- nextmv/cli/cloud/instance/delete.py +62 -0
- nextmv/cli/cloud/instance/exists.py +39 -0
- nextmv/cli/cloud/instance/get.py +62 -0
- nextmv/cli/cloud/instance/list.py +60 -0
- nextmv/cli/cloud/instance/update.py +216 -0
- nextmv/cli/cloud/managed_input/__init__.py +31 -0
- nextmv/cli/cloud/managed_input/create.py +146 -0
- nextmv/cli/cloud/managed_input/delete.py +65 -0
- nextmv/cli/cloud/managed_input/get.py +63 -0
- nextmv/cli/cloud/managed_input/list.py +60 -0
- nextmv/cli/cloud/managed_input/update.py +97 -0
- nextmv/cli/cloud/run/__init__.py +37 -0
- nextmv/cli/cloud/run/cancel.py +37 -0
- nextmv/cli/cloud/run/create.py +530 -0
- nextmv/cli/cloud/run/get.py +199 -0
- nextmv/cli/cloud/run/input.py +86 -0
- nextmv/cli/cloud/run/list.py +80 -0
- nextmv/cli/cloud/run/logs.py +167 -0
- nextmv/cli/cloud/run/metadata.py +67 -0
- nextmv/cli/cloud/run/track.py +501 -0
- nextmv/cli/cloud/scenario/__init__.py +29 -0
- nextmv/cli/cloud/scenario/create.py +451 -0
- nextmv/cli/cloud/scenario/delete.py +65 -0
- nextmv/cli/cloud/scenario/get.py +102 -0
- nextmv/cli/cloud/scenario/list.py +63 -0
- nextmv/cli/cloud/scenario/metadata.py +67 -0
- nextmv/cli/cloud/scenario/update.py +93 -0
- nextmv/cli/cloud/secrets/__init__.py +33 -0
- nextmv/cli/cloud/secrets/create.py +206 -0
- nextmv/cli/cloud/secrets/delete.py +67 -0
- nextmv/cli/cloud/secrets/get.py +66 -0
- nextmv/cli/cloud/secrets/list.py +60 -0
- nextmv/cli/cloud/secrets/update.py +147 -0
- nextmv/cli/cloud/shadow/__init__.py +33 -0
- nextmv/cli/cloud/shadow/create.py +184 -0
- nextmv/cli/cloud/shadow/delete.py +68 -0
- nextmv/cli/cloud/shadow/get.py +61 -0
- nextmv/cli/cloud/shadow/list.py +63 -0
- nextmv/cli/cloud/shadow/metadata.py +66 -0
- nextmv/cli/cloud/shadow/start.py +43 -0
- nextmv/cli/cloud/shadow/stop.py +43 -0
- nextmv/cli/cloud/shadow/update.py +95 -0
- nextmv/cli/cloud/upload/__init__.py +22 -0
- nextmv/cli/cloud/upload/create.py +39 -0
- nextmv/cli/cloud/version/__init__.py +33 -0
- nextmv/cli/cloud/version/create.py +97 -0
- nextmv/cli/cloud/version/delete.py +62 -0
- nextmv/cli/cloud/version/exists.py +39 -0
- nextmv/cli/cloud/version/get.py +62 -0
- nextmv/cli/cloud/version/list.py +60 -0
- nextmv/cli/cloud/version/update.py +92 -0
- nextmv/cli/community/__init__.py +24 -0
- nextmv/cli/community/clone.py +270 -0
- nextmv/cli/community/list.py +265 -0
- nextmv/cli/configuration/__init__.py +23 -0
- nextmv/cli/configuration/config.py +195 -0
- nextmv/cli/configuration/create.py +94 -0
- nextmv/cli/configuration/delete.py +67 -0
- nextmv/cli/configuration/list.py +77 -0
- nextmv/cli/main.py +188 -0
- nextmv/cli/message.py +153 -0
- nextmv/cli/options.py +206 -0
- nextmv/cli/version.py +38 -0
- nextmv/cloud/__init__.py +71 -17
- nextmv/cloud/acceptance_test.py +757 -51
- nextmv/cloud/account.py +406 -17
- nextmv/cloud/application/__init__.py +957 -0
- nextmv/cloud/application/_acceptance.py +419 -0
- nextmv/cloud/application/_batch_scenario.py +860 -0
- nextmv/cloud/application/_ensemble.py +251 -0
- nextmv/cloud/application/_input_set.py +227 -0
- nextmv/cloud/application/_instance.py +289 -0
- nextmv/cloud/application/_managed_input.py +227 -0
- nextmv/cloud/application/_run.py +1393 -0
- nextmv/cloud/application/_secrets.py +294 -0
- nextmv/cloud/application/_shadow.py +314 -0
- nextmv/cloud/application/_utils.py +54 -0
- nextmv/cloud/application/_version.py +303 -0
- nextmv/cloud/assets.py +48 -0
- nextmv/cloud/batch_experiment.py +294 -33
- nextmv/cloud/client.py +307 -66
- nextmv/cloud/ensemble.py +247 -0
- nextmv/cloud/input_set.py +120 -2
- nextmv/cloud/instance.py +133 -8
- nextmv/cloud/integration.py +533 -0
- nextmv/cloud/package.py +168 -53
- nextmv/cloud/scenario.py +410 -0
- nextmv/cloud/secrets.py +234 -0
- nextmv/cloud/shadow.py +190 -0
- nextmv/cloud/url.py +73 -0
- nextmv/cloud/version.py +132 -4
- nextmv/default_app/.gitignore +1 -0
- nextmv/default_app/README.md +32 -0
- nextmv/default_app/app.yaml +12 -0
- nextmv/default_app/input.json +5 -0
- nextmv/default_app/main.py +37 -0
- nextmv/default_app/requirements.txt +2 -0
- nextmv/default_app/src/__init__.py +0 -0
- nextmv/default_app/src/visuals.py +36 -0
- nextmv/deprecated.py +47 -0
- nextmv/input.py +861 -90
- nextmv/local/__init__.py +5 -0
- nextmv/local/application.py +1251 -0
- nextmv/local/executor.py +1042 -0
- nextmv/local/geojson_handler.py +323 -0
- nextmv/local/local.py +97 -0
- nextmv/local/plotly_handler.py +61 -0
- nextmv/local/runner.py +274 -0
- nextmv/logger.py +80 -9
- nextmv/manifest.py +1466 -0
- nextmv/model.py +241 -66
- nextmv/options.py +708 -115
- nextmv/output.py +1301 -274
- nextmv/polling.py +325 -0
- nextmv/run.py +1702 -0
- nextmv/safe.py +145 -0
- nextmv/status.py +122 -0
- nextmv-1.0.0.dev2.dist-info/METADATA +311 -0
- nextmv-1.0.0.dev2.dist-info/RECORD +170 -0
- {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/WHEEL +1 -1
- nextmv-1.0.0.dev2.dist-info/entry_points.txt +2 -0
- nextmv/cloud/application.py +0 -1405
- nextmv/cloud/manifest.py +0 -234
- nextmv/cloud/status.py +0 -29
- nextmv-0.18.0.dist-info/METADATA +0 -770
- nextmv-0.18.0.dist-info/RECORD +0 -25
- {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
nextmv/manifest.py
ADDED
|
@@ -0,0 +1,1466 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module with the logic for handling an app manifest.
|
|
3
|
+
|
|
4
|
+
This module provides classes and functions for managing Nextmv app manifests.
|
|
5
|
+
Manifest files (app.yaml) define how an application is built, run, and deployed
|
|
6
|
+
on the Nextmv platform.
|
|
7
|
+
|
|
8
|
+
Classes
|
|
9
|
+
-------
|
|
10
|
+
ManifestType
|
|
11
|
+
Enum for application types based on programming language.
|
|
12
|
+
ManifestRuntime
|
|
13
|
+
Enum for runtime environments where apps run on Nextmv.
|
|
14
|
+
ManifestPythonArch
|
|
15
|
+
Enum for target architecture for bundling Python apps.
|
|
16
|
+
ManifestBuild
|
|
17
|
+
Class for build-specific attributes in the manifest.
|
|
18
|
+
ManifestPythonModel
|
|
19
|
+
Class for model-specific instructions for Python apps.
|
|
20
|
+
ManifestPython
|
|
21
|
+
Class for Python-specific instructions in the manifest.
|
|
22
|
+
ManifestOptionUI
|
|
23
|
+
Class for UI attributes of options in the manifest.
|
|
24
|
+
ManifestOption
|
|
25
|
+
Class representing an option for the decision model in the manifest.
|
|
26
|
+
ManifestOptions
|
|
27
|
+
Class containing a list of options for the decision model.
|
|
28
|
+
ManifestValidation
|
|
29
|
+
Class for validation rules for options in the manifest.
|
|
30
|
+
ManifestContentMultiFileInput
|
|
31
|
+
Class for multi-file content format input configuration.
|
|
32
|
+
ManifestContentMultiFileOutput
|
|
33
|
+
Class for multi-file content format output configuration.
|
|
34
|
+
ManifestContentMultiFile
|
|
35
|
+
Class for multi-file content format configuration.
|
|
36
|
+
ManifestContent
|
|
37
|
+
Class for content configuration specifying how app input/output is handled.
|
|
38
|
+
ManifestConfiguration
|
|
39
|
+
Class for configuration settings for the decision model.
|
|
40
|
+
Manifest
|
|
41
|
+
Main class representing an app manifest for Nextmv.
|
|
42
|
+
|
|
43
|
+
Functions
|
|
44
|
+
---------
|
|
45
|
+
default_python_manifest
|
|
46
|
+
Creates a default Python manifest as a starting point for applications.
|
|
47
|
+
|
|
48
|
+
Constants
|
|
49
|
+
--------
|
|
50
|
+
MANIFEST_FILE_NAME
|
|
51
|
+
Name of the app manifest file.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
import os
|
|
55
|
+
from enum import Enum
|
|
56
|
+
from typing import Any
|
|
57
|
+
|
|
58
|
+
import yaml
|
|
59
|
+
from pydantic import AliasChoices, Field, field_validator
|
|
60
|
+
|
|
61
|
+
from nextmv.base_model import BaseModel
|
|
62
|
+
from nextmv.input import InputFormat
|
|
63
|
+
from nextmv.model import _REQUIREMENTS_FILE, ModelConfiguration
|
|
64
|
+
from nextmv.options import Option, Options, OptionsEnforcement
|
|
65
|
+
|
|
66
|
+
MANIFEST_FILE_NAME = "app.yaml"
|
|
67
|
+
"""Name of the app manifest file.
|
|
68
|
+
|
|
69
|
+
This constant defines the standard filename for Nextmv app manifest files.
|
|
70
|
+
|
|
71
|
+
You can import the `MANIFEST_FILE_NAME` constant directly from `nextmv`:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from nextmv import MANIFEST_FILE_NAME
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Notes
|
|
78
|
+
-----
|
|
79
|
+
All Nextmv applications must include an app.yaml file for proper deployment.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ManifestType(str, Enum):
|
|
84
|
+
"""
|
|
85
|
+
Type of application in the manifest, based on the programming language.
|
|
86
|
+
|
|
87
|
+
You can import the `ManifestType` class directly from `nextmv`:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from nextmv import ManifestType
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This enum defines the supported programming languages for applications
|
|
94
|
+
that can be deployed on Nextmv Cloud.
|
|
95
|
+
|
|
96
|
+
Attributes
|
|
97
|
+
----------
|
|
98
|
+
PYTHON : str
|
|
99
|
+
Python format, used for Python applications.
|
|
100
|
+
GO : str
|
|
101
|
+
Go format, used for Go applications.
|
|
102
|
+
JAVA : str
|
|
103
|
+
Java format, used for Java applications.
|
|
104
|
+
|
|
105
|
+
Examples
|
|
106
|
+
--------
|
|
107
|
+
>>> from nextmv import ManifestType
|
|
108
|
+
>>> manifest_type = ManifestType.PYTHON
|
|
109
|
+
>>> manifest_type
|
|
110
|
+
<ManifestType.PYTHON: 'python'>
|
|
111
|
+
>>> str(manifest_type)
|
|
112
|
+
'python'
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
PYTHON = "python"
|
|
116
|
+
"""Python format"""
|
|
117
|
+
GO = "go"
|
|
118
|
+
"""Go format"""
|
|
119
|
+
JAVA = "java"
|
|
120
|
+
"""Java format"""
|
|
121
|
+
BINARY = "binary"
|
|
122
|
+
"""Binary format"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ManifestRuntime(str, Enum):
|
|
126
|
+
"""
|
|
127
|
+
Runtime (environment) where the app will be run on Nextmv Cloud.
|
|
128
|
+
|
|
129
|
+
You can import the `ManifestRuntime` class directly from `nextmv`:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from nextmv import ManifestRuntime
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This enum defines the supported runtime environments for applications
|
|
136
|
+
that can be deployed on Nextmv Cloud.
|
|
137
|
+
|
|
138
|
+
Attributes
|
|
139
|
+
----------
|
|
140
|
+
DEFAULT : str
|
|
141
|
+
This runtime is used to run compiled applications such as Go binaries.
|
|
142
|
+
PYTHON : str
|
|
143
|
+
This runtime is used as the basis for all other Python runtimes and
|
|
144
|
+
Python applications.
|
|
145
|
+
JAVA : str
|
|
146
|
+
This runtime is used to run Java applications.
|
|
147
|
+
PYOMO : str
|
|
148
|
+
This runtime provisions Python packages to run Pyomo applications.
|
|
149
|
+
HEXALY : str
|
|
150
|
+
This runtime provisions Python packages to run Hexaly applications.
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
>>> from nextmv import ManifestRuntime
|
|
155
|
+
>>> runtime = ManifestRuntime.PYTHON
|
|
156
|
+
>>> runtime
|
|
157
|
+
<ManifestRuntime.PYTHON: 'ghcr.io/nextmv-io/runtime/python:3.11'>
|
|
158
|
+
>>> str(runtime)
|
|
159
|
+
'ghcr.io/nextmv-io/runtime/python:3.11'
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
DEFAULT = "ghcr.io/nextmv-io/runtime/default:latest"
|
|
163
|
+
"""A runtime used to run compiled applications such as Go binaries."""
|
|
164
|
+
PYTHON = "ghcr.io/nextmv-io/runtime/python:3.11"
|
|
165
|
+
"""A runtime used to run standard Python applications."""
|
|
166
|
+
JAVA = "ghcr.io/nextmv-io/runtime/java:latest"
|
|
167
|
+
"""A runtime used to run Java applications."""
|
|
168
|
+
PYOMO = "ghcr.io/nextmv-io/runtime/pyomo:latest"
|
|
169
|
+
"""A runtime provisioning Python packages to run Pyomo applications."""
|
|
170
|
+
CUOPT = "ghcr.io/nextmv-io/runtime/cuopt:latest"
|
|
171
|
+
"""A runtime providing the NVIDIA cuOpt solver."""
|
|
172
|
+
GAMSPY = "ghcr.io/nextmv-io/runtime/gamspy:latest"
|
|
173
|
+
"""A runtime provisioning the GAMS Python API and supported solvers."""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ManifestPythonArch(str, Enum):
|
|
177
|
+
"""
|
|
178
|
+
Target architecture for bundling Python apps.
|
|
179
|
+
|
|
180
|
+
You can import the `ManifestPythonArch` class directly from `nextmv`:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from nextmv import ManifestPythonArch
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Attributes
|
|
187
|
+
----------
|
|
188
|
+
ARM64 : str
|
|
189
|
+
ARM 64-bit architecture.
|
|
190
|
+
AMD64 : str
|
|
191
|
+
AMD 64-bit architecture.
|
|
192
|
+
|
|
193
|
+
Examples
|
|
194
|
+
--------
|
|
195
|
+
>>> from nextmv import ManifestPythonArch
|
|
196
|
+
>>> arch = ManifestPythonArch.ARM64
|
|
197
|
+
>>> arch
|
|
198
|
+
<ManifestPythonArch.ARM64: 'arm64'>
|
|
199
|
+
>>> str(arch)
|
|
200
|
+
'arm64'
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
ARM64 = "arm64"
|
|
204
|
+
"""ARM 64-bit architecture."""
|
|
205
|
+
AMD64 = "amd64"
|
|
206
|
+
"""AMD 64-bit architecture."""
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ManifestBuild(BaseModel):
|
|
210
|
+
"""
|
|
211
|
+
Build-specific attributes.
|
|
212
|
+
|
|
213
|
+
You can import the `ManifestBuild` class directly from `nextmv`:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from nextmv import ManifestBuild
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
command : Optional[str], default=None
|
|
222
|
+
The command to run to build the app. This command will be executed
|
|
223
|
+
without a shell, i.e., directly. The command must exit with a status of
|
|
224
|
+
0 to continue the push process of the app to Nextmv Cloud. This command
|
|
225
|
+
is executed prior to the pre-push command.
|
|
226
|
+
environment : Optional[dict[str, Any]], default=None
|
|
227
|
+
Environment variables to set when running the build command given as
|
|
228
|
+
key-value pairs.
|
|
229
|
+
|
|
230
|
+
Examples
|
|
231
|
+
--------
|
|
232
|
+
>>> from nextmv import ManifestBuild
|
|
233
|
+
>>> build_config = ManifestBuild(
|
|
234
|
+
... command="make build",
|
|
235
|
+
... environment={"DEBUG": "true"}
|
|
236
|
+
... )
|
|
237
|
+
>>> build_config.command
|
|
238
|
+
'make build'
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
command: str | None = None
|
|
242
|
+
"""The command to run to build the app.
|
|
243
|
+
|
|
244
|
+
This command will be executed without a shell, i.e., directly. The command
|
|
245
|
+
must exit with a status of 0 to continue the push process of the app to
|
|
246
|
+
Nextmv Cloud. This command is executed prior to the pre-push command.
|
|
247
|
+
"""
|
|
248
|
+
environment: dict[str, Any] | None = None
|
|
249
|
+
"""Environment variables to set when running the build command.
|
|
250
|
+
|
|
251
|
+
Given as key-value pairs.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
def environment_to_dict(self) -> dict[str, str]:
|
|
255
|
+
"""
|
|
256
|
+
Convert the environment variables to a dictionary.
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
dict[str, str]
|
|
261
|
+
The environment variables as a dictionary of string key-value pairs.
|
|
262
|
+
Returns an empty dictionary if no environment variables are set.
|
|
263
|
+
|
|
264
|
+
Examples
|
|
265
|
+
--------
|
|
266
|
+
>>> from nextmv import ManifestBuild
|
|
267
|
+
>>> build_config = ManifestBuild(environment={"COUNT": 1, "NAME": "test"})
|
|
268
|
+
>>> build_config.environment_to_dict()
|
|
269
|
+
{'COUNT': '1', 'NAME': 'test'}
|
|
270
|
+
>>> build_config_empty = ManifestBuild()
|
|
271
|
+
>>> build_config_empty.environment_to_dict()
|
|
272
|
+
{}
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
if self.environment is None:
|
|
276
|
+
return {}
|
|
277
|
+
|
|
278
|
+
return {key: str(value) for key, value in self.environment.items()}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class ManifestPythonModel(BaseModel):
|
|
282
|
+
"""
|
|
283
|
+
Model-specific instructions for a Python app.
|
|
284
|
+
|
|
285
|
+
You can import the `ManifestPythonModel` class directly from `nextmv`:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from nextmv import ManifestPythonModel
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
name : str
|
|
294
|
+
The name of the decision model.
|
|
295
|
+
options : Optional[list[dict[str, Any]]], default=None
|
|
296
|
+
Options for the decision model. This is a data representation of the
|
|
297
|
+
`nextmv.Options` class. It consists of a list of dicts. Each dict
|
|
298
|
+
represents the `nextmv.Option` class. It is used to be able to
|
|
299
|
+
reconstruct an Options object from data when loading a decision model.
|
|
300
|
+
|
|
301
|
+
Examples
|
|
302
|
+
--------
|
|
303
|
+
>>> from nextmv import ManifestPythonModel
|
|
304
|
+
>>> python_model_config = ManifestPythonModel(
|
|
305
|
+
... name="routing_model",
|
|
306
|
+
... options=[{"name": "max_vehicles", "type": "int", "default": 10}]
|
|
307
|
+
... )
|
|
308
|
+
>>> python_model_config.name
|
|
309
|
+
'routing_model'
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
name: str
|
|
313
|
+
"""The name of the decision model."""
|
|
314
|
+
options: list[dict[str, Any]] | None = None
|
|
315
|
+
"""
|
|
316
|
+
Options for the decision model. This is a data representation of the
|
|
317
|
+
`nextmv.Options` class. It consists of a list of dicts. Each dict
|
|
318
|
+
represents the `nextmv.Option` class. It is used to be able to
|
|
319
|
+
reconstruct an Options object from data when loading a decision model.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class ManifestPython(BaseModel):
|
|
324
|
+
"""
|
|
325
|
+
Python-specific instructions.
|
|
326
|
+
|
|
327
|
+
You can import the `ManifestPython` class directly from `nextmv`:
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from nextmv import ManifestPython
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
pip_requirements : Optional[Union[str, list[str]]], default=None
|
|
336
|
+
Path to a requirements.txt file containing (additional) Python
|
|
337
|
+
dependencies that will be bundled with the app. Alternatively, you can provide a
|
|
338
|
+
list of strings, each representing a package to install, e.g.,
|
|
339
|
+
`["nextmv==0.28.2", "ortools==9.12.4544"]`.
|
|
340
|
+
Aliases: `pip-requirements`.
|
|
341
|
+
model : Optional[ManifestPythonModel], default=None
|
|
342
|
+
Information about an encoded decision model as handled via mlflow. This
|
|
343
|
+
information is used to load the decision model from the app bundle.
|
|
344
|
+
|
|
345
|
+
Examples
|
|
346
|
+
--------
|
|
347
|
+
>>> from nextmv import ManifestPython, ManifestPythonModel
|
|
348
|
+
>>> python_config = ManifestPython(
|
|
349
|
+
... pip_requirements="requirements.txt",
|
|
350
|
+
... model=ManifestPythonModel(name="my_model")
|
|
351
|
+
... )
|
|
352
|
+
>>> python_config.pip_requirements
|
|
353
|
+
'requirements.txt'
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
pip_requirements: str | list[str] | None = Field(
|
|
357
|
+
serialization_alias="pip-requirements",
|
|
358
|
+
validation_alias=AliasChoices("pip-requirements", "pip_requirements"),
|
|
359
|
+
default=None,
|
|
360
|
+
)
|
|
361
|
+
"""
|
|
362
|
+
Path to a requirements.txt file or list of packages.
|
|
363
|
+
|
|
364
|
+
Contains (additional) Python dependencies that will be bundled with the
|
|
365
|
+
app. Can be either a string path to a requirements.txt file or a list
|
|
366
|
+
of package specifications.
|
|
367
|
+
"""
|
|
368
|
+
arch: ManifestPythonArch | None = None
|
|
369
|
+
"""
|
|
370
|
+
The architecture this model is meant to run on. One of "arm64" or "amd64". Uses
|
|
371
|
+
"arm64" if not specified.
|
|
372
|
+
"""
|
|
373
|
+
version: str | float | None = None
|
|
374
|
+
"""
|
|
375
|
+
The Python version this model is meant to run with. Uses "3.11" if not specified.
|
|
376
|
+
"""
|
|
377
|
+
model: ManifestPythonModel | None = None
|
|
378
|
+
"""
|
|
379
|
+
Information about an encoded decision model.
|
|
380
|
+
|
|
381
|
+
As handled via mlflow. This information is used to load the decision model
|
|
382
|
+
from the app bundle.
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
@field_validator("version", mode="before")
|
|
386
|
+
@classmethod
|
|
387
|
+
def validate_version(cls, v: str | float | None) -> str | None:
|
|
388
|
+
"""
|
|
389
|
+
Validate and convert the Python version field to a string.
|
|
390
|
+
|
|
391
|
+
This validator allows the version to be specified as either a float or string
|
|
392
|
+
in the manifest for convenience, but ensures it's stored internally as a string.
|
|
393
|
+
|
|
394
|
+
Parameters
|
|
395
|
+
----------
|
|
396
|
+
v : Optional[Union[str, float]]
|
|
397
|
+
The version value to validate. Can be None, a string, or a float.
|
|
398
|
+
|
|
399
|
+
Returns
|
|
400
|
+
-------
|
|
401
|
+
Optional[str]
|
|
402
|
+
The version as a string, or None if the input was None.
|
|
403
|
+
|
|
404
|
+
Examples
|
|
405
|
+
--------
|
|
406
|
+
>>> ManifestPython.validate_version(3.11)
|
|
407
|
+
'3.11'
|
|
408
|
+
>>> ManifestPython.validate_version("3.11")
|
|
409
|
+
'3.11'
|
|
410
|
+
>>> ManifestPython.validate_version(None) is None
|
|
411
|
+
True
|
|
412
|
+
"""
|
|
413
|
+
# We allow the version to be a float in the manifest for convenience, but we want
|
|
414
|
+
# to store it as a string internally.
|
|
415
|
+
if v is None:
|
|
416
|
+
return None
|
|
417
|
+
if isinstance(v, float):
|
|
418
|
+
return str(v)
|
|
419
|
+
return v
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class ManifestOptionUI(BaseModel):
|
|
423
|
+
"""
|
|
424
|
+
UI attributes for an option in the manifest.
|
|
425
|
+
|
|
426
|
+
You can import the `ManifestOptionUI` class directly from `nextmv`:
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
from nextmv import ManifestOptionUI
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Parameters
|
|
433
|
+
----------
|
|
434
|
+
control_type : str, optional
|
|
435
|
+
The type of control to use for the option in the Nextmv Cloud UI. This is
|
|
436
|
+
useful for defining how the option should be presented in the Nextmv
|
|
437
|
+
Cloud UI. Current control types include "input", "select", "slider", and
|
|
438
|
+
"toggle". This attribute is not used in the local `Options` class, but
|
|
439
|
+
it is used in the Nextmv Cloud UI to define the type of control to use for
|
|
440
|
+
the option. This will be validated by the Nextmv Cloud, and availability
|
|
441
|
+
is based on option_type.
|
|
442
|
+
hidden_from : list[str], optional
|
|
443
|
+
A list of team roles to which this option will be hidden in the UI. For
|
|
444
|
+
example, if you want to hide an option from the "operator" role, you can
|
|
445
|
+
pass `hidden_from=["operator"]`.
|
|
446
|
+
display_name : str, optional
|
|
447
|
+
An optional display name for the option. This is useful for making
|
|
448
|
+
the option more user-friendly in the UI.
|
|
449
|
+
|
|
450
|
+
Examples
|
|
451
|
+
--------
|
|
452
|
+
>>> from nextmv import ManifestOptionUI
|
|
453
|
+
>>> ui_config = ManifestOptionUI(control_type="input")
|
|
454
|
+
>>> ui_config.control_type
|
|
455
|
+
'input'
|
|
456
|
+
"""
|
|
457
|
+
|
|
458
|
+
control_type: str | None = None
|
|
459
|
+
"""The type of control to use for the option in the Nextmv Cloud UI."""
|
|
460
|
+
hidden_from: list[str] | None = None
|
|
461
|
+
"""A list of team roles for which this option will be hidden in the UI."""
|
|
462
|
+
display_name: str | None = None
|
|
463
|
+
"""An optional display name for the option. This is useful for making
|
|
464
|
+
the option more user-friendly in the UI.
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class ManifestOption(BaseModel):
|
|
469
|
+
"""
|
|
470
|
+
An option for the decision model that is recorded in the manifest.
|
|
471
|
+
|
|
472
|
+
You can import the `ManifestOption` class directly from `nextmv`:
|
|
473
|
+
|
|
474
|
+
```python
|
|
475
|
+
from nextmv import ManifestOption
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Parameters
|
|
479
|
+
----------
|
|
480
|
+
name : str
|
|
481
|
+
The name of the option.
|
|
482
|
+
option_type : str
|
|
483
|
+
The type of the option. This is a string representation of the
|
|
484
|
+
`nextmv.Option` class (e.g., "string", "int", "bool", "float").
|
|
485
|
+
Aliases: `type`.
|
|
486
|
+
default : Optional[Any], default=None
|
|
487
|
+
The default value of the option.
|
|
488
|
+
description : Optional[str], default=""
|
|
489
|
+
The description of the option.
|
|
490
|
+
required : bool, default=False
|
|
491
|
+
Whether the option is required or not.
|
|
492
|
+
additional_attributes : Optional[dict[str, Any]], default=None
|
|
493
|
+
Optional additional attributes for the option. The Nextmv Cloud may
|
|
494
|
+
perform validation on these attributes. For example, the maximum
|
|
495
|
+
length of a string or the maximum value of an integer. These
|
|
496
|
+
additional attributes will be shown in the help message of the
|
|
497
|
+
`Options`.
|
|
498
|
+
ui : Optional[ManifestOptionUI], default=None
|
|
499
|
+
Optional UI attributes for the option. This is a dictionary that can
|
|
500
|
+
contain additional information about how the option should be displayed
|
|
501
|
+
in the Nextmv Cloud UI. This is not used in the local `Options` class,
|
|
502
|
+
but it is used in the Nextmv Cloud UI to define how the option should be
|
|
503
|
+
presented.
|
|
504
|
+
|
|
505
|
+
Examples
|
|
506
|
+
--------
|
|
507
|
+
>>> from nextmv import ManifestOption
|
|
508
|
+
>>> option = ManifestOption(
|
|
509
|
+
... name="solve.duration",
|
|
510
|
+
... option_type="string",
|
|
511
|
+
... default="30s",
|
|
512
|
+
... description="Maximum duration for the solver."
|
|
513
|
+
... )
|
|
514
|
+
>>> option.name
|
|
515
|
+
'solve.duration'
|
|
516
|
+
"""
|
|
517
|
+
|
|
518
|
+
name: str
|
|
519
|
+
"""The name of the option"""
|
|
520
|
+
option_type: str = Field(
|
|
521
|
+
serialization_alias="option_type",
|
|
522
|
+
validation_alias=AliasChoices("type", "option_type"),
|
|
523
|
+
)
|
|
524
|
+
"""The type of the option (e.g., "string", "int", "bool", "float)."""
|
|
525
|
+
|
|
526
|
+
default: Any | None = None
|
|
527
|
+
"""The default value of the option"""
|
|
528
|
+
description: str | None = ""
|
|
529
|
+
"""The description of the option"""
|
|
530
|
+
required: bool = False
|
|
531
|
+
"""Whether the option is required or not"""
|
|
532
|
+
additional_attributes: dict[str, Any] | None = None
|
|
533
|
+
"""Optional additional attributes for the option."""
|
|
534
|
+
ui: ManifestOptionUI | None = None
|
|
535
|
+
"""Optional UI attributes for the option."""
|
|
536
|
+
|
|
537
|
+
@classmethod
|
|
538
|
+
def from_option(cls, option: Option) -> "ManifestOption":
|
|
539
|
+
"""
|
|
540
|
+
Create a `ManifestOption` from an `Option`.
|
|
541
|
+
|
|
542
|
+
Parameters
|
|
543
|
+
----------
|
|
544
|
+
option : nextmv.options.Option
|
|
545
|
+
The option to convert.
|
|
546
|
+
|
|
547
|
+
Returns
|
|
548
|
+
-------
|
|
549
|
+
ManifestOption
|
|
550
|
+
The converted option.
|
|
551
|
+
|
|
552
|
+
Raises
|
|
553
|
+
------
|
|
554
|
+
ValueError
|
|
555
|
+
If the `option.option_type` is unknown.
|
|
556
|
+
|
|
557
|
+
Examples
|
|
558
|
+
--------
|
|
559
|
+
>>> from nextmv.options import Option
|
|
560
|
+
>>> from nextmv import ManifestOption
|
|
561
|
+
>>> sdk_option = Option(name="max_stops", option_type=int, default=100)
|
|
562
|
+
>>> manifest_opt = ManifestOption.from_option(sdk_option)
|
|
563
|
+
>>> manifest_opt.name
|
|
564
|
+
'max_stops'
|
|
565
|
+
>>> manifest_opt.option_type
|
|
566
|
+
'int'
|
|
567
|
+
"""
|
|
568
|
+
option_type = option.option_type
|
|
569
|
+
if option_type is str:
|
|
570
|
+
option_type = "string"
|
|
571
|
+
elif option_type is bool:
|
|
572
|
+
option_type = "bool"
|
|
573
|
+
elif option_type is int:
|
|
574
|
+
option_type = "int"
|
|
575
|
+
elif option_type is float:
|
|
576
|
+
option_type = "float"
|
|
577
|
+
else:
|
|
578
|
+
raise ValueError(f"unknown option type: {option_type}")
|
|
579
|
+
|
|
580
|
+
return cls(
|
|
581
|
+
name=option.name,
|
|
582
|
+
option_type=option_type,
|
|
583
|
+
default=option.default,
|
|
584
|
+
description=option.description,
|
|
585
|
+
required=option.required,
|
|
586
|
+
additional_attributes=option.additional_attributes,
|
|
587
|
+
ui=ManifestOptionUI(
|
|
588
|
+
control_type=option.control_type,
|
|
589
|
+
hidden_from=option.hidden_from,
|
|
590
|
+
display_name=option.display_name,
|
|
591
|
+
)
|
|
592
|
+
if option.control_type or option.hidden_from or option.display_name
|
|
593
|
+
else None,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
def to_option(self) -> Option:
|
|
597
|
+
"""
|
|
598
|
+
Convert the `ManifestOption` to an `Option`.
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
nextmv.options.Option
|
|
603
|
+
The converted option.
|
|
604
|
+
|
|
605
|
+
Raises
|
|
606
|
+
------
|
|
607
|
+
ValueError
|
|
608
|
+
If the `self.option_type` is unknown.
|
|
609
|
+
|
|
610
|
+
Examples
|
|
611
|
+
--------
|
|
612
|
+
>>> from nextmv import ManifestOption
|
|
613
|
+
>>> manifest_opt = ManifestOption(name="max_stops", option_type="int", default=100)
|
|
614
|
+
>>> sdk_option = manifest_opt.to_option()
|
|
615
|
+
>>> sdk_option.name
|
|
616
|
+
'max_stops'
|
|
617
|
+
>>> sdk_option.option_type
|
|
618
|
+
<class 'int'>
|
|
619
|
+
"""
|
|
620
|
+
|
|
621
|
+
option_type_string = self.option_type
|
|
622
|
+
if option_type_string == "string":
|
|
623
|
+
option_type = str
|
|
624
|
+
elif option_type_string == "bool":
|
|
625
|
+
option_type = bool
|
|
626
|
+
elif option_type_string == "int":
|
|
627
|
+
option_type = int
|
|
628
|
+
elif option_type_string == "float":
|
|
629
|
+
option_type = float
|
|
630
|
+
else:
|
|
631
|
+
raise ValueError(f"unknown option type: {option_type_string}")
|
|
632
|
+
|
|
633
|
+
return Option(
|
|
634
|
+
name=self.name,
|
|
635
|
+
option_type=option_type,
|
|
636
|
+
default=self.default,
|
|
637
|
+
description=self.description,
|
|
638
|
+
required=self.required,
|
|
639
|
+
additional_attributes=self.additional_attributes,
|
|
640
|
+
control_type=self.ui.control_type if self.ui else None,
|
|
641
|
+
hidden_from=self.ui.hidden_from if self.ui else None,
|
|
642
|
+
display_name=self.ui.display_name if self.ui else None,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class ManifestValidation(BaseModel):
|
|
647
|
+
"""
|
|
648
|
+
Validation rules for options in the manifest.
|
|
649
|
+
|
|
650
|
+
You can import the `ManifestValidation` class directly from `nextmv`:
|
|
651
|
+
|
|
652
|
+
```python
|
|
653
|
+
from nextmv import ManifestValidation
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Parameters
|
|
657
|
+
----------
|
|
658
|
+
enforce : str, default="none"
|
|
659
|
+
The enforcement level for the validation rules. This can be set to
|
|
660
|
+
"none" or "all". If set to "none", no validation will be performed
|
|
661
|
+
on the options prior to creating a run. If set to "all", all validation
|
|
662
|
+
rules will be enforced on the options, and runs will not be created
|
|
663
|
+
if any of the rules of the options are violated.
|
|
664
|
+
|
|
665
|
+
Examples
|
|
666
|
+
--------
|
|
667
|
+
>>> from nextmv import ManifestValidation
|
|
668
|
+
>>> validation = ManifestValidation(enforce="all")
|
|
669
|
+
>>> validation.enforce
|
|
670
|
+
'all'
|
|
671
|
+
"""
|
|
672
|
+
|
|
673
|
+
enforce: str = "none"
|
|
674
|
+
"""The enforcement level for the validation rules.
|
|
675
|
+
This can be set to "none" or "all". If set to "none", no validation will
|
|
676
|
+
be performed on the options prior to creating a run. If set to "all", all
|
|
677
|
+
validation rules will be enforced on the options, and runs will not be
|
|
678
|
+
created if any of the rules of the options are violated.
|
|
679
|
+
"""
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
class ManifestOptions(BaseModel):
|
|
683
|
+
"""
|
|
684
|
+
Options for the decision model.
|
|
685
|
+
|
|
686
|
+
You can import the `ManifestOptions` class directly from `nextmv`:
|
|
687
|
+
|
|
688
|
+
```python
|
|
689
|
+
from nextmv import ManifestOptions
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Parameters
|
|
693
|
+
----------
|
|
694
|
+
strict : Optional[bool], default=False
|
|
695
|
+
If strict is set to `True`, only the listed options will be allowed.
|
|
696
|
+
items : Optional[list[ManifestOption]], default=None
|
|
697
|
+
The actual list of options for the decision model. An option
|
|
698
|
+
is a parameter that configures the decision model.
|
|
699
|
+
validation: Optional[ManifestValidation], default=None
|
|
700
|
+
Optional validation rules for all options.
|
|
701
|
+
format : Optional[list[str]], default=None
|
|
702
|
+
A list of strings that define how options are transformed into command
|
|
703
|
+
line arguments. Use `{{name}}` to refer to the option name and
|
|
704
|
+
`{{value}}` to refer to the option value.
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
Examples
|
|
708
|
+
--------
|
|
709
|
+
>>> from nextmv import ManifestOptions, ManifestOption
|
|
710
|
+
>>> options_config = ManifestOptions(
|
|
711
|
+
... strict=True,
|
|
712
|
+
... validation=ManifestValidation(enforce="all"),
|
|
713
|
+
... items=[
|
|
714
|
+
... ManifestOption(name="timeout", option_type="int", default=60),
|
|
715
|
+
... ManifestOption(name="vehicle_capacity", option_type="float", default=100.0)
|
|
716
|
+
... ]
|
|
717
|
+
... )
|
|
718
|
+
>>> options_config.strict
|
|
719
|
+
True
|
|
720
|
+
>>> len(options_config.items)
|
|
721
|
+
2
|
|
722
|
+
"""
|
|
723
|
+
|
|
724
|
+
strict: bool | None = False
|
|
725
|
+
"""If strict is set to `True`, only the listed options will be allowed."""
|
|
726
|
+
validation: ManifestValidation | None = None
|
|
727
|
+
"""Optional validation rules for all options."""
|
|
728
|
+
items: list[ManifestOption] | None = None
|
|
729
|
+
"""The actual list of options for the decision model.
|
|
730
|
+
|
|
731
|
+
An option is a parameter that configures the decision model.
|
|
732
|
+
"""
|
|
733
|
+
format: list[str] | None = None
|
|
734
|
+
"""A list of strings that define how options are transformed into command line arguments.
|
|
735
|
+
|
|
736
|
+
Use `{{name}}` to refer to the option name and `{{value}}` to refer to the option value.
|
|
737
|
+
For example, `["-{{name}}", "{{value}}"]` will transform an option named `max_vehicles`
|
|
738
|
+
with a value of `10` into the command line argument `-max_vehicles 10`.
|
|
739
|
+
"""
|
|
740
|
+
|
|
741
|
+
@classmethod
|
|
742
|
+
def from_options(
|
|
743
|
+
cls,
|
|
744
|
+
options: Options,
|
|
745
|
+
validation: OptionsEnforcement = None,
|
|
746
|
+
format: list[str] | None = None,
|
|
747
|
+
) -> "ManifestOptions":
|
|
748
|
+
"""
|
|
749
|
+
Create a `ManifestOptions` from a `nextmv.Options`.
|
|
750
|
+
|
|
751
|
+
Parameters
|
|
752
|
+
----------
|
|
753
|
+
options : nextmv.options.Options
|
|
754
|
+
The options to convert.
|
|
755
|
+
validation : Optional[OptionsEnforcement], default=None
|
|
756
|
+
Optional validation rules for the options. If provided, it will be
|
|
757
|
+
used to set the `validation` attribute of the `ManifestOptions`.
|
|
758
|
+
format : Optional[list[str]], default=None
|
|
759
|
+
A list of strings that define how options are transformed into
|
|
760
|
+
command line arguments. Use `{{name}}` to refer to the option name
|
|
761
|
+
and `{{value}}` to refer to the option value.
|
|
762
|
+
|
|
763
|
+
For example, `["-{{name}}", "{{value}}"]` will transform an option
|
|
764
|
+
named `max_vehicles` with a value of `10` into the command line
|
|
765
|
+
argument `-max_vehicles 10`.
|
|
766
|
+
|
|
767
|
+
Returns
|
|
768
|
+
-------
|
|
769
|
+
ManifestOptions
|
|
770
|
+
The converted options.
|
|
771
|
+
|
|
772
|
+
Examples
|
|
773
|
+
--------
|
|
774
|
+
>>> from nextmv.options import Options, Option
|
|
775
|
+
>>> from nextmv import ManifestOptions
|
|
776
|
+
>>> sdk_options = Options(Option("max_vehicles", int, 5))
|
|
777
|
+
>>> manifest_options = ManifestOptions.from_options(sdk_options)
|
|
778
|
+
>>> manifest_options.items[0].name
|
|
779
|
+
'max_vehicles'
|
|
780
|
+
"""
|
|
781
|
+
|
|
782
|
+
items = [ManifestOption.from_option(option) for option in options.options]
|
|
783
|
+
return cls(
|
|
784
|
+
strict=validation.strict if validation else False,
|
|
785
|
+
validation=ManifestValidation(enforce="all" if validation and validation.validation_enforce else "none"),
|
|
786
|
+
items=items,
|
|
787
|
+
format=format,
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
class ManifestContentMultiFileInput(BaseModel):
|
|
792
|
+
"""
|
|
793
|
+
Configuration for multi-file content format input.
|
|
794
|
+
|
|
795
|
+
You can import the `ManifestContentMultiFileInput` class directly from `nextmv`:
|
|
796
|
+
|
|
797
|
+
```python
|
|
798
|
+
from nextmv import ManifestContentMultiFileInput
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
Parameters
|
|
802
|
+
----------
|
|
803
|
+
path : str
|
|
804
|
+
The path to the input file or directory.
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
Examples
|
|
808
|
+
--------
|
|
809
|
+
>>> from nextmv import ManifestContentMultiFileInput
|
|
810
|
+
>>> input_config = ManifestContentMultiFileInput(path="data/input/")
|
|
811
|
+
>>> input_config.path
|
|
812
|
+
'data/input/'
|
|
813
|
+
"""
|
|
814
|
+
|
|
815
|
+
path: str
|
|
816
|
+
"""The path to the input file or directory."""
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
class ManifestContentMultiFileOutput(BaseModel):
|
|
820
|
+
"""
|
|
821
|
+
Configuration for multi-file content format output.
|
|
822
|
+
|
|
823
|
+
You can import the `ManifestContentMultiFileOutput` class directly from `nextmv`:
|
|
824
|
+
|
|
825
|
+
```python
|
|
826
|
+
from nextmv import ManifestContentMultiFileOutput
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
Parameters
|
|
830
|
+
----------
|
|
831
|
+
statistics : Optional[str], default=""
|
|
832
|
+
The path to the statistics file.
|
|
833
|
+
assets : Optional[str], default=""
|
|
834
|
+
The path to the assets file.
|
|
835
|
+
solutions : Optional[str], default=""
|
|
836
|
+
The path to the solutions directory.
|
|
837
|
+
|
|
838
|
+
Examples
|
|
839
|
+
--------
|
|
840
|
+
>>> from nextmv import ManifestContentMultiFileOutput
|
|
841
|
+
>>> output_config = ManifestContentMultiFileOutput(
|
|
842
|
+
... statistics="my-outputs/statistics.json",
|
|
843
|
+
... assets="my-outputs/assets.json",
|
|
844
|
+
... solutions="my-outputs/solutions/"
|
|
845
|
+
... )
|
|
846
|
+
>>> output_config.statistics
|
|
847
|
+
'my-outputs/statistics.json'
|
|
848
|
+
"""
|
|
849
|
+
|
|
850
|
+
statistics: str | None = ""
|
|
851
|
+
"""The path to the statistics file."""
|
|
852
|
+
assets: str | None = ""
|
|
853
|
+
"""The path to the assets file."""
|
|
854
|
+
solutions: str | None = ""
|
|
855
|
+
"""The path to the solutions directory."""
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
class ManifestContentMultiFile(BaseModel):
|
|
859
|
+
"""
|
|
860
|
+
Configuration for multi-file content format.
|
|
861
|
+
|
|
862
|
+
You can import the `ManifestContentMultiFile` class directly from `nextmv`:
|
|
863
|
+
|
|
864
|
+
```python
|
|
865
|
+
from nextmv import ManifestContentMultiFile
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
Parameters
|
|
869
|
+
----------
|
|
870
|
+
input : ManifestContentMultiFileInput
|
|
871
|
+
Configuration for multi-file content format input.
|
|
872
|
+
output : ManifestContentMultiFileOutput
|
|
873
|
+
Configuration for multi-file content format output.
|
|
874
|
+
|
|
875
|
+
Examples
|
|
876
|
+
--------
|
|
877
|
+
>>> from nextmv import ManifestContentMultiFile, ManifestContentMultiFileInput, ManifestContentMultiFileOutput
|
|
878
|
+
>>> multi_file_config = ManifestContentMultiFile(
|
|
879
|
+
... input=ManifestContentMultiFileInput(path="data/input/"),
|
|
880
|
+
... output=ManifestContentMultiFileOutput(
|
|
881
|
+
... statistics="my-outputs/statistics.json",
|
|
882
|
+
... assets="my-outputs/assets.json",
|
|
883
|
+
... solutions="my-outputs/solutions/"
|
|
884
|
+
... )
|
|
885
|
+
... )
|
|
886
|
+
>>> multi_file_config.input.path
|
|
887
|
+
'data/input/'
|
|
888
|
+
|
|
889
|
+
"""
|
|
890
|
+
|
|
891
|
+
input: ManifestContentMultiFileInput
|
|
892
|
+
"""Configuration for multi-file content format input."""
|
|
893
|
+
output: ManifestContentMultiFileOutput
|
|
894
|
+
"""Configuration for multi-file content format output."""
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
class ManifestContent(BaseModel):
|
|
898
|
+
"""
|
|
899
|
+
Content configuration for specifying how the app input/output is handled.
|
|
900
|
+
|
|
901
|
+
You can import the `ManifestContent` class directly from `nextmv`:
|
|
902
|
+
|
|
903
|
+
```python
|
|
904
|
+
from nextmv import ManifestContent
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Parameters
|
|
908
|
+
----------
|
|
909
|
+
format : str
|
|
910
|
+
The format of the content. Must be one of "json", "multi-file", or "csv-archive".
|
|
911
|
+
multi_file : Optional[ManifestContentMultiFile], default=None
|
|
912
|
+
Configuration for multi-file content format.
|
|
913
|
+
|
|
914
|
+
Examples
|
|
915
|
+
--------
|
|
916
|
+
>>> from nextmv import ManifestContent
|
|
917
|
+
>>> content_config = ManifestContent(
|
|
918
|
+
... format="multi-file",
|
|
919
|
+
... multi_file=ManifestContentMultiFile(
|
|
920
|
+
... input=ManifestContentMultiFileInput(path="data/input/"),
|
|
921
|
+
... output=ManifestContentMultiFileOutput(
|
|
922
|
+
... statistics="my-outputs/statistics.json",
|
|
923
|
+
... assets="my-outputs/assets.json",
|
|
924
|
+
... solutions="my-outputs/solutions/"
|
|
925
|
+
... )
|
|
926
|
+
... )
|
|
927
|
+
... )
|
|
928
|
+
>>> content_config.format
|
|
929
|
+
'multi-file'
|
|
930
|
+
>>> content_config.multi_file.input.path
|
|
931
|
+
'data/input/'
|
|
932
|
+
"""
|
|
933
|
+
|
|
934
|
+
format: InputFormat
|
|
935
|
+
"""
|
|
936
|
+
The format of the content. Can only be `InputFormat.JSON`,
|
|
937
|
+
`InputFormat.MULTI_FILE`, or `InputFormat.CSV_ARCHIVE`.
|
|
938
|
+
"""
|
|
939
|
+
multi_file: ManifestContentMultiFile | None = Field(
|
|
940
|
+
serialization_alias="multi-file",
|
|
941
|
+
validation_alias=AliasChoices("multi-file", "multi_file"),
|
|
942
|
+
default=None,
|
|
943
|
+
)
|
|
944
|
+
"""Configuration for multi-file content format."""
|
|
945
|
+
|
|
946
|
+
def model_post_init(self, __context) -> None:
|
|
947
|
+
"""
|
|
948
|
+
Post-initialization validation to ensure format field contains valid values.
|
|
949
|
+
|
|
950
|
+
This method is automatically called by Pydantic after the model is initialized
|
|
951
|
+
to validate that the format field contains one of the acceptable values.
|
|
952
|
+
|
|
953
|
+
Parameters
|
|
954
|
+
----------
|
|
955
|
+
__context : Any
|
|
956
|
+
Pydantic context (unused in this implementation).
|
|
957
|
+
|
|
958
|
+
Raises
|
|
959
|
+
------
|
|
960
|
+
ValueError
|
|
961
|
+
If the format field contains an invalid value that is not one of the
|
|
962
|
+
acceptable formats (JSON, MULTI_FILE, or CSV_ARCHIVE).
|
|
963
|
+
"""
|
|
964
|
+
acceptable_formats = [InputFormat.JSON, InputFormat.MULTI_FILE, InputFormat.CSV_ARCHIVE]
|
|
965
|
+
if self.format not in acceptable_formats:
|
|
966
|
+
raise ValueError(f"Invalid format: {self.format}. Must be one of {acceptable_formats}.")
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
class ManifestConfiguration(BaseModel):
|
|
970
|
+
"""
|
|
971
|
+
Configuration for the decision model.
|
|
972
|
+
|
|
973
|
+
You can import the `ManifestConfiguration` class directly from `nextmv`:
|
|
974
|
+
|
|
975
|
+
```python
|
|
976
|
+
from nextmv import ManifestConfiguration
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
Parameters
|
|
980
|
+
----------
|
|
981
|
+
options : Optional[ManifestOptions], default=None
|
|
982
|
+
Options for the decision model.
|
|
983
|
+
content : Optional[ManifestContent], default=None
|
|
984
|
+
Content configuration for specifying how the app input/output is handled.
|
|
985
|
+
|
|
986
|
+
Examples
|
|
987
|
+
--------
|
|
988
|
+
>>> from nextmv import ManifestConfiguration, ManifestOptions, ManifestOption
|
|
989
|
+
>>> model_config = ManifestConfiguration(
|
|
990
|
+
... options=ManifestOptions(
|
|
991
|
+
... items=[ManifestOption(name="debug_mode", option_type="bool", default=False)]
|
|
992
|
+
... )
|
|
993
|
+
... )
|
|
994
|
+
>>> model_config.options.items[0].name
|
|
995
|
+
'debug_mode'
|
|
996
|
+
"""
|
|
997
|
+
|
|
998
|
+
options: ManifestOptions | None = None
|
|
999
|
+
"""Options for the decision model."""
|
|
1000
|
+
content: ManifestContent | None = None
|
|
1001
|
+
"""Content configuration for specifying how the app input/output is handled."""
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
class ManifestExecution(BaseModel):
|
|
1005
|
+
"""
|
|
1006
|
+
Execution configuration for the decision model.
|
|
1007
|
+
|
|
1008
|
+
You can import the `ManifestExecution` class directly from `nextmv`:
|
|
1009
|
+
|
|
1010
|
+
```python
|
|
1011
|
+
from nextmv import ManifestExecution
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
Parameters
|
|
1015
|
+
----------
|
|
1016
|
+
entrypoint : Optional[str], default=None
|
|
1017
|
+
The entrypoint for the decision model, e.g.: `./app.py`.
|
|
1018
|
+
cwd : Optional[str], default=None
|
|
1019
|
+
The working directory to set when running the app, e.g.: `./src/`.
|
|
1020
|
+
|
|
1021
|
+
Examples
|
|
1022
|
+
--------
|
|
1023
|
+
>>> from nextmv import ManifestExecution
|
|
1024
|
+
>>> exec_config = ManifestExecution(
|
|
1025
|
+
... entrypoint="./app.py",
|
|
1026
|
+
... cwd="./src/"
|
|
1027
|
+
... )
|
|
1028
|
+
>>> exec_config.entrypoint
|
|
1029
|
+
'./app.py'
|
|
1030
|
+
"""
|
|
1031
|
+
|
|
1032
|
+
entrypoint: str | None = None
|
|
1033
|
+
"""The entrypoint for the decision model, e.g.: `./app.py`."""
|
|
1034
|
+
cwd: str | None = None
|
|
1035
|
+
"""The working directory to set when running the app, e.g.: `./src/`."""
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
class Manifest(BaseModel):
|
|
1039
|
+
"""
|
|
1040
|
+
Represents an app manifest (`app.yaml`) for Nextmv Cloud.
|
|
1041
|
+
|
|
1042
|
+
You can import the `Manifest` class directly from `nextmv`:
|
|
1043
|
+
|
|
1044
|
+
```python
|
|
1045
|
+
from nextmv import Manifest
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
An application that runs on the Nextmv Platform must contain a file named
|
|
1049
|
+
`app.yaml` which is known as the app manifest. This file is used to specify
|
|
1050
|
+
the execution environment for the app.
|
|
1051
|
+
|
|
1052
|
+
This class represents the app manifest and allows you to load it from a
|
|
1053
|
+
file or create it programmatically.
|
|
1054
|
+
|
|
1055
|
+
Parameters
|
|
1056
|
+
----------
|
|
1057
|
+
files : list[str]
|
|
1058
|
+
The files to include (or exclude) in the app. This is mandatory.
|
|
1059
|
+
runtime : ManifestRuntime, default=ManifestRuntime.PYTHON
|
|
1060
|
+
The runtime to use for the app, it provides the environment
|
|
1061
|
+
in which the app runs. This is mandatory.
|
|
1062
|
+
type : ManifestType, default=ManifestType.PYTHON
|
|
1063
|
+
Type of application, based on the programming language. This is
|
|
1064
|
+
mandatory.
|
|
1065
|
+
build : Optional[ManifestBuild], default=None
|
|
1066
|
+
Build-specific attributes. The `build.command` to run to build
|
|
1067
|
+
the app. This command will be executed without a shell, i.e., directly.
|
|
1068
|
+
The command must exit with a status of 0 to continue the push process of
|
|
1069
|
+
the app to Nextmv Cloud. This command is executed prior to the pre-push
|
|
1070
|
+
command. The `build.environment` is used to set environment variables when
|
|
1071
|
+
running the build command given as key-value pairs.
|
|
1072
|
+
pre_push : Optional[str], default=None
|
|
1073
|
+
A command to run before the app is pushed to the Nextmv Cloud.
|
|
1074
|
+
This command can be used to compile a binary, run tests or similar tasks.
|
|
1075
|
+
One difference with what is specified under build, is that the command
|
|
1076
|
+
will be executed via the shell (i.e., `bash -c` on Linux & macOS or
|
|
1077
|
+
`cmd /c` on Windows). The command must exit with a status of 0 to
|
|
1078
|
+
continue the push process. This command is executed just before the app
|
|
1079
|
+
gets bundled and pushed (after the build command).
|
|
1080
|
+
Aliases: `pre-push`.
|
|
1081
|
+
python : Optional[ManifestPython], default=None
|
|
1082
|
+
Only for Python apps. Contains further Python-specific
|
|
1083
|
+
attributes.
|
|
1084
|
+
configuration : Optional[ManifestConfiguration], default=None
|
|
1085
|
+
A list of options for the decision model. An option is a
|
|
1086
|
+
parameter that configures the decision model.
|
|
1087
|
+
entrypoint : Optional[str], default=None
|
|
1088
|
+
Optional entrypoint for the decision model. When not specified, the
|
|
1089
|
+
following default entrypoints are used, according to the `.runtime`:
|
|
1090
|
+
- `ManifestRuntime.PYTHON`, `ManifestRuntime.HEXALY`, `ManifestRuntime.PYOMO`: `./main.py`
|
|
1091
|
+
- `ManifestRuntime.DEFAULT`: `./main`
|
|
1092
|
+
- Java: `./main.jar`
|
|
1093
|
+
|
|
1094
|
+
Examples
|
|
1095
|
+
--------
|
|
1096
|
+
>>> from nextmv import Manifest, ManifestRuntime, ManifestType
|
|
1097
|
+
>>> manifest = Manifest(
|
|
1098
|
+
... files=["main.py", "model_logic/"],
|
|
1099
|
+
... runtime=ManifestRuntime.PYTHON,
|
|
1100
|
+
... type=ManifestType.PYTHON,
|
|
1101
|
+
... )
|
|
1102
|
+
>>> manifest.files
|
|
1103
|
+
['main.py', 'model_logic/']
|
|
1104
|
+
"""
|
|
1105
|
+
|
|
1106
|
+
type: ManifestType = ManifestType.PYTHON
|
|
1107
|
+
"""
|
|
1108
|
+
Type of application, based on the programming language. This is mandatory.
|
|
1109
|
+
"""
|
|
1110
|
+
runtime: ManifestRuntime = ManifestRuntime.PYTHON
|
|
1111
|
+
"""
|
|
1112
|
+
The runtime to use for the app. It provides the environment in which the
|
|
1113
|
+
app runs. This is mandatory.
|
|
1114
|
+
"""
|
|
1115
|
+
python: ManifestPython | None = None
|
|
1116
|
+
"""
|
|
1117
|
+
Python-specific attributes. Only for Python apps. Contains further
|
|
1118
|
+
Python-specific attributes.
|
|
1119
|
+
"""
|
|
1120
|
+
files: list[str] = Field(
|
|
1121
|
+
default_factory=list,
|
|
1122
|
+
)
|
|
1123
|
+
"""The files to include (or exclude) in the app. This is mandatory."""
|
|
1124
|
+
configuration: ManifestConfiguration | None = None
|
|
1125
|
+
"""
|
|
1126
|
+
Configuration for the decision model. A list of options for the decision
|
|
1127
|
+
model. An option is a parameter that configures the decision model.
|
|
1128
|
+
"""
|
|
1129
|
+
build: ManifestBuild | None = None
|
|
1130
|
+
"""
|
|
1131
|
+
Build-specific attributes.
|
|
1132
|
+
|
|
1133
|
+
The `build.command` to run to build the app. This command will be executed
|
|
1134
|
+
without a shell, i.e., directly. The command must exit with a status of 0
|
|
1135
|
+
to continue the push process of the app to Nextmv Cloud. This command is
|
|
1136
|
+
executed prior to the pre-push command. The `build.environment` is used to
|
|
1137
|
+
set environment variables when running the build command given as key-value
|
|
1138
|
+
pairs.
|
|
1139
|
+
"""
|
|
1140
|
+
pre_push: str | None = Field(
|
|
1141
|
+
serialization_alias="pre-push",
|
|
1142
|
+
validation_alias=AliasChoices("pre-push", "pre_push"),
|
|
1143
|
+
default=None,
|
|
1144
|
+
)
|
|
1145
|
+
"""
|
|
1146
|
+
A command to run before the app is pushed to the Nextmv Cloud.
|
|
1147
|
+
|
|
1148
|
+
This command can be used to compile a binary, run tests or similar tasks.
|
|
1149
|
+
One difference with what is specified under build, is that the command will
|
|
1150
|
+
be executed via the shell (i.e., `bash -c` on Linux & macOS or `cmd /c` on
|
|
1151
|
+
Windows). The command must exit with a status of 0 to continue the push
|
|
1152
|
+
process. This command is executed just before the app gets bundled and
|
|
1153
|
+
pushed (after the build command).
|
|
1154
|
+
"""
|
|
1155
|
+
execution: ManifestExecution | None = None
|
|
1156
|
+
"""
|
|
1157
|
+
Optional execution configuration for the decision model. Allows configuration of
|
|
1158
|
+
entrypoint and more.
|
|
1159
|
+
"""
|
|
1160
|
+
|
|
1161
|
+
@classmethod
|
|
1162
|
+
def from_yaml(cls, dirpath: str) -> "Manifest":
|
|
1163
|
+
"""
|
|
1164
|
+
Load a manifest from a YAML file.
|
|
1165
|
+
|
|
1166
|
+
The YAML file is expected to be named `app.yaml` and located in the
|
|
1167
|
+
specified directory.
|
|
1168
|
+
|
|
1169
|
+
Parameters
|
|
1170
|
+
----------
|
|
1171
|
+
dirpath : str
|
|
1172
|
+
Path to the directory containing the `app.yaml` file.
|
|
1173
|
+
|
|
1174
|
+
Returns
|
|
1175
|
+
-------
|
|
1176
|
+
Manifest
|
|
1177
|
+
The loaded manifest.
|
|
1178
|
+
|
|
1179
|
+
Raises
|
|
1180
|
+
------
|
|
1181
|
+
FileNotFoundError
|
|
1182
|
+
If the `app.yaml` file is not found in `dirpath`.
|
|
1183
|
+
yaml.YAMLError
|
|
1184
|
+
If there is an error parsing the YAML file.
|
|
1185
|
+
|
|
1186
|
+
Examples
|
|
1187
|
+
--------
|
|
1188
|
+
Assuming an `app.yaml` file exists in `./my_app_dir`:
|
|
1189
|
+
|
|
1190
|
+
```yaml
|
|
1191
|
+
# ./my_app_dir/app.yaml
|
|
1192
|
+
files:
|
|
1193
|
+
- main.py
|
|
1194
|
+
runtime: ghcr.io/nextmv-io/runtime/python:3.11
|
|
1195
|
+
type: python
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
>>> from nextmv import Manifest
|
|
1199
|
+
>>> # manifest = Manifest.from_yaml("./my_app_dir") # This would be run
|
|
1200
|
+
>>> # assert manifest.type == "python"
|
|
1201
|
+
"""
|
|
1202
|
+
|
|
1203
|
+
with open(os.path.join(dirpath, MANIFEST_FILE_NAME)) as file:
|
|
1204
|
+
raw_manifest = yaml.safe_load(file)
|
|
1205
|
+
|
|
1206
|
+
return cls.from_dict(raw_manifest)
|
|
1207
|
+
|
|
1208
|
+
def to_yaml(self, dirpath: str) -> None:
|
|
1209
|
+
"""
|
|
1210
|
+
Write the manifest to a YAML file.
|
|
1211
|
+
|
|
1212
|
+
The manifest will be written to a file named `app.yaml` in the
|
|
1213
|
+
specified directory.
|
|
1214
|
+
|
|
1215
|
+
Parameters
|
|
1216
|
+
----------
|
|
1217
|
+
dirpath : str
|
|
1218
|
+
Path to the directory where the `app.yaml` file will be written.
|
|
1219
|
+
|
|
1220
|
+
Raises
|
|
1221
|
+
------
|
|
1222
|
+
IOError
|
|
1223
|
+
If there is an error writing the file.
|
|
1224
|
+
yaml.YAMLError
|
|
1225
|
+
If there is an error serializing the manifest to YAML.
|
|
1226
|
+
|
|
1227
|
+
Examples
|
|
1228
|
+
--------
|
|
1229
|
+
>>> from nextmv import Manifest
|
|
1230
|
+
>>> manifest = Manifest(files=["solver.py"], type="python")
|
|
1231
|
+
>>> # manifest.to_yaml("./output_dir") # This would create ./output_dir/app.yaml
|
|
1232
|
+
"""
|
|
1233
|
+
|
|
1234
|
+
with open(os.path.join(dirpath, MANIFEST_FILE_NAME), "w") as file:
|
|
1235
|
+
yaml.dump(
|
|
1236
|
+
self.to_dict(),
|
|
1237
|
+
file,
|
|
1238
|
+
sort_keys=False,
|
|
1239
|
+
default_flow_style=False,
|
|
1240
|
+
indent=2,
|
|
1241
|
+
width=120,
|
|
1242
|
+
)
|
|
1243
|
+
|
|
1244
|
+
def extract_options(self, should_parse: bool = True) -> Options | None:
|
|
1245
|
+
"""
|
|
1246
|
+
Convert the manifest options to a `nextmv.Options` object.
|
|
1247
|
+
|
|
1248
|
+
If the manifest does not have valid options defined in
|
|
1249
|
+
`.configuration.options.items`, this method returns `None`.
|
|
1250
|
+
|
|
1251
|
+
Use the `should_parse` argument to decide if you want the options
|
|
1252
|
+
parsed, or not. For more information on option parsing, please read the
|
|
1253
|
+
docstrings on the `.parse()` method of the `nextmv.Options` object.
|
|
1254
|
+
|
|
1255
|
+
Parameters
|
|
1256
|
+
----------
|
|
1257
|
+
should_parse : bool, default=True
|
|
1258
|
+
Whether to parse the options, or not. By default, options are
|
|
1259
|
+
parsed. When command-line arguments are parsed, the help menu is
|
|
1260
|
+
created, thus parsing Options more than once may result in
|
|
1261
|
+
unexpected behavior.
|
|
1262
|
+
|
|
1263
|
+
Returns
|
|
1264
|
+
-------
|
|
1265
|
+
Optional[nextmv.options.Options]
|
|
1266
|
+
The options extracted from the manifest. If no options are found,
|
|
1267
|
+
`None` is returned.
|
|
1268
|
+
|
|
1269
|
+
Examples
|
|
1270
|
+
--------
|
|
1271
|
+
>>> from nextmv import Manifest, ManifestConfiguration, ManifestOptions, ManifestOption
|
|
1272
|
+
>>> manifest = Manifest(
|
|
1273
|
+
... files=["main.py"],
|
|
1274
|
+
... configuration=ManifestConfiguration(
|
|
1275
|
+
... options=ManifestOptions(
|
|
1276
|
+
... items=[
|
|
1277
|
+
... ManifestOption(name="duration", option_type="string", default="10s")
|
|
1278
|
+
... ]
|
|
1279
|
+
... )
|
|
1280
|
+
... )
|
|
1281
|
+
... )
|
|
1282
|
+
>>> sdk_options = manifest.extract_options()
|
|
1283
|
+
>>> sdk_options.get_option("duration").default
|
|
1284
|
+
'10s'
|
|
1285
|
+
>>> empty_manifest = Manifest(files=["main.py"])
|
|
1286
|
+
>>> empty_manifest.extract_options() is None
|
|
1287
|
+
True
|
|
1288
|
+
"""
|
|
1289
|
+
|
|
1290
|
+
if self.configuration is None or self.configuration.options is None or self.configuration.options.items is None:
|
|
1291
|
+
return None
|
|
1292
|
+
|
|
1293
|
+
options = [option.to_option() for option in self.configuration.options.items]
|
|
1294
|
+
|
|
1295
|
+
opt = Options(*options)
|
|
1296
|
+
if should_parse:
|
|
1297
|
+
opt.parse()
|
|
1298
|
+
|
|
1299
|
+
return opt
|
|
1300
|
+
|
|
1301
|
+
@classmethod
|
|
1302
|
+
def from_model_configuration(
|
|
1303
|
+
cls,
|
|
1304
|
+
model_configuration: ModelConfiguration,
|
|
1305
|
+
) -> "Manifest":
|
|
1306
|
+
"""
|
|
1307
|
+
Create a Python manifest from a `nextmv.model.ModelConfiguration`.
|
|
1308
|
+
|
|
1309
|
+
Note that the `ModelConfiguration` is almost always used in
|
|
1310
|
+
conjunction with the `nextmv.Model` class. If you are not
|
|
1311
|
+
implementing an instance of `nextmv.Model`, consider using the
|
|
1312
|
+
`from_options` method instead to initialize the manifest with the
|
|
1313
|
+
options of the model.
|
|
1314
|
+
|
|
1315
|
+
The resulting manifest will have:
|
|
1316
|
+
|
|
1317
|
+
- `files` set to `["main.py", f"{model_configuration.name}/**"]`
|
|
1318
|
+
- `runtime` set to `ManifestRuntime.PYTHON`
|
|
1319
|
+
- `type` set to `ManifestType.PYTHON`
|
|
1320
|
+
- `python.pip_requirements` set to the default requirements file name.
|
|
1321
|
+
- `python.model.name` set to `model_configuration.name`.
|
|
1322
|
+
- `python.model.options` populated from `model_configuration.options`.
|
|
1323
|
+
- `configuration.options` populated from `model_configuration.options`.
|
|
1324
|
+
|
|
1325
|
+
Parameters
|
|
1326
|
+
----------
|
|
1327
|
+
model_configuration : nextmv.model.ModelConfiguration
|
|
1328
|
+
The model configuration.
|
|
1329
|
+
|
|
1330
|
+
Returns
|
|
1331
|
+
-------
|
|
1332
|
+
Manifest
|
|
1333
|
+
The Python manifest.
|
|
1334
|
+
|
|
1335
|
+
Examples
|
|
1336
|
+
--------
|
|
1337
|
+
>>> from nextmv.model import ModelConfiguration
|
|
1338
|
+
>>> from nextmv.options import Options, Option
|
|
1339
|
+
>>> from nextmv import Manifest
|
|
1340
|
+
>>> opts = Options(Option(name="vehicle_count", option_type=int, default=5))
|
|
1341
|
+
>>> mc = ModelConfiguration(name="vehicle_router", options=opts)
|
|
1342
|
+
>>> manifest = Manifest.from_model_configuration(mc)
|
|
1343
|
+
>>> manifest.python.model.name
|
|
1344
|
+
'vehicle_router'
|
|
1345
|
+
>>> manifest.files
|
|
1346
|
+
['main.py', 'vehicle_router/**']
|
|
1347
|
+
>>> manifest.configuration.options.items[0].name
|
|
1348
|
+
'vehicle_count'
|
|
1349
|
+
"""
|
|
1350
|
+
|
|
1351
|
+
manifest_python_dict = {
|
|
1352
|
+
"pip-requirements": _REQUIREMENTS_FILE,
|
|
1353
|
+
"model": {
|
|
1354
|
+
"name": model_configuration.name,
|
|
1355
|
+
},
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if model_configuration.options is not None:
|
|
1359
|
+
manifest_python_dict["model"]["options"] = model_configuration.options.options_dict()
|
|
1360
|
+
|
|
1361
|
+
manifest_python = ManifestPython.from_dict(manifest_python_dict)
|
|
1362
|
+
manifest = cls(
|
|
1363
|
+
files=["main.py", f"{model_configuration.name}/**"],
|
|
1364
|
+
runtime=ManifestRuntime.PYTHON,
|
|
1365
|
+
type=ManifestType.PYTHON,
|
|
1366
|
+
python=manifest_python,
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
if model_configuration.options is not None:
|
|
1370
|
+
manifest.configuration = ManifestConfiguration(
|
|
1371
|
+
options=ManifestOptions.from_options(
|
|
1372
|
+
options=model_configuration.options,
|
|
1373
|
+
validation=model_configuration.options_enforcement,
|
|
1374
|
+
),
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
return manifest
|
|
1378
|
+
|
|
1379
|
+
@classmethod
|
|
1380
|
+
def from_options(cls, options: Options, validation: OptionsEnforcement = None) -> "Manifest":
|
|
1381
|
+
"""
|
|
1382
|
+
Create a basic Python manifest from `nextmv.options.Options`.
|
|
1383
|
+
|
|
1384
|
+
If you have more files than just a `main.py`, make sure you modify
|
|
1385
|
+
the `.files` attribute of the resulting manifest. This method assumes
|
|
1386
|
+
that requirements are specified in a `requirements.txt` file. You may
|
|
1387
|
+
also specify a different requirements file once you instantiate the
|
|
1388
|
+
manifest.
|
|
1389
|
+
|
|
1390
|
+
The resulting manifest will have:
|
|
1391
|
+
- `files` set to `["main.py"]`
|
|
1392
|
+
- `runtime` set to `ManifestRuntime.PYTHON`
|
|
1393
|
+
- `type` set to `ManifestType.PYTHON`
|
|
1394
|
+
- `python.pip_requirements` set to `"requirements.txt"`.
|
|
1395
|
+
- `configuration.options` populated from the provided `options`.
|
|
1396
|
+
|
|
1397
|
+
Parameters
|
|
1398
|
+
----------
|
|
1399
|
+
options : nextmv.options.Options
|
|
1400
|
+
The options to include in the manifest.
|
|
1401
|
+
validation : nextmv.options.OptionsEnforcement, default=None
|
|
1402
|
+
The validation rules for the options. This is used to set the
|
|
1403
|
+
`validation` attribute of the `ManifestOptions`.
|
|
1404
|
+
|
|
1405
|
+
Returns
|
|
1406
|
+
-------
|
|
1407
|
+
Manifest
|
|
1408
|
+
The manifest with the given options.
|
|
1409
|
+
|
|
1410
|
+
Examples
|
|
1411
|
+
--------
|
|
1412
|
+
>>> from nextmv.options import Options, Option
|
|
1413
|
+
>>> from nextmv import Manifest
|
|
1414
|
+
>>> opts = Options(
|
|
1415
|
+
... Option(name="max_runtime", option_type=str, default="60s"),
|
|
1416
|
+
... Option(name="use_heuristic", option_type=bool, default=True)
|
|
1417
|
+
... )
|
|
1418
|
+
>>> manifest = Manifest.from_options(opts)
|
|
1419
|
+
>>> manifest.files
|
|
1420
|
+
['main.py']
|
|
1421
|
+
>>> manifest.python.pip_requirements
|
|
1422
|
+
'requirements.txt'
|
|
1423
|
+
>>> len(manifest.configuration.options.items)
|
|
1424
|
+
2
|
|
1425
|
+
>>> manifest.configuration.options.items[0].name
|
|
1426
|
+
'max_runtime'
|
|
1427
|
+
"""
|
|
1428
|
+
|
|
1429
|
+
manifest = cls(
|
|
1430
|
+
files=["main.py"],
|
|
1431
|
+
runtime=ManifestRuntime.PYTHON,
|
|
1432
|
+
type=ManifestType.PYTHON,
|
|
1433
|
+
python=ManifestPython(pip_requirements="requirements.txt"),
|
|
1434
|
+
configuration=ManifestConfiguration(
|
|
1435
|
+
options=ManifestOptions.from_options(options=options, validation=validation),
|
|
1436
|
+
),
|
|
1437
|
+
)
|
|
1438
|
+
|
|
1439
|
+
return manifest
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
def default_python_manifest() -> Manifest:
|
|
1443
|
+
"""
|
|
1444
|
+
Creates a default Python manifest as a starting point for applications
|
|
1445
|
+
being executed on the Nextmv Platform.
|
|
1446
|
+
|
|
1447
|
+
You can import the `default_python_manifest` function directly from `nextmv`:
|
|
1448
|
+
|
|
1449
|
+
```python
|
|
1450
|
+
from nextmv import default_python_manifest
|
|
1451
|
+
```
|
|
1452
|
+
|
|
1453
|
+
Returns
|
|
1454
|
+
-------
|
|
1455
|
+
Manifest
|
|
1456
|
+
A default Python manifest with common settings.
|
|
1457
|
+
"""
|
|
1458
|
+
|
|
1459
|
+
m = Manifest(
|
|
1460
|
+
files=["main.py"],
|
|
1461
|
+
runtime=ManifestRuntime.PYTHON,
|
|
1462
|
+
type=ManifestType.PYTHON,
|
|
1463
|
+
python=ManifestPython(pip_requirements="requirements.txt"),
|
|
1464
|
+
)
|
|
1465
|
+
|
|
1466
|
+
return m
|