mlbundle 0.1.0a1__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.

Potentially problematic release.


This version of mlbundle might be problematic. Click here for more details.

Files changed (91) hide show
  1. mlbundle-0.1.0a1/LICENSE +21 -0
  2. mlbundle-0.1.0a1/PKG-INFO +347 -0
  3. mlbundle-0.1.0a1/README.md +297 -0
  4. mlbundle-0.1.0a1/pyproject.toml +254 -0
  5. mlbundle-0.1.0a1/pyproject.toml.orig +214 -0
  6. mlbundle-0.1.0a1/src/mlbundle/__init__.py +82 -0
  7. mlbundle-0.1.0a1/src/mlbundle/_base.py +19 -0
  8. mlbundle-0.1.0a1/src/mlbundle/_canonical_work.py +67 -0
  9. mlbundle-0.1.0a1/src/mlbundle/_catboost/__init__.py +159 -0
  10. mlbundle-0.1.0a1/src/mlbundle/_catboost/budget.py +22 -0
  11. mlbundle-0.1.0a1/src/mlbundle/_catboost/flatbuffer.py +104 -0
  12. mlbundle-0.1.0a1/src/mlbundle/_catboost/native.py +244 -0
  13. mlbundle-0.1.0a1/src/mlbundle/_catboost/prediction.py +67 -0
  14. mlbundle-0.1.0a1/src/mlbundle/_catboost/scope.py +109 -0
  15. mlbundle-0.1.0a1/src/mlbundle/_catboost/state.py +62 -0
  16. mlbundle-0.1.0a1/src/mlbundle/_composite/__init__.py +1 -0
  17. mlbundle-0.1.0a1/src/mlbundle/_composite/claims.py +133 -0
  18. mlbundle-0.1.0a1/src/mlbundle/_composite/io.py +158 -0
  19. mlbundle-0.1.0a1/src/mlbundle/_composite/prediction.py +77 -0
  20. mlbundle-0.1.0a1/src/mlbundle/_container_rules.py +73 -0
  21. mlbundle-0.1.0a1/src/mlbundle/_export_manifest.py +78 -0
  22. mlbundle-0.1.0a1/src/mlbundle/_native_io.py +53 -0
  23. mlbundle-0.1.0a1/src/mlbundle/_onnx/__init__.py +115 -0
  24. mlbundle-0.1.0a1/src/mlbundle/_onnx/_wire_runs.py +103 -0
  25. mlbundle-0.1.0a1/src/mlbundle/_onnx/description.py +252 -0
  26. mlbundle-0.1.0a1/src/mlbundle/_onnx/prediction.py +136 -0
  27. mlbundle-0.1.0a1/src/mlbundle/_onnx/protobuf.py +111 -0
  28. mlbundle-0.1.0a1/src/mlbundle/_onnx/session.py +87 -0
  29. mlbundle-0.1.0a1/src/mlbundle/_onnx/signatures.py +63 -0
  30. mlbundle-0.1.0a1/src/mlbundle/_onnx/validation.py +164 -0
  31. mlbundle-0.1.0a1/src/mlbundle/_pipeline_contract.py +104 -0
  32. mlbundle-0.1.0a1/src/mlbundle/_prediction_input.py +63 -0
  33. mlbundle-0.1.0a1/src/mlbundle/_schema_fields.py +68 -0
  34. mlbundle-0.1.0a1/src/mlbundle/_sklearn/__init__.py +183 -0
  35. mlbundle-0.1.0a1/src/mlbundle/_sklearn/_width_checks.py +59 -0
  36. mlbundle-0.1.0a1/src/mlbundle/_sklearn/archive.py +32 -0
  37. mlbundle-0.1.0a1/src/mlbundle/_sklearn/arrays.py +150 -0
  38. mlbundle-0.1.0a1/src/mlbundle/_sklearn/column_width.py +198 -0
  39. mlbundle-0.1.0a1/src/mlbundle/_sklearn/consistency.py +127 -0
  40. mlbundle-0.1.0a1/src/mlbundle/_sklearn/dtypes.py +41 -0
  41. mlbundle-0.1.0a1/src/mlbundle/_sklearn/encoder_width.py +123 -0
  42. mlbundle-0.1.0a1/src/mlbundle/_sklearn/fitted.py +113 -0
  43. mlbundle-0.1.0a1/src/mlbundle/_sklearn/labels.py +52 -0
  44. mlbundle-0.1.0a1/src/mlbundle/_sklearn/nodes.py +75 -0
  45. mlbundle-0.1.0a1/src/mlbundle/_sklearn/parameters.py +94 -0
  46. mlbundle-0.1.0a1/src/mlbundle/_sklearn/payload.py +234 -0
  47. mlbundle-0.1.0a1/src/mlbundle/_sklearn/pipeline_scope.py +141 -0
  48. mlbundle-0.1.0a1/src/mlbundle/_sklearn/polynomial_names.py +31 -0
  49. mlbundle-0.1.0a1/src/mlbundle/_sklearn/prediction.py +119 -0
  50. mlbundle-0.1.0a1/src/mlbundle/_sklearn/prediction_state.py +61 -0
  51. mlbundle-0.1.0a1/src/mlbundle/_sklearn/references.py +110 -0
  52. mlbundle-0.1.0a1/src/mlbundle/_sklearn/serialization.py +80 -0
  53. mlbundle-0.1.0a1/src/mlbundle/_sklearn/state.py +144 -0
  54. mlbundle-0.1.0a1/src/mlbundle/_sklearn/state_fields.json +228 -0
  55. mlbundle-0.1.0a1/src/mlbundle/_sklearn/structure.py +265 -0
  56. mlbundle-0.1.0a1/src/mlbundle/_sklearn/transform_names.py +111 -0
  57. mlbundle-0.1.0a1/src/mlbundle/_sklearn/transform_width.py +270 -0
  58. mlbundle-0.1.0a1/src/mlbundle/_sklearn/transformers.py +69 -0
  59. mlbundle-0.1.0a1/src/mlbundle/_sklearn/trusted.py +89 -0
  60. mlbundle-0.1.0a1/src/mlbundle/_xgboost/__init__.py +262 -0
  61. mlbundle-0.1.0a1/src/mlbundle/_xgboost/arrays.py +47 -0
  62. mlbundle-0.1.0a1/src/mlbundle/_xgboost/categories.py +109 -0
  63. mlbundle-0.1.0a1/src/mlbundle/_xgboost/native.py +193 -0
  64. mlbundle-0.1.0a1/src/mlbundle/_xgboost/prediction.py +59 -0
  65. mlbundle-0.1.0a1/src/mlbundle/_xgboost/snapshots.py +41 -0
  66. mlbundle-0.1.0a1/src/mlbundle/_xgboost/state.py +147 -0
  67. mlbundle-0.1.0a1/src/mlbundle/_xgboost/structure.py +47 -0
  68. mlbundle-0.1.0a1/src/mlbundle/_xgboost/ubjson.py +119 -0
  69. mlbundle-0.1.0a1/src/mlbundle/_zip_directory.py +173 -0
  70. mlbundle-0.1.0a1/src/mlbundle/_zip_members.py +116 -0
  71. mlbundle-0.1.0a1/src/mlbundle/adapters.py +33 -0
  72. mlbundle-0.1.0a1/src/mlbundle/canonical.py +290 -0
  73. mlbundle-0.1.0a1/src/mlbundle/claims.py +124 -0
  74. mlbundle-0.1.0a1/src/mlbundle/cli.py +193 -0
  75. mlbundle-0.1.0a1/src/mlbundle/container.py +93 -0
  76. mlbundle-0.1.0a1/src/mlbundle/contract.py +55 -0
  77. mlbundle-0.1.0a1/src/mlbundle/declarations.py +68 -0
  78. mlbundle-0.1.0a1/src/mlbundle/environment.py +85 -0
  79. mlbundle-0.1.0a1/src/mlbundle/errors.py +92 -0
  80. mlbundle-0.1.0a1/src/mlbundle/inspection.py +30 -0
  81. mlbundle-0.1.0a1/src/mlbundle/integrity.py +30 -0
  82. mlbundle-0.1.0a1/src/mlbundle/limits.py +38 -0
  83. mlbundle-0.1.0a1/src/mlbundle/manifest.py +222 -0
  84. mlbundle-0.1.0a1/src/mlbundle/model_io.py +151 -0
  85. mlbundle-0.1.0a1/src/mlbundle/py.typed +0 -0
  86. mlbundle-0.1.0a1/src/mlbundle/registry.py +103 -0
  87. mlbundle-0.1.0a1/src/mlbundle/results.py +68 -0
  88. mlbundle-0.1.0a1/src/mlbundle/samples.py +71 -0
  89. mlbundle-0.1.0a1/src/mlbundle/schema.py +145 -0
  90. mlbundle-0.1.0a1/src/mlbundle/training.py +104 -0
  91. mlbundle-0.1.0a1/src/mlbundle/writing.py +256 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pablo Ulloa Santín
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,347 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlbundle
3
+ Version: 0.1.0a1
4
+ Summary: Pickle-free, verifiable model bundles with a manifest, schema and structure.
5
+ Keywords: machine-learning,mlops,model-serialization,model-packaging,reproducibility,security,scikit-learn,xgboost,catboost,onnx,skops
6
+ Author: Pablo Ulloa Santín
7
+ Author-email: Pablo Ulloa Santín <ulloasantinpablo2@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: Implementation :: CPython
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Dist: pydantic>=2.13.4,<3
23
+ Requires-Dist: scikit-learn>=1.6 ; extra == 'all'
24
+ Requires-Dist: skops>=0.16.0 ; extra == 'all'
25
+ Requires-Dist: xgboost>=3.4.1 ; extra == 'all'
26
+ Requires-Dist: catboost>=1.2.10 ; extra == 'all'
27
+ Requires-Dist: onnx>=1.23.0 ; extra == 'all'
28
+ Requires-Dist: onnxruntime>=1.30.0 ; extra == 'all'
29
+ Requires-Dist: mlschema>=0.2.1,<0.3 ; python_full_version == '3.14.*' and extra == 'all'
30
+ Requires-Dist: catboost>=1.2.10 ; extra == 'catboost'
31
+ Requires-Dist: onnx>=1.23.0 ; extra == 'onnx'
32
+ Requires-Dist: onnxruntime>=1.30.0 ; extra == 'onnx'
33
+ Requires-Dist: mlschema>=0.2.1,<0.3 ; python_full_version == '3.14.*' and extra == 'schema'
34
+ Requires-Dist: scikit-learn>=1.6 ; extra == 'sklearn'
35
+ Requires-Dist: skops>=0.16.0 ; extra == 'sklearn'
36
+ Requires-Dist: xgboost>=3.4.1 ; extra == 'xgboost'
37
+ Requires-Dist: scikit-learn>=1.2 ; extra == 'xgboost'
38
+ Requires-Python: >=3.14
39
+ Project-URL: Homepage, https://github.com/UlloaSP/mlbundle
40
+ Project-URL: Repository, https://github.com/UlloaSP/mlbundle
41
+ Project-URL: Issues, https://github.com/UlloaSP/mlbundle/issues
42
+ Project-URL: Documentation, https://github.com/UlloaSP/mlbundle/blob/main/README.md
43
+ Provides-Extra: all
44
+ Provides-Extra: catboost
45
+ Provides-Extra: onnx
46
+ Provides-Extra: schema
47
+ Provides-Extra: sklearn
48
+ Provides-Extra: xgboost
49
+ Description-Content-Type: text/markdown
50
+
51
+ # mlbundle
52
+
53
+ Package a trained model, manifest, locally inferred schema and structure in one `.mlbundle` file.
54
+ Export on the training machine and validate/load in the consuming application with the same Python library.
55
+
56
+ mlbundle replaces three gaps in separate model/DataFrame uploads:
57
+
58
+ - **Pickle code execution:** closed adapters use skops, native XGBoost UBJSON,
59
+ CatBoost CBM or an existing ONNX file, without pickle/joblib/cloudpickle.
60
+ - **Missing training versions:** the manifest records Python, OS, architecture
61
+ and installed package versions alongside model and feature metadata.
62
+ - **Training data leaving the machine:** schema inference runs locally;
63
+ training rows are included only when explicitly requested as samples.
64
+
65
+ **Pre-alpha v0.1.0a1, published to reserve the PyPI name.** Format version 1 is
66
+ not frozen; bundles may need re-export. The security design has not had an
67
+ independent review: do not accept bundles from untrusted parties.
68
+ Requires **Python >=3.14**; tested on Windows/Linux. Schema requires Python 3.14.
69
+
70
+ ## Install and try scikit-learn
71
+
72
+ Install the pre-release with
73
+ `python -m pip install --pre "mlbundle[sklearn]"`.
74
+ Replace `[sklearn]` with `[xgboost]`, `[catboost]`, `[onnx]`, `[schema]` or `[all]` for the
75
+ corresponding dependencies. Combine extras as `[sklearn,schema]` for local
76
+ DataFrame inference. Omit the brackets for a base install that can inspect
77
+ every bundle without model libraries. Load/verify needs the matching model extra.
78
+
79
+ ```python
80
+ from sklearn.tree import DecisionTreeClassifier
81
+ from mlbundle import export, inspect, load
82
+
83
+ rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
84
+ model = DecisionTreeClassifier(random_state=7).fit(rows, ["cold", "warm"] * 2)
85
+ export(model, "trained.mlbundle", feature_names=["temperature", "humidity"])
86
+ bundle = load("trained.mlbundle")
87
+ assert isinstance(bundle.native_model, DecisionTreeClassifier)
88
+ assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
89
+ assert bundle.class_labels == [["cold", "warm"]]
90
+ print(bundle.predict_proba([[0.0, 1.0]])) # [[1.0, 0.0]]
91
+ assert inspect("trained.mlbundle").manifest.model.library == "sklearn"
92
+ ```
93
+
94
+ `export` returns metadata and requires `overwrite=True` for existing destinations.
95
+ `load` accepts paths, bytes or streams. Classifiers use `bundle.predict_proba`;
96
+ regressors use `bundle.predict`. Results are plain lists: output-major classifier
97
+ labels/probabilities, row-major regression values. `native_model` supports native
98
+ inference/explanations; mutating it invalidates the metadata verification.
99
+
100
+ Native names win; otherwise origin is `declared`, `dataframe`, or `generated` (`feature_1` through `feature_N`).
101
+ `target_name=` and `metadata=` remain author declarations.
102
+
103
+ ## XGBoost
104
+
105
+ Install the `xgboost` extra. Train on encoded indices and supply the original
106
+ labels in probability-column order, nested even for one output:
107
+
108
+ ```python
109
+ from xgboost import XGBClassifier
110
+ from mlbundle import export, load
111
+
112
+ rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
113
+ model = XGBClassifier(n_estimators=3, max_depth=2, n_jobs=1).fit(rows, [0, 1, 0, 1])
114
+ export(model, "xgboost.mlbundle", class_labels=[["cold", "warm"]])
115
+ bundle = load("xgboost.mlbundle")
116
+ assert isinstance(bundle.native_model, XGBClassifier)
117
+ assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
118
+ assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
119
+ assert bundle.class_labels == [["cold", "warm"]]
120
+ ```
121
+
122
+ Labels/settings bind to a native **copy**. Load verifies consistency, not history.
123
+ Omitted labels use encoded indices/an existing binding; native predict stays encoded.
124
+ Standalone sklearn, CatBoost and regressors reject `class_labels=`; composites
125
+ delegate labels to the final native adapter.
126
+
127
+ Only recoverable native configuration is recorded; learning rate, max depth,
128
+ seed and thread count do not survive native XGBoost I/O and are not reconstructed.
129
+
130
+ ## CatBoost
131
+
132
+ Install the `catboost` extra (>=1.2.10; includes matplotlib/plotly). Numeric,
133
+ single-target symmetric classifiers/regressors use native CBM and native labels:
134
+
135
+ ```python
136
+ from catboost import CatBoostClassifier
137
+ from mlbundle import export, load
138
+
139
+ rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
140
+ model = CatBoostClassifier(
141
+ iterations=3, depth=2, verbose=False, allow_writing_files=False
142
+ ).fit(rows, ["cold", "warm"] * 2)
143
+ export(model, "catboost.mlbundle")
144
+ bundle = load("catboost.mlbundle")
145
+ assert bundle.class_labels == [["cold", "warm"]]
146
+ assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
147
+ ```
148
+
149
+ `class_labels=`, categorical/text/embedding features,
150
+ Depthwise/Lossguide, custom Python objectives/metrics and other losses are rejected.
151
+ Classifier losses: Logloss, CrossEntropy, MultiClass. Regressors: RMSE, MAE,
152
+ Quantile, Huber, Poisson, MAPE. Complete trees/borders/NaN handling/importances
153
+ are verified on load. [The CatBoost contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-catboost.md) details
154
+ wrapper recovery, native-parser limits and the private in-memory serializer.
155
+
156
+ ## Composite pipelines
157
+
158
+ Install `[sklearn,xgboost]` or `[sklearn,catboost]`. Export the fitted Pipeline
159
+ as it is; transformers use skops and the final estimator uses its native adapter:
160
+
161
+ ```python
162
+ from sklearn.pipeline import make_pipeline
163
+ from sklearn.preprocessing import StandardScaler
164
+ from xgboost import XGBClassifier
165
+ from mlbundle import export, load
166
+
167
+ rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
168
+ model = make_pipeline(StandardScaler(), XGBClassifier(n_estimators=3, n_jobs=1))
169
+ model.fit(rows, [0, 1, 0, 1])
170
+ export(model, "pipeline.mlbundle", class_labels=[["cold", "warm"]])
171
+ bundle = load("pipeline.mlbundle")
172
+ assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
173
+ assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
174
+ assert bundle.class_labels == [["cold", "warm"]]
175
+ ```
176
+
177
+ Load rebuilds a real Pipeline after verifying both parts. Bundle probability labels
178
+ use XGBoost's bound originals; native classes/predict stay encoded. CatBoost keeps
179
+ native labels. Prefixes include scalers, PCA, SimpleImputer (indicators/empty columns),
180
+ KBinsDiscretizer, PolynomialFeatures, univariate selectors, OneHotEncoder/OrdinalEncoder,
181
+ identity/closed NumPy FunctionTransformer, nested native-free Pipeline and ColumnTransformer.
182
+ Each needs a fitted, data-free width proof. Unknown widths, FeatureUnion/KNNImputer,
183
+ slice/callable/scalar selectors, caching, metadata routing and other native placements
184
+ remain rejected. Step names/outer options are declarations; trees address transformed
185
+ features, without original-feature attribution. Schema/dtypes/samples describe original
186
+ inputs; six members permit schema plus explicit samples. See [the pipeline contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-pipeline.md).
187
+
188
+ ## Existing ONNX files
189
+
190
+ Install the `onnx` extra. This example assumes your existing `trained.onnx` is
191
+ a self-contained tabular classifier with two numeric features and two probability
192
+ columns. Labels on a label-free graph are count-checked declarations:
193
+
194
+ ```python
195
+ from pathlib import Path
196
+ from mlbundle import export, load
197
+
198
+ original = Path("trained.onnx").read_bytes()
199
+ export("trained.onnx", "onnx.mlbundle", class_labels=[["cold", "warm"]])
200
+ bundle = load("onnx.mlbundle")
201
+ assert bundle.native_model.data == original
202
+ assert bundle.native_model.session.get_providers() == ["CPUExecutionProvider"]
203
+ assert bundle.class_labels == [["cold", "warm"]]
204
+ print(bundle.predict_proba([[0.0, 0.0]]))
205
+ ```
206
+
207
+ Paths and bytes are preserved unchanged; a ModelProto is serialized once.
208
+ There is no conversion. `native_model` contains the original `data` and CPU
209
+ `session`. Native graph labels are verified; supplied labels must agree with
210
+ them. Without native/supplied labels, encoded indices are generated. ONNX has
211
+ no visualization structure. See [the exact ONNX contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md).
212
+
213
+ ## Local schema and explicit samples
214
+
215
+ Install a model extra plus `schema` on Python 3.14. Supply a nonempty pandas
216
+ DataFrame with string feature names in model input order. A named target must
217
+ exist and is excluded; without `target_name`, the frame contains features only.
218
+
219
+ ```python
220
+ import pandas as pd
221
+ from sklearn.tree import DecisionTreeClassifier
222
+ from mlbundle import export, inspect, load
223
+
224
+ training = pd.DataFrame(
225
+ [[0.0, 1.0, "cold"], [1.0, 0.0, "warm"]] * 2,
226
+ columns=["temperature", "humidity", "target"],
227
+ )
228
+ features = training.drop(columns="target")
229
+ model = DecisionTreeClassifier(random_state=7).fit(features, training["target"])
230
+ export(model, "schema.mlbundle", training_data=training, target_name="target")
231
+ view = inspect("schema.mlbundle")
232
+ assert view.manifest.features.dtypes == ["float64", "float64"]
233
+ assert view.schema["reports"][0]["labels"] == ["cold", "warm"]
234
+ assert view.samples is None
235
+ assert load("schema.mlbundle").schema == view.schema
236
+ # Samples are real data: explicitly include the first two feature rows.
237
+ export(model, "sampled.mlbundle", training_data=features, sample_rows=2)
238
+ assert inspect("sampled.mlbundle").samples == features.head(2).to_dict("records")
239
+ ```
240
+
241
+ `schema.json` contains a schema version, locally inferred fields and one report
242
+ per target output. `color__red`/`color__blue` binary columns can become one
243
+ category field using mlschema's `__` convention. Frame names must match native
244
+ names in order; `feature_names=` cannot rename/reorder a supplied frame.
245
+ Training dtypes are declarations, separate from verified model dtypes. Inspect/load
246
+ validate schema mappings, reports and sample keys without pandas or mlschema.
247
+
248
+ **Data leaving the machine:** names/dtypes, field kinds/nullability, dtype-based
249
+ steps, categories (including unused), one-hot suffixes, pair-axis metadata and
250
+ class labels. No inferred ranges, means, counts or arbitrary text/example rows.
251
+ Names/categories and learned models/structure can reveal training information.
252
+ Explicit `sample_rows=N` includes the first N feature rows, without target/index;
253
+ N must be positive and within the frame. See [schema and samples](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-schema.md).
254
+
255
+ ## Inspect, load and the CLI
256
+
257
+ `inspect` validates archive layout, limits, manifest, hashes and schema/sample
258
+ declarations **without importing any model library**. It cannot attest that
259
+ metadata or structure matches the model. `load` additionally deserializes through
260
+ the closed adapter and recomputes native claims and structure before returning
261
+ the model and predictor. Export checks the same native round trip before writing.
262
+
263
+ ```shell
264
+ mlbundle inspect trained.mlbundle
265
+ mlbundle inspect trained.mlbundle --json
266
+ mlbundle verify trained.mlbundle
267
+ ```
268
+
269
+ Inspection prints metadata, members/hashes and declarations. Human values escape
270
+ terminal controls and truncate at 512 characters; `--json` is lossless ASCII-escaped
271
+ manifest JSON. `verify` requires the model extra. Errors are one bounded stderr
272
+ line without bundle content/traceback. Exit codes: **0** success, **1** bundle or
273
+ dependency rejection (`MLBundleError`), **2** usage/I/O failure.
274
+
275
+ ## Supported models and limits
276
+
277
+ - **Sklearn:** fitted classifiers exposing `predict_proba` and regressors with
278
+ recoverable feature/output counts. Tested families include trees/ensembles,
279
+ linear/regularized/robust/GLM models, SVMs, PLSRegression, naive Bayes, LDA/QDA,
280
+ dense brute-force neighbors, NearestCentroid, MLP and MultiOutput/RegressorChain.
281
+ SimpleImputer/KNNImputer, scalers, KBinsDiscretizer, categorical encoders,
282
+ univariate selectors and closed NumPy FunctionTransformer functions round-trip,
283
+ including nested ColumnTransformer pipelines; see [the exact lists](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md).
284
+ All-sklearn Pipeline structure stays null; composite trees expose the inner boundary. See
285
+ [tested configurations](https://github.com/UlloaSP/mlbundle/blob/main/tests/test_sklearn_families.py) and [core round trips/rejections](https://github.com/UlloaSP/mlbundle/blob/main/tests/test_sklearn.py).
286
+ - **Sklearn rejections:** custom estimators/subclasses, functions/types outside the
287
+ closed lists (including lambdas and user functions), compound fitted dtypes, classifiers without
288
+ probabilities, multilabel/ClassifierChain layouts, unsupported native placements,
289
+ and untrusted nested storage such as fitted sparse neighbor arrays.
290
+ HistGradientBoosting uses untrusted `TreePredictor` storage and is rejected.
291
+ - **Sklearn versions:** the closed class/attribute catalog is audited/tested
292
+ against **1.9.1 with skops 0.16.0**, not every version allowed by `>=1.6`.
293
+ Unknown state fails; export/load sklearn versions must match. Configurations
294
+ must satisfy the catalog and round trip; see [state ownership](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md).
295
+ - **XGBoost:** exact XGBClassifier/XGBRegressor, one target, native gbtree
296
+ (including XGBoost 3.4.1 dart normalized to gbtree). Closed probability/regression
297
+ objectives; raw boosters, RF wrappers, subclasses, ranking/survival/custom
298
+ objectives and unsupported storage are rejected. At most 100,000 trees/depth 14,
299
+ with complete state/dumps; see [the mapping](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-adapters.md#implemented-xgboost-mapping).
300
+ - **ONNX:** fixed-width rank-two numeric inputs; one regression/probability target,
301
+ optional native labels and dynamic/one-row batches. Unsupported layouts,
302
+ external data/custom domains/functions and runtime/training configuration fail
303
+ before CPU sessions. Tested with ONNX 1.23.0/Runtime 1.30.0; see [ONNX bounds](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md).
304
+
305
+ Archive ceilings: **6 members**, **512 MiB** archive/compressed/member bytes,
306
+ **1 GiB** aggregate uncompressed bytes, ratio **200**, JSON depth **32**.
307
+ Manifest: **8 MiB**; structure: **256 MiB**; schema/samples: **16 MiB each**.
308
+ Features/classes: at most **100,000**; outputs: **1,024** (XGBoost/CatBoost/ONNX: one).
309
+ Callers may only lower `ArchiveLimits`. Native [state](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md)/[ONNX](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md)
310
+ bounds do not cap process memory, prediction rows or CPU.
311
+
312
+ ## Bundle contents and security
313
+
314
+ Every bundle contains `manifest.json` and one native member, or the declared
315
+ `model.skops` + `model.ubj`/`model.cbm` composite pair. Single-step composites use only the leaf. Optional members are `schema.json`, `structure.json`
316
+ (required when the adapter extracts structure) and explicitly requested
317
+ `samples.json`. No training DataFrame or arbitrary files are included.
318
+ The manifest records identity/time/writer, model parameters/counts/state,
319
+ ordered features/dtypes/origin, target labels/name, environment, member
320
+ SHA-256/sizes, references and free-form user metadata. The normative
321
+ [format specification](https://github.com/UlloaSP/mlbundle/blob/main/docs/format.md) owns these records and verification rules.
322
+
323
+ Treat bundles as untrusted. The reader validates ZIP layout, allowed members,
324
+ limits, strict JSON/manifest and hashes; adapters use closed trust/loading paths
325
+ and recompute claims. **Hashes detect corruption, not deliberate tampering**;
326
+ there is no signing or proof of authorship, training history, quality or fairness.
327
+ Schema, samples and training-environment declarations are not authenticated.
328
+ Native parsers/inference require process isolation, memory/CPU/row limits and
329
+ maintained dependencies. See [SECURITY.md](https://github.com/UlloaSP/mlbundle/blob/main/SECURITY.md) for guarantees and reporting.
330
+
331
+ ## Development
332
+
333
+ From the root: `uv python install 3.14`, `uv sync --locked --all-extras`,
334
+ `uv run pre-commit install`. Default dev installs every extra; base: `uv sync --no-dev`.
335
+ Pydantic v2 is the sole core dependency; model/schema imports are lazy. Schema uses
336
+ mlschema `>=0.2.1,<0.3` on 3.14 only (0.2.1 excludes 3.15). `uv.lock` fixes tested
337
+ versions; dependency edits require `uv sync` and committing the updated lockfile.
338
+
339
+ Run `uv run ruff check .`, `uv run ruff format --check .`, `uv run pyright`,
340
+ `uv run pytest` and `uv build`. Tests execute every fenced README example and
341
+ write terminal/XML coverage with an 80% branch-inclusive minimum.
342
+ CI checks lint/types and tests the same built wheel on Ubuntu/Windows, including
343
+ clean base installs. The owner, or an agent on the owner's explicit request for
344
+ that version, runs manual [publishing](https://github.com/UlloaSP/mlbundle/blob/main/docs/publishing.md) from `main` with `pypi_env` approval. Read [AGENTS.md](https://github.com/UlloaSP/mlbundle/blob/main/AGENTS.md).
345
+ Agents never run or approve it on their own initiative or add publish paths, tokens or secrets.
346
+ Releases, version tags, conversions, per-model environments, signing and additional
347
+ model libraries remain outside scope. The project is MIT licensed.