nextmv 0.26.3__tar.gz → 0.27.0__tar.gz
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-0.26.3 → nextmv-0.27.0}/PKG-INFO +1 -1
- nextmv-0.27.0/nextmv/__about__.py +1 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/__entrypoint__.py +3 -5
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/application.py +21 -17
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/manifest.py +187 -25
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/run.py +6 -6
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/scenario.py +1 -1
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/input.py +2 -2
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/model.py +3 -3
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/options.py +20 -3
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/output.py +166 -128
- nextmv-0.27.0/tests/cloud/app.yaml +45 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_application.py +3 -2
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_manifest.py +19 -16
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_output.py +228 -57
- nextmv-0.26.3/nextmv/__about__.py +0 -1
- nextmv-0.26.3/tests/cloud/app.yaml +0 -43
- {nextmv-0.26.3 → nextmv-0.27.0}/.gitignore +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/LICENSE +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/README.md +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/base_model.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/account.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/client.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/input_set.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/instance.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/package.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/safe.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/secrets.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/status.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/cloud/version.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/deprecated.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/nextmv/logger.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/pyproject.toml +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/requirements.txt +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_client.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_package.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_run.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_safe_name_id.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/cloud/test_scenario.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options1.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options2.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options3.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options4.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options5.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options6.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options7.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/scripts/options_deprecated.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_base_model.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_input.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_logger.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_model.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_options.py +0 -0
- {nextmv-0.26.3 → nextmv-0.27.0}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.27.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
When working in a notebook environment, we don
|
|
2
|
+
When working in a notebook environment, we don't really create a `main.py` file
|
|
3
3
|
with the main entrypoint of the program. Because the logic is mostly encoded
|
|
4
4
|
inside the `Model` class, we need to create a `main.py` file that we can run in
|
|
5
5
|
Nextmv Cloud. This file is used as that entrypoint. It is not intended for a
|
|
@@ -19,9 +19,7 @@ def main() -> None:
|
|
|
19
19
|
manifest = cloud.Manifest.from_yaml(".")
|
|
20
20
|
|
|
21
21
|
# Load the options from the manifest.
|
|
22
|
-
options =
|
|
23
|
-
if manifest.options is not None:
|
|
24
|
-
options = manifest.extract_options()
|
|
22
|
+
options = manifest.extract_options()
|
|
25
23
|
|
|
26
24
|
# Load the model.
|
|
27
25
|
loaded_model = load_model(
|
|
@@ -29,7 +27,7 @@ def main() -> None:
|
|
|
29
27
|
suppress_warnings=True,
|
|
30
28
|
)
|
|
31
29
|
|
|
32
|
-
# Load the input and solve the model by using mlflow
|
|
30
|
+
# Load the input and solve the model by using mlflow's inference API.
|
|
33
31
|
input = nextmv.load(options=options)
|
|
34
32
|
output = loaded_model.predict(input)
|
|
35
33
|
|
|
@@ -881,7 +881,7 @@ class Application:
|
|
|
881
881
|
instance_id: Optional[str]
|
|
882
882
|
ID of the instance to use for the input set. This is used to
|
|
883
883
|
filter the runs associated with the input set. If not provided,
|
|
884
|
-
the application
|
|
884
|
+
the application's `default_instance_id` is used.
|
|
885
885
|
maximum_runs: Optional[int]
|
|
886
886
|
Maximum number of runs to use for the input set. This is used to
|
|
887
887
|
filter the runs associated with the input set. If not provided,
|
|
@@ -997,7 +997,7 @@ class Application:
|
|
|
997
997
|
description: Optional[str] = None,
|
|
998
998
|
upload_id: Optional[str] = None,
|
|
999
999
|
run_id: Optional[str] = None,
|
|
1000
|
-
format: Optional[Union[Format, dict[str,
|
|
1000
|
+
format: Optional[Union[Format, dict[str, Any]]] = None,
|
|
1001
1001
|
) -> ManagedInput:
|
|
1002
1002
|
"""
|
|
1003
1003
|
Create a new managed input. There are two methods for creating a
|
|
@@ -1076,9 +1076,9 @@ class Application:
|
|
|
1076
1076
|
description: Optional[str] = None,
|
|
1077
1077
|
upload_id: Optional[str] = None,
|
|
1078
1078
|
options: Optional[Union[Options, dict[str, str]]] = None,
|
|
1079
|
-
configuration: Optional[Union[RunConfiguration, dict[str,
|
|
1079
|
+
configuration: Optional[Union[RunConfiguration, dict[str, Any]]] = None,
|
|
1080
1080
|
batch_experiment_id: Optional[str] = None,
|
|
1081
|
-
external_result: Optional[Union[ExternalRunResult, dict[str,
|
|
1081
|
+
external_result: Optional[Union[ExternalRunResult, dict[str, Any]]] = None,
|
|
1082
1082
|
) -> str:
|
|
1083
1083
|
"""
|
|
1084
1084
|
Submit an input to start a new run of the application. Returns the
|
|
@@ -1112,7 +1112,7 @@ class Application:
|
|
|
1112
1112
|
used, the options are extracted from the `.to_cloud_dict()` method.
|
|
1113
1113
|
Note that specifying `options` overrides the `input.options` (if
|
|
1114
1114
|
the `input` is of type `nextmv.Input`).
|
|
1115
|
-
configuration: Optional[Union[RunConfiguration, dict[str,
|
|
1115
|
+
configuration: Optional[Union[RunConfiguration, dict[str, Any]]]
|
|
1116
1116
|
Configuration to use for the run. This can be a
|
|
1117
1117
|
`cloud.RunConfiguration` object or a dict. If the object is used,
|
|
1118
1118
|
then the `.to_dict()` method is applied to extract the
|
|
@@ -1120,7 +1120,7 @@ class Application:
|
|
|
1120
1120
|
batch_experiment_id: Optional[str]
|
|
1121
1121
|
ID of a batch experiment to associate the run with. This is used
|
|
1122
1122
|
when the run is part of a batch experiment.
|
|
1123
|
-
external_result: Optional[Union[ExternalRunResult, dict[str,
|
|
1123
|
+
external_result: Optional[Union[ExternalRunResult, dict[str, Any]]]
|
|
1124
1124
|
External result to use for the run. This can be a
|
|
1125
1125
|
`cloud.ExternalRunResult` object or a dict. If the object is used,
|
|
1126
1126
|
then the `.to_dict()` method is applied to extract the
|
|
@@ -1228,9 +1228,9 @@ class Application:
|
|
|
1228
1228
|
upload_id: Optional[str] = None,
|
|
1229
1229
|
run_options: Optional[Union[Options, dict[str, str]]] = None,
|
|
1230
1230
|
polling_options: PollingOptions = _DEFAULT_POLLING_OPTIONS,
|
|
1231
|
-
configuration: Optional[Union[RunConfiguration, dict[str,
|
|
1231
|
+
configuration: Optional[Union[RunConfiguration, dict[str, Any]]] = None,
|
|
1232
1232
|
batch_experiment_id: Optional[str] = None,
|
|
1233
|
-
external_result: Optional[Union[ExternalRunResult, dict[str,
|
|
1233
|
+
external_result: Optional[Union[ExternalRunResult, dict[str, Any]]] = None,
|
|
1234
1234
|
) -> RunResult:
|
|
1235
1235
|
"""
|
|
1236
1236
|
Submit an input to start a new run of the application and poll for the
|
|
@@ -1271,7 +1271,7 @@ class Application:
|
|
|
1271
1271
|
convenience method that combines the `new_run` and
|
|
1272
1272
|
`run_result_with_polling` methods, applying polling logic to check
|
|
1273
1273
|
when the run succeeded.
|
|
1274
|
-
configuration: Optional[Union[RunConfiguration, dict[str,
|
|
1274
|
+
configuration: Optional[Union[RunConfiguration, dict[str, Any]]]
|
|
1275
1275
|
Configuration to use for the run. This can be a
|
|
1276
1276
|
`cloud.RunConfiguration` object or a dict. If the object is used,
|
|
1277
1277
|
then the `.to_dict()` method is applied to extract the
|
|
@@ -1279,7 +1279,7 @@ class Application:
|
|
|
1279
1279
|
batch_experiment_id: Optional[str]
|
|
1280
1280
|
ID of a batch experiment to associate the run with. This is used
|
|
1281
1281
|
when the run is part of a batch experiment.
|
|
1282
|
-
external_result: Optional[Union[ExternalRunResult, dict[str,
|
|
1282
|
+
external_result: Optional[Union[ExternalRunResult, dict[str, Any]]]
|
|
1283
1283
|
External result to use for the run. This can be a
|
|
1284
1284
|
`cloud.ExternalRunResult` object or a dict. If the object is used,
|
|
1285
1285
|
then the `.to_dict()` method is applied to extract the
|
|
@@ -1555,7 +1555,7 @@ class Application:
|
|
|
1555
1555
|
exception will be raised.
|
|
1556
1556
|
|
|
1557
1557
|
There are two ways to push an app to Nextmv Cloud:
|
|
1558
|
-
1. Specifying `app_dir`, which is the path to an app
|
|
1558
|
+
1. Specifying `app_dir`, which is the path to an app's root directory.
|
|
1559
1559
|
This acts as an external strategy, where the app is composed of files
|
|
1560
1560
|
in a directory and those apps are packaged and pushed to Nextmv Cloud.
|
|
1561
1561
|
2. Specifying a `model` and `model_configuration`. This acts as an
|
|
@@ -1566,7 +1566,7 @@ class Application:
|
|
|
1566
1566
|
Examples
|
|
1567
1567
|
-------
|
|
1568
1568
|
|
|
1569
|
-
1. Push an app using an external strategy, i.e., specifying the app
|
|
1569
|
+
1. Push an app using an external strategy, i.e., specifying the app's
|
|
1570
1570
|
directory:
|
|
1571
1571
|
```python
|
|
1572
1572
|
import os
|
|
@@ -1640,7 +1640,7 @@ class Application:
|
|
|
1640
1640
|
manifest : Optional[Manifest], optional
|
|
1641
1641
|
The manifest for the app, by default None.
|
|
1642
1642
|
app_dir : Optional[str], optional
|
|
1643
|
-
The path to the app
|
|
1643
|
+
The path to the app's directory, by default None.
|
|
1644
1644
|
verbose : bool, optional
|
|
1645
1645
|
Whether to print verbose output, by default False.
|
|
1646
1646
|
"""
|
|
@@ -1793,7 +1793,7 @@ class Application:
|
|
|
1793
1793
|
requests.HTTPError: If the response status code is not 2xx.
|
|
1794
1794
|
"""
|
|
1795
1795
|
|
|
1796
|
-
def polling_func() -> tuple[
|
|
1796
|
+
def polling_func() -> tuple[Any, bool]:
|
|
1797
1797
|
run_information = self.run_metadata(run_id=run_id)
|
|
1798
1798
|
if run_information.metadata.status_v2 in {
|
|
1799
1799
|
StatusV2.succeeded,
|
|
@@ -2327,6 +2327,10 @@ class Application:
|
|
|
2327
2327
|
"runtime": manifest.runtime,
|
|
2328
2328
|
},
|
|
2329
2329
|
}
|
|
2330
|
+
|
|
2331
|
+
if manifest.configuration is not None and manifest.configuration.options is not None:
|
|
2332
|
+
activation_request["requirements"]["options"] = manifest.configuration.options.to_dict()
|
|
2333
|
+
|
|
2330
2334
|
response = self.client.request(
|
|
2331
2335
|
method="PUT",
|
|
2332
2336
|
endpoint=endpoint,
|
|
@@ -2402,11 +2406,11 @@ class Application:
|
|
|
2402
2406
|
raise ValueError(f"Unknown scenario input type: {scenario.scenario_input.scenario_input_type}")
|
|
2403
2407
|
|
|
2404
2408
|
|
|
2405
|
-
def poll(polling_options: PollingOptions, polling_func: Callable[[], tuple[
|
|
2409
|
+
def poll(polling_options: PollingOptions, polling_func: Callable[[], tuple[Any, bool]]) -> Any:
|
|
2406
2410
|
"""
|
|
2407
2411
|
Auxiliary function for polling.
|
|
2408
2412
|
|
|
2409
|
-
The `polling_func` is a callable that must return a `tuple[
|
|
2413
|
+
The `polling_func` is a callable that must return a `tuple[Any, bool]`
|
|
2410
2414
|
where the first element is the result of the polling and the second
|
|
2411
2415
|
element is a boolean indicating if the polling was successful or should be
|
|
2412
2416
|
retried.
|
|
@@ -2424,7 +2428,7 @@ def poll(polling_options: PollingOptions, polling_func: Callable[[], tuple[any,
|
|
|
2424
2428
|
|
|
2425
2429
|
Returns
|
|
2426
2430
|
-------
|
|
2427
|
-
|
|
2431
|
+
Any
|
|
2428
2432
|
Result of the polling function.
|
|
2429
2433
|
"""
|
|
2430
2434
|
|
|
@@ -16,8 +16,19 @@ FILE_NAME = "app.yaml"
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class ManifestType(str, Enum):
|
|
19
|
-
"""
|
|
20
|
-
|
|
19
|
+
"""
|
|
20
|
+
Type of application in the manifest, based on the programming
|
|
21
|
+
language.
|
|
22
|
+
|
|
23
|
+
Attributes
|
|
24
|
+
----------
|
|
25
|
+
PYTHON: str
|
|
26
|
+
Python format
|
|
27
|
+
GO: str
|
|
28
|
+
Go format
|
|
29
|
+
JAVA: str
|
|
30
|
+
Java format
|
|
31
|
+
"""
|
|
21
32
|
|
|
22
33
|
PYTHON = "python"
|
|
23
34
|
"""Python format"""
|
|
@@ -28,7 +39,23 @@ class ManifestType(str, Enum):
|
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
class ManifestRuntime(str, Enum):
|
|
31
|
-
"""
|
|
42
|
+
"""
|
|
43
|
+
Runtime (environment) where the app will be run on Nextmv Cloud.
|
|
44
|
+
|
|
45
|
+
Attributes
|
|
46
|
+
----------
|
|
47
|
+
DEFAULT: str
|
|
48
|
+
This runtime is used to run compiled applications such as Go binaries.
|
|
49
|
+
PYTHON: str
|
|
50
|
+
This runtime is used as the basis for all other Python runtimes and
|
|
51
|
+
Python applications.
|
|
52
|
+
JAVA: str
|
|
53
|
+
This runtime is used to run Java applications.
|
|
54
|
+
PYOMO: str
|
|
55
|
+
This runtime provisions Python packages to run Pyomo applications.
|
|
56
|
+
HEXALY: str
|
|
57
|
+
This runtime provisions Python packages to run Hexaly applications.
|
|
58
|
+
"""
|
|
32
59
|
|
|
33
60
|
DEFAULT = "ghcr.io/nextmv-io/runtime/default:latest"
|
|
34
61
|
"""This runtime is used to run compiled applications such as Go binaries."""
|
|
@@ -49,7 +76,20 @@ class ManifestRuntime(str, Enum):
|
|
|
49
76
|
|
|
50
77
|
|
|
51
78
|
class ManifestBuild(BaseModel):
|
|
52
|
-
"""
|
|
79
|
+
"""
|
|
80
|
+
Build-specific attributes.
|
|
81
|
+
|
|
82
|
+
Attributes
|
|
83
|
+
----------
|
|
84
|
+
command: Optional[str]
|
|
85
|
+
The command to run to build the app. This command will be executed
|
|
86
|
+
without a shell, i.e., directly. The command must exit with a status of
|
|
87
|
+
0 to continue the push process of the app to Nextmv Cloud. This command
|
|
88
|
+
is executed prior to the pre-push command.
|
|
89
|
+
environment: Optional[dict[str, Any]]
|
|
90
|
+
Environment variables to set when running the build command given as
|
|
91
|
+
key-value pairs.
|
|
92
|
+
"""
|
|
53
93
|
|
|
54
94
|
command: Optional[str] = None
|
|
55
95
|
"""
|
|
@@ -82,7 +122,19 @@ class ManifestBuild(BaseModel):
|
|
|
82
122
|
|
|
83
123
|
|
|
84
124
|
class ManifestPythonModel(BaseModel):
|
|
85
|
-
"""
|
|
125
|
+
"""
|
|
126
|
+
Model-specific instructions for a Python app.
|
|
127
|
+
|
|
128
|
+
Attributes
|
|
129
|
+
----------
|
|
130
|
+
name: str
|
|
131
|
+
The name of the decision model.
|
|
132
|
+
options: Optional[list[dict[str, Any]]]
|
|
133
|
+
Options for the decision model. This is a data representation of the
|
|
134
|
+
`nextmv.Options` class. It consists of a list of dicts. Each dict
|
|
135
|
+
represents the `nextmv.Option` class. It is used to be able to
|
|
136
|
+
reconstruct an Options object from data when loading a decision model.
|
|
137
|
+
"""
|
|
86
138
|
|
|
87
139
|
name: str
|
|
88
140
|
"""The name of the decision model."""
|
|
@@ -96,7 +148,18 @@ class ManifestPythonModel(BaseModel):
|
|
|
96
148
|
|
|
97
149
|
|
|
98
150
|
class ManifestPython(BaseModel):
|
|
99
|
-
"""
|
|
151
|
+
"""
|
|
152
|
+
Python-specific instructions.
|
|
153
|
+
|
|
154
|
+
Attributes
|
|
155
|
+
----------
|
|
156
|
+
pip_requirements: Optional[str]
|
|
157
|
+
Path to a requirements.txt file containing (additional) Python
|
|
158
|
+
dependencies that will be bundled with the app.
|
|
159
|
+
model: Optional[ManifestPythonModel]
|
|
160
|
+
Information about an encoded decision model as handlded via mlflow. This
|
|
161
|
+
information is used to load the decision model from the app bundle.
|
|
162
|
+
"""
|
|
100
163
|
|
|
101
164
|
pip_requirements: Optional[str] = Field(
|
|
102
165
|
serialization_alias="pip-requirements",
|
|
@@ -115,7 +178,23 @@ class ManifestPython(BaseModel):
|
|
|
115
178
|
|
|
116
179
|
|
|
117
180
|
class ManifestOption(BaseModel):
|
|
118
|
-
"""
|
|
181
|
+
"""
|
|
182
|
+
An option for the decision model that is recorded in the manifest.
|
|
183
|
+
|
|
184
|
+
Attributes
|
|
185
|
+
----------
|
|
186
|
+
name: str
|
|
187
|
+
The name of the option.
|
|
188
|
+
option_type: str
|
|
189
|
+
The type of the option. This is a string representation of the
|
|
190
|
+
`nextmv.Option` class.
|
|
191
|
+
default: Optional[Any]
|
|
192
|
+
The default value of the option.
|
|
193
|
+
description: Optional[str]
|
|
194
|
+
The description of the option.
|
|
195
|
+
required: bool
|
|
196
|
+
Whether the option is required or not.
|
|
197
|
+
"""
|
|
119
198
|
|
|
120
199
|
name: str
|
|
121
200
|
"""The name of the option"""
|
|
@@ -131,8 +210,13 @@ class ManifestOption(BaseModel):
|
|
|
131
210
|
"""The description of the option"""
|
|
132
211
|
required: bool = False
|
|
133
212
|
"""Whether the option is required or not"""
|
|
134
|
-
|
|
135
|
-
"""
|
|
213
|
+
additional_attributes: Optional[dict[str, Any]] = None
|
|
214
|
+
"""
|
|
215
|
+
Optional additional attributes for the option. The Nextmv Cloud may
|
|
216
|
+
perform validation on these attributes. For example, the maximum length of
|
|
217
|
+
a string or the maximum value of an integer. These additional attributes
|
|
218
|
+
will be shown in the help message of the `Options`.
|
|
219
|
+
"""
|
|
136
220
|
|
|
137
221
|
@classmethod
|
|
138
222
|
def from_option(cls, option: Option) -> "ManifestOption":
|
|
@@ -153,9 +237,9 @@ class ManifestOption(BaseModel):
|
|
|
153
237
|
if option_type is str:
|
|
154
238
|
option_type = "string"
|
|
155
239
|
elif option_type is bool:
|
|
156
|
-
option_type = "
|
|
240
|
+
option_type = "bool"
|
|
157
241
|
elif option_type is int:
|
|
158
|
-
option_type = "
|
|
242
|
+
option_type = "int"
|
|
159
243
|
elif option_type is float:
|
|
160
244
|
option_type = "float"
|
|
161
245
|
else:
|
|
@@ -167,7 +251,7 @@ class ManifestOption(BaseModel):
|
|
|
167
251
|
default=option.default,
|
|
168
252
|
description=option.description,
|
|
169
253
|
required=option.required,
|
|
170
|
-
|
|
254
|
+
additional_attributes=option.additional_attributes,
|
|
171
255
|
)
|
|
172
256
|
|
|
173
257
|
def to_option(self) -> Option:
|
|
@@ -183,9 +267,9 @@ class ManifestOption(BaseModel):
|
|
|
183
267
|
option_type_string = self.option_type
|
|
184
268
|
if option_type_string == "string":
|
|
185
269
|
option_type = str
|
|
186
|
-
elif option_type_string == "
|
|
270
|
+
elif option_type_string == "bool":
|
|
187
271
|
option_type = bool
|
|
188
|
-
elif option_type_string == "
|
|
272
|
+
elif option_type_string == "int":
|
|
189
273
|
option_type = int
|
|
190
274
|
elif option_type_string == "float":
|
|
191
275
|
option_type = float
|
|
@@ -198,10 +282,47 @@ class ManifestOption(BaseModel):
|
|
|
198
282
|
default=self.default,
|
|
199
283
|
description=self.description,
|
|
200
284
|
required=self.required,
|
|
201
|
-
|
|
285
|
+
additional_attributes=self.additional_attributes,
|
|
202
286
|
)
|
|
203
287
|
|
|
204
288
|
|
|
289
|
+
class ManifestOptions(BaseModel):
|
|
290
|
+
"""
|
|
291
|
+
Options for the decision model.
|
|
292
|
+
|
|
293
|
+
Attributes
|
|
294
|
+
----------
|
|
295
|
+
strict: bool
|
|
296
|
+
If strict is set to `True`, only the listed options will be allowed.
|
|
297
|
+
items: list[ManifestOption]
|
|
298
|
+
Optional. The actual list of options for the decision model. An option
|
|
299
|
+
is a parameter that configures the decision model.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
strict: Optional[bool] = False
|
|
303
|
+
"""If strict is set to `True`, only the listed options will be allowed."""
|
|
304
|
+
items: Optional[list[ManifestOption]] = None
|
|
305
|
+
"""
|
|
306
|
+
Optional. The actual list of options for the decision model. An option is a
|
|
307
|
+
parameter that configures the decision model.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class ManifestConfiguration(BaseModel):
|
|
312
|
+
"""
|
|
313
|
+
Configuration for the decision model.
|
|
314
|
+
|
|
315
|
+
Attributes
|
|
316
|
+
----------
|
|
317
|
+
options: ManifestOptions
|
|
318
|
+
Optional. The actual list of options for the decision model. An option
|
|
319
|
+
is a parameter that configures the decision model.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
options: ManifestOptions
|
|
323
|
+
"""Options for the decision model."""
|
|
324
|
+
|
|
325
|
+
|
|
205
326
|
class Manifest(BaseModel):
|
|
206
327
|
"""
|
|
207
328
|
An application that runs on the Nextmv Platform must contain a file named
|
|
@@ -210,6 +331,34 @@ class Manifest(BaseModel):
|
|
|
210
331
|
|
|
211
332
|
This class represents the app manifest and allows you to load it from a
|
|
212
333
|
file or create it programmatically.
|
|
334
|
+
|
|
335
|
+
Attributes
|
|
336
|
+
----------
|
|
337
|
+
files: list[str]
|
|
338
|
+
Mandatory. The files to include (or exclude) in the app.
|
|
339
|
+
runtime: ManifestRuntime
|
|
340
|
+
Mandatory. The runtime to use for the app, it provides the environment
|
|
341
|
+
in which the app runs.
|
|
342
|
+
type: ManifestType
|
|
343
|
+
Mandatory. Type of application, based on the programming language.
|
|
344
|
+
build: Optional[ManifestBuild]
|
|
345
|
+
Optional. Build-specific attributes. The build.command to run to build
|
|
346
|
+
the app. This command will be executed without a shell, i.e., directly.
|
|
347
|
+
The command must exit with a status of 0 to continue the push process of
|
|
348
|
+
the app to Nextmv Cloud. This command is executed prior to the pre-push
|
|
349
|
+
command. The build.environment is used to set environment variables when
|
|
350
|
+
running the build command given as key-value pairs.
|
|
351
|
+
pre_push: Optional[str]
|
|
352
|
+
Optional. A command to run before the app is pushed to the Nextmv Cloud.
|
|
353
|
+
This command can be used to compile a binary, run tests or similar tasks.
|
|
354
|
+
One difference with what is specified under build, is that the command
|
|
355
|
+
will be executed via
|
|
356
|
+
python: Optional[ManifestPython]
|
|
357
|
+
Optional. Only for Python apps. Contains further Python-specific
|
|
358
|
+
attributes.
|
|
359
|
+
configuration: Optional[ManifestConfiguration]
|
|
360
|
+
Optional. A list of options for the decision model. An option is a
|
|
361
|
+
parameter that configures the decision model.
|
|
213
362
|
"""
|
|
214
363
|
|
|
215
364
|
files: list[str]
|
|
@@ -250,7 +399,7 @@ class Manifest(BaseModel):
|
|
|
250
399
|
Optional. Only for Python apps. Contains further Python-specific
|
|
251
400
|
attributes.
|
|
252
401
|
"""
|
|
253
|
-
|
|
402
|
+
configuration: Optional[ManifestConfiguration] = None
|
|
254
403
|
"""
|
|
255
404
|
Optional. A list of options for the decision model. An option is a
|
|
256
405
|
parameter that configures the decision model.
|
|
@@ -292,20 +441,23 @@ class Manifest(BaseModel):
|
|
|
292
441
|
with open(os.path.join(dirpath, FILE_NAME), "w") as file:
|
|
293
442
|
yaml.dump(self.to_dict(), file)
|
|
294
443
|
|
|
295
|
-
def extract_options(self) -> Options:
|
|
444
|
+
def extract_options(self) -> Optional[Options]:
|
|
296
445
|
"""
|
|
297
|
-
Convert the manifest options to a `nextmv.Options` object.
|
|
446
|
+
Convert the manifest options to a `nextmv.Options` object. If the
|
|
447
|
+
manifest does not have valid options defined in
|
|
448
|
+
`.configuration.options.items`, this method simply returns a `None`.
|
|
298
449
|
|
|
299
450
|
Returns
|
|
300
451
|
-------
|
|
301
|
-
Options
|
|
302
|
-
The
|
|
452
|
+
Optional[Options]
|
|
453
|
+
The options extracted from the manifest. If no options are found,
|
|
454
|
+
`None` is returned.
|
|
303
455
|
"""
|
|
304
456
|
|
|
305
|
-
if self.options is None:
|
|
306
|
-
|
|
457
|
+
if self.configuration is None or self.configuration.options is None or self.configuration.options.items is None:
|
|
458
|
+
return None
|
|
307
459
|
|
|
308
|
-
options = [option.to_option() for option in self.options]
|
|
460
|
+
options = [option.to_option() for option in self.configuration.options.items]
|
|
309
461
|
|
|
310
462
|
return Options(*options)
|
|
311
463
|
|
|
@@ -348,7 +500,12 @@ class Manifest(BaseModel):
|
|
|
348
500
|
)
|
|
349
501
|
|
|
350
502
|
if model_configuration.options is not None:
|
|
351
|
-
manifest.
|
|
503
|
+
manifest.configuration = ManifestConfiguration(
|
|
504
|
+
options=ManifestOptions(
|
|
505
|
+
strict=False,
|
|
506
|
+
items=[ManifestOption.from_option(opt) for opt in model_configuration.options.options],
|
|
507
|
+
),
|
|
508
|
+
)
|
|
352
509
|
|
|
353
510
|
return manifest
|
|
354
511
|
|
|
@@ -377,7 +534,12 @@ class Manifest(BaseModel):
|
|
|
377
534
|
runtime=ManifestRuntime.PYTHON,
|
|
378
535
|
type=ManifestType.PYTHON,
|
|
379
536
|
python=ManifestPython(pip_requirements="requirements.txt"),
|
|
380
|
-
|
|
537
|
+
configuration=ManifestConfiguration(
|
|
538
|
+
options=ManifestOptions(
|
|
539
|
+
strict=False,
|
|
540
|
+
items=[ManifestOption.from_option(opt) for opt in options.options],
|
|
541
|
+
),
|
|
542
|
+
),
|
|
381
543
|
)
|
|
382
544
|
|
|
383
545
|
return manifest
|
|
@@ -248,11 +248,11 @@ class TrackedRun:
|
|
|
248
248
|
|
|
249
249
|
Attributes
|
|
250
250
|
----------
|
|
251
|
-
input : Union[Input, dict[str,
|
|
251
|
+
input : Union[Input, dict[str, Any], str]
|
|
252
252
|
The input of the run being tracked. Please note that if the input
|
|
253
253
|
format is JSON, then the input data must be JSON serializable. This
|
|
254
254
|
field is required.
|
|
255
|
-
output : Union[Output, dict[str,
|
|
255
|
+
output : Union[Output, dict[str, Any], str]
|
|
256
256
|
The output of the run being tracked. Please note that if the output
|
|
257
257
|
format is JSON, then the output data must be JSON serializable. This
|
|
258
258
|
field is required.
|
|
@@ -270,9 +270,9 @@ class TrackedRun:
|
|
|
270
270
|
the log. This field is optional.
|
|
271
271
|
"""
|
|
272
272
|
|
|
273
|
-
input: Union[Input, dict[str,
|
|
273
|
+
input: Union[Input, dict[str, Any], str]
|
|
274
274
|
"""The input of the run being tracked."""
|
|
275
|
-
output: Union[Output, dict[str,
|
|
275
|
+
output: Union[Output, dict[str, Any], str]
|
|
276
276
|
"""The output of the run being tracked. Only JSON output_format is supported."""
|
|
277
277
|
status: TrackedRunStatus
|
|
278
278
|
"""The status of the run being tracked"""
|
|
@@ -303,7 +303,7 @@ class TrackedRun:
|
|
|
303
303
|
try:
|
|
304
304
|
_ = json.dumps(self.input)
|
|
305
305
|
except (TypeError, OverflowError) as e:
|
|
306
|
-
raise ValueError("Input is dict[str,
|
|
306
|
+
raise ValueError("Input is dict[str, Any] but it is not JSON serializable") from e
|
|
307
307
|
|
|
308
308
|
if isinstance(self.output, Output):
|
|
309
309
|
if self.output.output_format != OutputFormat.JSON:
|
|
@@ -312,7 +312,7 @@ class TrackedRun:
|
|
|
312
312
|
try:
|
|
313
313
|
_ = json.dumps(self.output)
|
|
314
314
|
except (TypeError, OverflowError) as e:
|
|
315
|
-
raise ValueError("Output is dict[str,
|
|
315
|
+
raise ValueError("Output is dict[str, Any] but it is not JSON serializable") from e
|
|
316
316
|
|
|
317
317
|
def logs_text(self) -> str:
|
|
318
318
|
"""
|
|
@@ -211,7 +211,7 @@ def _option_sets(scenarios: list[Scenario]) -> dict[str, dict[str, dict[str, str
|
|
|
211
211
|
def _scenarios_by_id(scenarios: list[Scenario]) -> dict[str, Scenario]:
|
|
212
212
|
"""
|
|
213
213
|
This function maps a scenario to its ID. A scenario ID is created if it
|
|
214
|
-
wasn
|
|
214
|
+
wasn't defined. This function also checks that there are no duplicate
|
|
215
215
|
scenario IDs.
|
|
216
216
|
"""
|
|
217
217
|
|
|
@@ -92,7 +92,7 @@ class Input:
|
|
|
92
92
|
new_options = copy.deepcopy(init_options)
|
|
93
93
|
self.options = new_options
|
|
94
94
|
|
|
95
|
-
def to_dict(self) -> dict[str,
|
|
95
|
+
def to_dict(self) -> dict[str, Any]:
|
|
96
96
|
"""
|
|
97
97
|
Convert the input to a dictionary.
|
|
98
98
|
|
|
@@ -102,7 +102,7 @@ class Input:
|
|
|
102
102
|
|
|
103
103
|
Returns
|
|
104
104
|
-------
|
|
105
|
-
dict[str,
|
|
105
|
+
dict[str, Any]
|
|
106
106
|
The input as a dictionary.
|
|
107
107
|
"""
|
|
108
108
|
|
|
@@ -47,7 +47,7 @@ warnings.showwarning = custom_showwarning
|
|
|
47
47
|
# the model requires and that we install and bundle with the app.
|
|
48
48
|
_REQUIREMENTS_FILE = "model_requirements.txt"
|
|
49
49
|
|
|
50
|
-
# When working in a notebook environment, we don
|
|
50
|
+
# When working in a notebook environment, we don't really create a `main.py`
|
|
51
51
|
# file with the main entrypoint of the program. Because the logic is mostly
|
|
52
52
|
# encoded inside the `Model` class, we need to create a `main.py` file that we
|
|
53
53
|
# can run in Nextmv Cloud. This file is used as that entrypoint.
|
|
@@ -148,7 +148,7 @@ class Model:
|
|
|
148
148
|
saved and loaded.
|
|
149
149
|
"""
|
|
150
150
|
|
|
151
|
-
# mlflow is a big package. We don
|
|
151
|
+
# mlflow is a big package. We don't want to make it a dependency of
|
|
152
152
|
# `nextmv` because it is not always needed. We only need it if we are
|
|
153
153
|
# working with the "app from model" logic, which involves working with
|
|
154
154
|
# this `Model` class.
|
|
@@ -180,7 +180,7 @@ class Model:
|
|
|
180
180
|
params: Optional[dict[str, Any]] = None,
|
|
181
181
|
) -> Any:
|
|
182
182
|
"""
|
|
183
|
-
The predict method allows us to work with mlflow
|
|
183
|
+
The predict method allows us to work with mlflow's [python_function]
|
|
184
184
|
model flavor. Warning: This method should not be used or overridden
|
|
185
185
|
directly. Instead, you should implement the `solve` method.
|
|
186
186
|
|