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/model.py
CHANGED
|
@@ -1,22 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Model module for creating and saving decision models in Nextmv Cloud.
|
|
3
|
+
|
|
4
|
+
This module provides the base classes and functionality for creating decision models
|
|
5
|
+
that can be deployed and run in Nextmv Cloud. The main components are:
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
Model
|
|
10
|
+
Base class for defining decision models.
|
|
11
|
+
ModelConfiguration
|
|
12
|
+
Configuration for packaging and deploying models.
|
|
13
|
+
|
|
14
|
+
Models defined using this module can be packaged with their dependencies and
|
|
15
|
+
deployed to Nextmv Cloud for execution.
|
|
16
|
+
"""
|
|
17
|
+
|
|
1
18
|
import logging
|
|
2
19
|
import os
|
|
3
20
|
import shutil
|
|
4
21
|
import warnings
|
|
5
22
|
from dataclasses import dataclass
|
|
6
|
-
from typing import Any
|
|
23
|
+
from typing import Any
|
|
7
24
|
|
|
8
25
|
from nextmv.input import Input
|
|
9
26
|
from nextmv.logger import log
|
|
10
|
-
from nextmv.options import Options
|
|
27
|
+
from nextmv.options import Options, OptionsEnforcement
|
|
11
28
|
from nextmv.output import Output
|
|
12
29
|
|
|
13
30
|
# The following block of code is used to suppress warnings from mlflow. We
|
|
14
31
|
# suppress these warnings because they are not relevant to the user, and they
|
|
15
32
|
# are not actionable.
|
|
16
|
-
original_showwarning = warnings.showwarning
|
|
17
33
|
|
|
34
|
+
"""
|
|
35
|
+
Module-level function and variable to suppress warnings from mlflow.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
_original_showwarning = warnings.showwarning
|
|
39
|
+
"""Original showwarning function from the warnings module."""
|
|
18
40
|
|
|
19
|
-
|
|
41
|
+
|
|
42
|
+
def _custom_showwarning(message, category, filename, lineno, file=None, line=None):
|
|
43
|
+
"""
|
|
44
|
+
Custom warning handler that suppresses specific mlflow warnings.
|
|
45
|
+
|
|
46
|
+
This function filters out non-actionable warnings from the mlflow library
|
|
47
|
+
to keep the console output clean and relevant for the user.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
message : str
|
|
52
|
+
The warning message.
|
|
53
|
+
category : Warning
|
|
54
|
+
The warning category.
|
|
55
|
+
filename : str
|
|
56
|
+
The filename where the warning was raised.
|
|
57
|
+
lineno : int
|
|
58
|
+
The line number where the warning was raised.
|
|
59
|
+
file : file, optional
|
|
60
|
+
The file to write the warning to.
|
|
61
|
+
line : str, optional
|
|
62
|
+
The line of source code to be included in the warning message.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
None
|
|
67
|
+
If the warning matches certain patterns, the function returns early
|
|
68
|
+
without showing the warning. Otherwise, it delegates to the original
|
|
69
|
+
warning handler.
|
|
70
|
+
"""
|
|
20
71
|
# .../site-packages/mlflow/pyfunc/utils/data_validation.py:134: UserWarning:Add
|
|
21
72
|
# type hints to the `predict` method to enable data validation and automatic
|
|
22
73
|
# signature inference during model logging. Check
|
|
@@ -33,10 +84,10 @@ def custom_showwarning(message, category, filename, lineno, file=None, line=None
|
|
|
33
84
|
if "mlflow/pyfunc/__init__.py" in filename:
|
|
34
85
|
return
|
|
35
86
|
|
|
36
|
-
|
|
87
|
+
_original_showwarning(message, category, filename, lineno, file, line)
|
|
37
88
|
|
|
38
89
|
|
|
39
|
-
warnings.showwarning =
|
|
90
|
+
warnings.showwarning = _custom_showwarning
|
|
40
91
|
|
|
41
92
|
# When working with the `Model`, we expect to be working in a notebook
|
|
42
93
|
# environment, and not interact with the local filesystem a lot. We use the
|
|
@@ -47,77 +98,114 @@ warnings.showwarning = custom_showwarning
|
|
|
47
98
|
# the model requires and that we install and bundle with the app.
|
|
48
99
|
_REQUIREMENTS_FILE = "model_requirements.txt"
|
|
49
100
|
|
|
50
|
-
# When working in a notebook environment, we don
|
|
101
|
+
# When working in a notebook environment, we don't really create a `main.py`
|
|
51
102
|
# file with the main entrypoint of the program. Because the logic is mostly
|
|
52
103
|
# encoded inside the `Model` class, we need to create a `main.py` file that we
|
|
53
104
|
# can run in Nextmv Cloud. This file is used as that entrypoint.
|
|
54
105
|
_ENTRYPOINT_FILE = "__entrypoint__.py"
|
|
55
106
|
|
|
107
|
+
|
|
108
|
+
# Required mlflow dependency version for model packaging.
|
|
56
109
|
_MLFLOW_DEPENDENCY = "mlflow>=2.18.0"
|
|
57
110
|
|
|
58
111
|
|
|
59
112
|
@dataclass
|
|
60
113
|
class ModelConfiguration:
|
|
61
114
|
"""
|
|
62
|
-
|
|
63
|
-
model. It is used to define how a Python model is encoded and loaded.
|
|
64
|
-
|
|
65
|
-
The `name` is required, and should be a personalized name for the model.
|
|
115
|
+
Configuration class for Nextmv models.
|
|
66
116
|
|
|
67
|
-
You
|
|
68
|
-
is done by passing a list of requirements, as if they were lines in a
|
|
69
|
-
`requirements.txt` file. An example of this is `["nextmv==0.1.0"]`.
|
|
117
|
+
You can import the `ModelConfiguration` class directly from `nextmv`:
|
|
70
118
|
|
|
71
|
-
|
|
72
|
-
|
|
119
|
+
```python
|
|
120
|
+
from nextmv import ModelConfiguration
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This class holds the configuration for a model, defining how a Python model
|
|
124
|
+
is encoded and loaded for use in Nextmv Cloud.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
name : str
|
|
129
|
+
A personalized name for the model. This is required.
|
|
130
|
+
requirements : list[str], optional
|
|
131
|
+
A list of Python dependencies that the decision model requires,
|
|
132
|
+
formatted as they would appear in a requirements.txt file.
|
|
133
|
+
options : Options, optional
|
|
134
|
+
Options that the decision model requires.
|
|
135
|
+
options_enforcement:
|
|
136
|
+
Enforcement of options for the model. This controls how options
|
|
137
|
+
are handled when the model is run.
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> from nextmv import ModelConfiguration, Options
|
|
142
|
+
>>> config = ModelConfiguration(
|
|
143
|
+
... name="my_routing_model",
|
|
144
|
+
... requirements=["nextroute>=1.0.0"],
|
|
145
|
+
... options=Options({"max_time": 60}),
|
|
146
|
+
... options_enforcement=OptionsEnforcement(
|
|
147
|
+
strict=True,
|
|
148
|
+
validation_enforce=True
|
|
149
|
+
)
|
|
150
|
+
... )
|
|
73
151
|
"""
|
|
74
152
|
|
|
75
153
|
name: str
|
|
76
154
|
"""The name of the decision model."""
|
|
77
|
-
|
|
78
|
-
requirements: Optional[list[str]] = None
|
|
155
|
+
requirements: list[str] | None = None
|
|
79
156
|
"""A list of Python dependencies that the decision model requires."""
|
|
80
|
-
options:
|
|
157
|
+
options: Options | None = None
|
|
81
158
|
"""Options that the decision model requires."""
|
|
159
|
+
options_enforcement: OptionsEnforcement | None = None
|
|
160
|
+
"""Enforcement of options for the model."""
|
|
82
161
|
|
|
83
162
|
|
|
84
163
|
class Model:
|
|
85
164
|
"""
|
|
86
|
-
|
|
87
|
-
Cloud. You must create a subclass of this class and implement the `solve`
|
|
88
|
-
method. The `solve` method is the main entry point of your model and should
|
|
89
|
-
return an output with a solution (decision).
|
|
90
|
-
|
|
91
|
-
Example
|
|
92
|
-
-------
|
|
93
|
-
```python
|
|
94
|
-
import nextroute
|
|
165
|
+
Base class for defining decision models that run in Nextmv Cloud.
|
|
95
166
|
|
|
96
|
-
import nextmv
|
|
167
|
+
You can import the `Model` class directly from `nextmv`:
|
|
97
168
|
|
|
169
|
+
```python
|
|
170
|
+
from nextmv import Model
|
|
171
|
+
```
|
|
98
172
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def solve(self, input: nextmv.Input) -> nextmv.Output:
|
|
103
|
-
nextroute_input = nextroute.schema.Input.from_dict(input.data)
|
|
104
|
-
nextroute_options = nextroute.Options.extract_from_dict(input.options.to_dict())
|
|
105
|
-
nextroute_output = nextroute.solve(nextroute_input, nextroute_options)
|
|
173
|
+
This class serves as a foundation for creating decision models that can be
|
|
174
|
+
deployed to Nextmv Cloud. Subclasses must implement the `solve` method,
|
|
175
|
+
which is the main entry point for processing inputs and producing decisions.
|
|
106
176
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
177
|
+
Methods
|
|
178
|
+
-------
|
|
179
|
+
solve(input)
|
|
180
|
+
Process input data and produce a decision output.
|
|
181
|
+
save(model_dir, configuration)
|
|
182
|
+
Save the model to the filesystem for deployment.
|
|
183
|
+
|
|
184
|
+
Examples
|
|
185
|
+
--------
|
|
186
|
+
>>> import nextroute
|
|
187
|
+
>>> import nextmv
|
|
188
|
+
>>>
|
|
189
|
+
>>> class DecisionModel(nextmv.Model):
|
|
190
|
+
... def solve(self, input: nextmv.Input) -> nextmv.Output:
|
|
191
|
+
... nextroute_input = nextroute.schema.Input.from_dict(input.data)
|
|
192
|
+
... nextroute_options = nextroute.Options.extract_from_dict(input.options.to_dict())
|
|
193
|
+
... nextroute_output = nextroute.solve(nextroute_input, nextroute_options)
|
|
194
|
+
...
|
|
195
|
+
... return nextmv.Output(
|
|
196
|
+
... options=input.options,
|
|
197
|
+
... solution=nextroute_output.solutions[0].to_dict(),
|
|
198
|
+
... statistics=nextroute_output.statistics.to_dict(),
|
|
199
|
+
... )
|
|
113
200
|
"""
|
|
114
201
|
|
|
115
202
|
def solve(self, input: Input) -> Output:
|
|
116
203
|
"""
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
204
|
+
Process input data and produce a decision output.
|
|
205
|
+
|
|
206
|
+
This is the main entry point of your model that you must implement in
|
|
207
|
+
subclasses. It receives input data and should process it to produce an
|
|
208
|
+
output containing the solution to the decision problem.
|
|
121
209
|
|
|
122
210
|
Parameters
|
|
123
211
|
----------
|
|
@@ -129,26 +217,66 @@ class Model:
|
|
|
129
217
|
Output
|
|
130
218
|
The output of the model, which is the solution to the decision
|
|
131
219
|
model/problem.
|
|
220
|
+
|
|
221
|
+
Raises
|
|
222
|
+
------
|
|
223
|
+
NotImplementedError
|
|
224
|
+
When called on the base Model class, as this method must be
|
|
225
|
+
implemented by subclasses.
|
|
226
|
+
|
|
227
|
+
Examples
|
|
228
|
+
--------
|
|
229
|
+
>>> def solve(self, input: Input) -> Output:
|
|
230
|
+
... # Process input data
|
|
231
|
+
... result = self._process_data(input.data)
|
|
232
|
+
...
|
|
233
|
+
... # Return formatted output
|
|
234
|
+
... return Output(
|
|
235
|
+
... options=input.options,
|
|
236
|
+
... solution=result,
|
|
237
|
+
... statistics={"processing_time": 0.5}
|
|
238
|
+
... )
|
|
132
239
|
"""
|
|
133
240
|
|
|
134
241
|
raise NotImplementedError
|
|
135
242
|
|
|
136
243
|
def save(model_self, model_dir: str, configuration: ModelConfiguration) -> None:
|
|
137
244
|
"""
|
|
138
|
-
Save the model to the local filesystem
|
|
139
|
-
|
|
140
|
-
|
|
245
|
+
Save the model to the local filesystem for deployment.
|
|
246
|
+
|
|
247
|
+
This method packages the model according to the provided configuration,
|
|
248
|
+
creating all necessary files and dependencies for deployment to Nextmv
|
|
249
|
+
Cloud.
|
|
141
250
|
|
|
142
251
|
Parameters
|
|
143
252
|
----------
|
|
144
|
-
|
|
253
|
+
model_dir : str
|
|
145
254
|
The directory where the model will be saved.
|
|
146
255
|
configuration : ModelConfiguration
|
|
147
256
|
The configuration of the model, which defines how the model is
|
|
148
257
|
saved and loaded.
|
|
258
|
+
|
|
259
|
+
Raises
|
|
260
|
+
------
|
|
261
|
+
ImportError
|
|
262
|
+
If mlflow is not installed, which is required for model packaging.
|
|
263
|
+
|
|
264
|
+
Notes
|
|
265
|
+
-----
|
|
266
|
+
This method uses mlflow for model packaging, creating the necessary
|
|
267
|
+
files and directory structure for deployment.
|
|
268
|
+
|
|
269
|
+
Examples
|
|
270
|
+
--------
|
|
271
|
+
>>> model = MyDecisionModel()
|
|
272
|
+
>>> config = ModelConfiguration(
|
|
273
|
+
... name="routing_model",
|
|
274
|
+
... requirements=["pandas", "numpy"]
|
|
275
|
+
... )
|
|
276
|
+
>>> model.save("/tmp/my_model", config)
|
|
149
277
|
"""
|
|
150
278
|
|
|
151
|
-
# mlflow is a big package. We don
|
|
279
|
+
# mlflow is a big package. We don't want to make it a dependency of
|
|
152
280
|
# `nextmv` because it is not always needed. We only need it if we are
|
|
153
281
|
# working with the "app from model" logic, which involves working with
|
|
154
282
|
# this `Model` class.
|
|
@@ -156,7 +284,7 @@ class Model:
|
|
|
156
284
|
import mlflow as mlflow
|
|
157
285
|
except ImportError as e:
|
|
158
286
|
raise ImportError(
|
|
159
|
-
"mlflow is not installed. Please install optional dependencies with `pip install nextmv[
|
|
287
|
+
"mlflow is not installed. Please install optional dependencies with `pip install nextmv[notebook]`"
|
|
160
288
|
) from e
|
|
161
289
|
|
|
162
290
|
finally:
|
|
@@ -165,26 +293,46 @@ class Model:
|
|
|
165
293
|
|
|
166
294
|
class MLFlowModel(PythonModel):
|
|
167
295
|
"""
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
296
|
+
Transient class to translate a Nextmv Decision Model into an MLflow PythonModel.
|
|
297
|
+
|
|
298
|
+
This class complies with the MLflow inference API, implementing a `predict`
|
|
299
|
+
method that calls the user-defined `solve` method of the Nextmv Decision Model.
|
|
300
|
+
|
|
301
|
+
Methods
|
|
302
|
+
-------
|
|
303
|
+
predict(context, model_input, params)
|
|
304
|
+
MLflow-compliant predict method that delegates to the Nextmv model's solve method.
|
|
174
305
|
"""
|
|
175
306
|
|
|
176
307
|
def predict(
|
|
177
308
|
self,
|
|
178
309
|
context,
|
|
179
310
|
model_input,
|
|
180
|
-
params:
|
|
311
|
+
params: dict[str, Any] | None = None,
|
|
181
312
|
) -> Any:
|
|
182
313
|
"""
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
314
|
+
MLflow-compliant prediction method that calls the Nextmv model's solve method.
|
|
315
|
+
|
|
316
|
+
This method enables compatibility with MLflow's python_function model flavor.
|
|
317
|
+
|
|
318
|
+
Parameters
|
|
319
|
+
----------
|
|
320
|
+
context : mlflow.pyfunc.PythonModelContext
|
|
321
|
+
The MLflow model context.
|
|
322
|
+
model_input : Any
|
|
323
|
+
The input data for prediction, passed to the solve method.
|
|
324
|
+
params : Optional[dict[str, Any]], optional
|
|
325
|
+
Additional parameters for prediction.
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
Any
|
|
330
|
+
The result from the Nextmv model's solve method.
|
|
331
|
+
|
|
332
|
+
Notes
|
|
333
|
+
-----
|
|
334
|
+
This method should not be used or overridden directly. Instead,
|
|
335
|
+
implement the `solve` method in your Nextmv Model subclass.
|
|
188
336
|
"""
|
|
189
337
|
|
|
190
338
|
return model_self.solve(model_input)
|
|
@@ -228,10 +376,37 @@ class Model:
|
|
|
228
376
|
|
|
229
377
|
def _cleanup_python_model(
|
|
230
378
|
model_dir: str,
|
|
231
|
-
model_configuration:
|
|
379
|
+
model_configuration: ModelConfiguration | None = None,
|
|
232
380
|
verbose: bool = False,
|
|
233
381
|
) -> None:
|
|
234
|
-
"""
|
|
382
|
+
"""
|
|
383
|
+
Clean up Python-specific model packaging artifacts.
|
|
384
|
+
|
|
385
|
+
This function removes temporary files and directories created during the
|
|
386
|
+
model packaging process.
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
model_dir : str
|
|
391
|
+
The directory where the model was saved.
|
|
392
|
+
model_configuration : Optional[ModelConfiguration], optional
|
|
393
|
+
The configuration of the model. If None, the function returns early.
|
|
394
|
+
verbose : bool, default=False
|
|
395
|
+
If True, log a message when cleanup is complete.
|
|
396
|
+
|
|
397
|
+
Returns
|
|
398
|
+
-------
|
|
399
|
+
None
|
|
400
|
+
This function does not return anything.
|
|
401
|
+
|
|
402
|
+
Notes
|
|
403
|
+
-----
|
|
404
|
+
Files and directories removed include:
|
|
405
|
+
- The model directory itself
|
|
406
|
+
- The mlruns directory created by MLflow
|
|
407
|
+
- The requirements file
|
|
408
|
+
- The main.py file
|
|
409
|
+
"""
|
|
235
410
|
|
|
236
411
|
if model_configuration is None:
|
|
237
412
|
return
|