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.
Files changed (175) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +8 -13
  3. nextmv/__init__.py +53 -0
  4. nextmv/_serialization.py +96 -0
  5. nextmv/base_model.py +54 -9
  6. nextmv/cli/CONTRIBUTING.md +511 -0
  7. nextmv/cli/__init__.py +0 -0
  8. nextmv/cli/cloud/__init__.py +47 -0
  9. nextmv/cli/cloud/acceptance/__init__.py +27 -0
  10. nextmv/cli/cloud/acceptance/create.py +393 -0
  11. nextmv/cli/cloud/acceptance/delete.py +68 -0
  12. nextmv/cli/cloud/acceptance/get.py +104 -0
  13. nextmv/cli/cloud/acceptance/list.py +62 -0
  14. nextmv/cli/cloud/acceptance/update.py +95 -0
  15. nextmv/cli/cloud/account/__init__.py +28 -0
  16. nextmv/cli/cloud/account/create.py +83 -0
  17. nextmv/cli/cloud/account/delete.py +60 -0
  18. nextmv/cli/cloud/account/get.py +66 -0
  19. nextmv/cli/cloud/account/update.py +70 -0
  20. nextmv/cli/cloud/app/__init__.py +35 -0
  21. nextmv/cli/cloud/app/create.py +141 -0
  22. nextmv/cli/cloud/app/delete.py +58 -0
  23. nextmv/cli/cloud/app/exists.py +44 -0
  24. nextmv/cli/cloud/app/get.py +66 -0
  25. nextmv/cli/cloud/app/list.py +61 -0
  26. nextmv/cli/cloud/app/push.py +137 -0
  27. nextmv/cli/cloud/app/update.py +124 -0
  28. nextmv/cli/cloud/batch/__init__.py +29 -0
  29. nextmv/cli/cloud/batch/create.py +454 -0
  30. nextmv/cli/cloud/batch/delete.py +68 -0
  31. nextmv/cli/cloud/batch/get.py +104 -0
  32. nextmv/cli/cloud/batch/list.py +63 -0
  33. nextmv/cli/cloud/batch/metadata.py +66 -0
  34. nextmv/cli/cloud/batch/update.py +95 -0
  35. nextmv/cli/cloud/data/__init__.py +26 -0
  36. nextmv/cli/cloud/data/upload.py +162 -0
  37. nextmv/cli/cloud/ensemble/__init__.py +31 -0
  38. nextmv/cli/cloud/ensemble/create.py +414 -0
  39. nextmv/cli/cloud/ensemble/delete.py +67 -0
  40. nextmv/cli/cloud/ensemble/get.py +65 -0
  41. nextmv/cli/cloud/ensemble/update.py +103 -0
  42. nextmv/cli/cloud/input_set/__init__.py +30 -0
  43. nextmv/cli/cloud/input_set/create.py +170 -0
  44. nextmv/cli/cloud/input_set/get.py +63 -0
  45. nextmv/cli/cloud/input_set/list.py +63 -0
  46. nextmv/cli/cloud/input_set/update.py +123 -0
  47. nextmv/cli/cloud/instance/__init__.py +35 -0
  48. nextmv/cli/cloud/instance/create.py +290 -0
  49. nextmv/cli/cloud/instance/delete.py +62 -0
  50. nextmv/cli/cloud/instance/exists.py +39 -0
  51. nextmv/cli/cloud/instance/get.py +62 -0
  52. nextmv/cli/cloud/instance/list.py +60 -0
  53. nextmv/cli/cloud/instance/update.py +216 -0
  54. nextmv/cli/cloud/managed_input/__init__.py +31 -0
  55. nextmv/cli/cloud/managed_input/create.py +146 -0
  56. nextmv/cli/cloud/managed_input/delete.py +65 -0
  57. nextmv/cli/cloud/managed_input/get.py +63 -0
  58. nextmv/cli/cloud/managed_input/list.py +60 -0
  59. nextmv/cli/cloud/managed_input/update.py +97 -0
  60. nextmv/cli/cloud/run/__init__.py +37 -0
  61. nextmv/cli/cloud/run/cancel.py +37 -0
  62. nextmv/cli/cloud/run/create.py +530 -0
  63. nextmv/cli/cloud/run/get.py +199 -0
  64. nextmv/cli/cloud/run/input.py +86 -0
  65. nextmv/cli/cloud/run/list.py +80 -0
  66. nextmv/cli/cloud/run/logs.py +167 -0
  67. nextmv/cli/cloud/run/metadata.py +67 -0
  68. nextmv/cli/cloud/run/track.py +501 -0
  69. nextmv/cli/cloud/scenario/__init__.py +29 -0
  70. nextmv/cli/cloud/scenario/create.py +451 -0
  71. nextmv/cli/cloud/scenario/delete.py +65 -0
  72. nextmv/cli/cloud/scenario/get.py +102 -0
  73. nextmv/cli/cloud/scenario/list.py +63 -0
  74. nextmv/cli/cloud/scenario/metadata.py +67 -0
  75. nextmv/cli/cloud/scenario/update.py +93 -0
  76. nextmv/cli/cloud/secrets/__init__.py +33 -0
  77. nextmv/cli/cloud/secrets/create.py +206 -0
  78. nextmv/cli/cloud/secrets/delete.py +67 -0
  79. nextmv/cli/cloud/secrets/get.py +66 -0
  80. nextmv/cli/cloud/secrets/list.py +60 -0
  81. nextmv/cli/cloud/secrets/update.py +147 -0
  82. nextmv/cli/cloud/shadow/__init__.py +33 -0
  83. nextmv/cli/cloud/shadow/create.py +184 -0
  84. nextmv/cli/cloud/shadow/delete.py +68 -0
  85. nextmv/cli/cloud/shadow/get.py +61 -0
  86. nextmv/cli/cloud/shadow/list.py +63 -0
  87. nextmv/cli/cloud/shadow/metadata.py +66 -0
  88. nextmv/cli/cloud/shadow/start.py +43 -0
  89. nextmv/cli/cloud/shadow/stop.py +43 -0
  90. nextmv/cli/cloud/shadow/update.py +95 -0
  91. nextmv/cli/cloud/upload/__init__.py +22 -0
  92. nextmv/cli/cloud/upload/create.py +39 -0
  93. nextmv/cli/cloud/version/__init__.py +33 -0
  94. nextmv/cli/cloud/version/create.py +97 -0
  95. nextmv/cli/cloud/version/delete.py +62 -0
  96. nextmv/cli/cloud/version/exists.py +39 -0
  97. nextmv/cli/cloud/version/get.py +62 -0
  98. nextmv/cli/cloud/version/list.py +60 -0
  99. nextmv/cli/cloud/version/update.py +92 -0
  100. nextmv/cli/community/__init__.py +24 -0
  101. nextmv/cli/community/clone.py +270 -0
  102. nextmv/cli/community/list.py +265 -0
  103. nextmv/cli/configuration/__init__.py +23 -0
  104. nextmv/cli/configuration/config.py +195 -0
  105. nextmv/cli/configuration/create.py +94 -0
  106. nextmv/cli/configuration/delete.py +67 -0
  107. nextmv/cli/configuration/list.py +77 -0
  108. nextmv/cli/main.py +188 -0
  109. nextmv/cli/message.py +153 -0
  110. nextmv/cli/options.py +206 -0
  111. nextmv/cli/version.py +38 -0
  112. nextmv/cloud/__init__.py +71 -17
  113. nextmv/cloud/acceptance_test.py +757 -51
  114. nextmv/cloud/account.py +406 -17
  115. nextmv/cloud/application/__init__.py +957 -0
  116. nextmv/cloud/application/_acceptance.py +419 -0
  117. nextmv/cloud/application/_batch_scenario.py +860 -0
  118. nextmv/cloud/application/_ensemble.py +251 -0
  119. nextmv/cloud/application/_input_set.py +227 -0
  120. nextmv/cloud/application/_instance.py +289 -0
  121. nextmv/cloud/application/_managed_input.py +227 -0
  122. nextmv/cloud/application/_run.py +1393 -0
  123. nextmv/cloud/application/_secrets.py +294 -0
  124. nextmv/cloud/application/_shadow.py +314 -0
  125. nextmv/cloud/application/_utils.py +54 -0
  126. nextmv/cloud/application/_version.py +303 -0
  127. nextmv/cloud/assets.py +48 -0
  128. nextmv/cloud/batch_experiment.py +294 -33
  129. nextmv/cloud/client.py +307 -66
  130. nextmv/cloud/ensemble.py +247 -0
  131. nextmv/cloud/input_set.py +120 -2
  132. nextmv/cloud/instance.py +133 -8
  133. nextmv/cloud/integration.py +533 -0
  134. nextmv/cloud/package.py +168 -53
  135. nextmv/cloud/scenario.py +410 -0
  136. nextmv/cloud/secrets.py +234 -0
  137. nextmv/cloud/shadow.py +190 -0
  138. nextmv/cloud/url.py +73 -0
  139. nextmv/cloud/version.py +132 -4
  140. nextmv/default_app/.gitignore +1 -0
  141. nextmv/default_app/README.md +32 -0
  142. nextmv/default_app/app.yaml +12 -0
  143. nextmv/default_app/input.json +5 -0
  144. nextmv/default_app/main.py +37 -0
  145. nextmv/default_app/requirements.txt +2 -0
  146. nextmv/default_app/src/__init__.py +0 -0
  147. nextmv/default_app/src/visuals.py +36 -0
  148. nextmv/deprecated.py +47 -0
  149. nextmv/input.py +861 -90
  150. nextmv/local/__init__.py +5 -0
  151. nextmv/local/application.py +1251 -0
  152. nextmv/local/executor.py +1042 -0
  153. nextmv/local/geojson_handler.py +323 -0
  154. nextmv/local/local.py +97 -0
  155. nextmv/local/plotly_handler.py +61 -0
  156. nextmv/local/runner.py +274 -0
  157. nextmv/logger.py +80 -9
  158. nextmv/manifest.py +1466 -0
  159. nextmv/model.py +241 -66
  160. nextmv/options.py +708 -115
  161. nextmv/output.py +1301 -274
  162. nextmv/polling.py +325 -0
  163. nextmv/run.py +1702 -0
  164. nextmv/safe.py +145 -0
  165. nextmv/status.py +122 -0
  166. nextmv-1.0.0.dev2.dist-info/METADATA +311 -0
  167. nextmv-1.0.0.dev2.dist-info/RECORD +170 -0
  168. {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/WHEEL +1 -1
  169. nextmv-1.0.0.dev2.dist-info/entry_points.txt +2 -0
  170. nextmv/cloud/application.py +0 -1405
  171. nextmv/cloud/manifest.py +0 -234
  172. nextmv/cloud/status.py +0 -29
  173. nextmv-0.18.0.dist-info/METADATA +0 -770
  174. nextmv-0.18.0.dist-info/RECORD +0 -25
  175. {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, Optional
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
- def custom_showwarning(message, category, filename, lineno, file=None, line=None):
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
- original_showwarning(message, category, filename, lineno, file, line)
87
+ _original_showwarning(message, category, filename, lineno, file, line)
37
88
 
38
89
 
39
- warnings.showwarning = custom_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 dont really create a `main.py`
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
- ModelConfiguration is a class that holds the configuration for a
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 may specify the `requirements` that your decision model requires. This
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
- Lastly, if your decision model requires options, you may specify them by
72
- passing an instance of `Options`.
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: Optional[Options] = None
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
- Model is the base class for defining a decision model that runs in Nextmv
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
- # Define the model that makes decisions. This model uses the Nextroute library
100
- # to solve a routing problem.
101
- class DecisionModel(nextmv.Model):
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
- return nextmv.Output(
108
- options=input.options,
109
- solution=nextroute_output.solutions[0].to_dict(),
110
- statistics=nextroute_output.statistics.to_dict(),
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
- The `solve` method is the main entry point of your model. You must
118
- implement this method yourself. It receives a `nextmv.Input` and should
119
- process it to produce a `nextmv.Output`, which is the solution to the
120
- decision model/problem.
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, in the location given by `dir`.
139
- The model is saved according to the configuration provided, which is of
140
- type `ModelConfiguration`.
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
- dir : str
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 dont want to make it a dependency of
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[all]`"
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
- The `MLFlowModel` class exists as a transient class to translate a
169
- Nextmv `DecisionModel` into an `mlflow.pyfunc.PythonModel`. This
170
- class must comply with the inference API of mlflow, which is why it
171
- has a `predict` method. The translation happens by having this
172
- `predict` method call the user-defined `solve` method of the
173
- `DecisionModel`.
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: Optional[dict[str, Any]] = None,
311
+ params: dict[str, Any] | None = None,
181
312
  ) -> Any:
182
313
  """
183
- The predict method allows us to work with mlflow’s [python_function]
184
- model flavor. Warning: This method should not be used or overridden
185
- directly. Instead, you should implement the `solve` method.
186
-
187
- [python_function]: https://mlflow.org/docs/latest/python_api/mlflow.pyfunc.html
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: Optional[ModelConfiguration] = None,
379
+ model_configuration: ModelConfiguration | None = None,
232
380
  verbose: bool = False,
233
381
  ) -> None:
234
- """Cleans up the Python-specific model packaging logic."""
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