Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ inferred schema and model structure in one compressed `.mlbundle` file.

It owns export, metadata-only inspection and validated loading. It is the
training-side companion to MLSuite, whose runtime accepts `.mlbundle` uploads.
MLSuite does not train models. The package version is 0.2.0; format 1 is not frozen.
MLSuite does not train models. The package version is 0.2.1; format 1 is not frozen.
Implementation claims require code and test proof.

## What must remain true
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<a href="https://mlbundle.eshkra.dev"><img src="https://img.shields.io/badge/website-303039?style=flat&amp;logo=astro&amp;logoColor=ff6a1a" alt="Website" /></a>
<a href="https://docs.mlbundle.eshkra.dev"><img src="https://img.shields.io/badge/docs-303039?style=flat&amp;logo=readthedocs&amp;logoColor=ff6a1a" alt="Documentation" /></a>
<a href="https://playground.mlbundle.eshkra.dev"><img src="https://img.shields.io/badge/playground-303039?style=flat&amp;logo=stackblitz&amp;logoColor=ff6a1a" alt="Playground" /></a>
<a href="https://pypi.org/project/mlbundle/"><img src="https://img.shields.io/badge/version-0.2.0-b73d00?style=flat&amp;logo=pypi&amp;logoColor=white&amp;labelColor=303039" alt="Version 0.2.0" /></a>
<a href="https://pypi.org/project/mlbundle/"><img src="https://img.shields.io/badge/version-0.2.1-b73d00?style=flat&amp;logo=pypi&amp;logoColor=white&amp;labelColor=303039" alt="Version 0.2.1" /></a>
<a href="https://github.com/UlloaSP/mlbundle"><img src="https://img.shields.io/badge/source-303039?style=flat&amp;logo=github&amp;logoColor=white" alt="Source on GitHub" /></a>
<a href="https://github.com/UlloaSP/mlbundle/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/UlloaSP/mlbundle/ci.yml?branch=main&amp;style=flat&amp;label=CI&amp;labelColor=303039" alt="CI status" /></a>
<a href="https://github.com/UlloaSP/mlbundle/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-b73d00?style=flat&amp;labelColor=303039" alt="MIT license" /></a>
Expand Down Expand Up @@ -43,11 +43,11 @@ preserve the supported model without conversion.

## Installation

MLbundle **0.2.0** requires Python **3.14** for export with the schema extra.
MLbundle **0.2.1** requires Python **3.14** for export with the schema extra.
The installation command for this release is:

```bash
python -m pip install "mlbundle[sklearn,schema]==0.2.0"
python -m pip install "mlbundle[sklearn,schema]==0.2.1"
```

To use a local checkout, run `python -m pip install -e ".[sklearn,schema]"`
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Security policy

MLbundle 0.2.0 has not had an independent security review;
MLbundle 0.2.1 has not had an independent security review;
do not accept bundles from untrusted parties. Format version 1 is not frozen.
It implements strict manifest validation, secure container reading/writing, metadata-only
`inspect`, export/load, sklearn, XGBoost, CatBoost and ONNX adapters, local schema inference,
Expand Down
4 changes: 2 additions & 2 deletions docs/publishing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Publishing to PyPI

Version `0.2.0` is prepared by this checkout. Merging a version change into
Version `0.2.1` is prepared by this checkout. Merging a version change into
`main` automatically starts the PyPI workflow; no one needs to start a release
run manually. Format version 1 is not frozen, so incompatible manifest/schema
shapes require re-export. Valid a2 bundles remain compatible, as documented in
Expand All @@ -19,7 +19,7 @@ independent review; do not accept bundles from untrusted parties.
tests on Ubuntu and Windows, and all frontend checks, tests and builds.
4. If every check passes, Actions uploads the exact CI-built wheel and source
archive to [mlbundle on PyPI](https://pypi.org/project/mlbundle/).
Install with `python -m pip install "mlbundle[sklearn,schema]==0.2.0"` on
Install with `python -m pip install "mlbundle[sklearn,schema]==0.2.1"` on
Python 3.14, replacing the example version as needed.

The GitHub `pypi_env` environment contains the project-scoped
Expand Down
10 changes: 5 additions & 5 deletions docs/src/content/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
---
title: Installation
description: Install mlbundle 0.2.0 with the extras for your model library, from PyPI or a local checkout, and check the Python and dependency ranges.
description: Install mlbundle 0.2.1 with the extras for your model library, from PyPI or a local checkout, and check the Python and dependency ranges.
---

mlbundle requires **Python 3.14 or newer**. Its only core dependency is Pydantic v2; every model
library is an optional extra that is imported lazily, only when a bundle needs it.

These pages document mlbundle 0.2.0.
These pages document mlbundle 0.2.1.

```bash
python -m pip install "mlbundle[sklearn,schema]==0.2.0"
python -m pip install "mlbundle[sklearn,schema]==0.2.1"
```

Replace `sklearn` with the extra for your model library; the extras are listed
[below](#choose-the-extras).

## From a local checkout

To work from source, or before 0.2.0 is available from your package index, install a checkout of
To work from source, or before 0.2.1 is available from your package index, install a checkout of
the repository:

```bash
Expand Down Expand Up @@ -66,7 +66,7 @@ from importlib.metadata import version

import mlbundle

print(version("mlbundle")) # 0.2.0
print(version("mlbundle")) # 0.2.1
print(mlbundle.FORMAT_NAME, mlbundle.FORMAT_VERSION) # mlbundle 1
```

Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/guides/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Metadata and integrity validated; model claims NOT verified.
Format: mlbundle 1
Bundle id: aa6f6b39-574a-499b-a0ff-de4cdd334050
Created: 2026-10-19T11:17:51.949658+00:00
Writer: "0.2.0"
Writer: "0.2.1"
Model: sklearn "DecisionTreeClassifier" (classifier)
Counts: features=2, outputs=1, classes=[2]
Features (declared): ["temperature", "humidity"]
Expand All @@ -34,7 +34,7 @@ Members:
schema.json: 361 bytes sha256=ea62a6a7502ae8ea30b45b61bb18d8e31bd2ec55f026d9fbf0c05ef42d01ff25
structure.json: 526 bytes sha256=66204da26781a4e2a800a1f3ed246ab1a43e8c97ced73d06a2640f897eeda03c
Training: {"python": "3.14.7", "implementation": "CPython", "os": "Windows", "os_version": "11", "architecture": "AMD64"}
Packages: {"catboost": "1.2.10", "mlbundle": "0.2.0", "mlschema": "0.3.0", ...}
Packages: {"catboost": "1.2.10", "mlbundle": "0.2.1", "mlschema": "0.3.0", ...}
ONNX environment: null
Present: schema=yes, structure=yes, samples=no
User metadata: {}
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/inspect-and-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ from mlbundle import inspect

view = inspect("trained.mlbundle")
manifest = view.manifest
print(manifest.format_version, manifest.writer_version) # 1 0.2.0
print(manifest.format_version, manifest.writer_version) # 1 0.2.1
print(manifest.model.n_features, manifest.model.n_outputs) # 2 1
print([member.value for member in view.members])
# ['manifest.json', 'model.skops', 'schema.json', 'structure.json']
Expand Down
20 changes: 10 additions & 10 deletions landing/src/data/example.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"version": "0.2.0",
"version": "0.2.1",
"code": "import pandas as pd\nfrom sklearn.tree import DecisionTreeClassifier\n\nfrom mlbundle import export, load\n\ntraining = pd.DataFrame(\n [[0.0, 1.0, \"cold\"], [1.0, 0.0, \"warm\"]] * 2,\n columns=[\"temperature\", \"humidity\", \"target\"],\n)\nfeatures = training.drop(columns=\"target\")\nmodel = DecisionTreeClassifier(random_state=7)\nmodel.fit(features, training[\"target\"])\n\nexport(\n model,\n \"trained.mlbundle\",\n training_data=training,\n target_names=[\"target\"],\n)\n\nbundle = load(\"trained.mlbundle\")\nrecord = {\"temperature\": 0.0, \"humidity\": 1.0}\nprediction = bundle.predict_record(record)[0]\nprint(prediction.predicted_label, prediction.probabilities)\n",
"printed": "cold (1.0, 0.0)",
"bundleBytes": 9227,
"bundleBytes": 9233,
"members": [
{
"name": "manifest.json",
Expand All @@ -12,7 +12,7 @@
{
"name": "model.skops",
"size": 15011,
"sha256": "13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6"
"sha256": "b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2"
},
{
"name": "schema.json",
Expand All @@ -28,9 +28,9 @@
"manifest": {
"format_name": "mlbundle",
"format_version": "1",
"bundle_id": "a9e717a9-b071-4d23-bbd1-6deedc47e6b5",
"created_at": "2026-10-19T11:17:33.114088Z",
"writer_version": "0.2.0",
"bundle_id": "869eb397-2dce-44b4-b0d0-b6a85d757b0b",
"created_at": "2026-10-19T13:27:32.304933Z",
"writer_version": "0.2.1",
"model": {
"estimator_class": "DecisionTreeClassifier",
"kind": "classifier",
Expand Down Expand Up @@ -90,7 +90,7 @@
"architecture": "AMD64",
"packages": {
"catboost": "1.2.10",
"mlbundle": "0.2.0",
"mlbundle": "0.2.1",
"mlschema": "0.3.0",
"numpy": "2.5.4",
"onnx": "1.23.0",
Expand All @@ -105,7 +105,7 @@
},
"integrity": {
"model.skops": {
"sha256": "13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6",
"sha256": "b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2",
"size": 15011
},
"schema.json": {
Expand Down Expand Up @@ -159,7 +159,7 @@
},
"inspect": {
"exit": 0,
"stdout": "Metadata and integrity validated; model claims NOT verified.\nFormat: mlbundle 1\nBundle id: a9e717a9-b071-4d23-bbd1-6deedc47e6b5\nCreated: 2026-10-19T11:17:33.114088+00:00\nWriter: \"0.2.0\"\nModel: sklearn \"DecisionTreeClassifier\" (classifier)\nCounts: features=2, outputs=1, classes=[2]\nFeatures (model): [\"temperature\", \"humidity\"]\nClass labels (output-major): [[\"cold\", \"warm\"]]\nTarget names (output-major): [\"target\"]\nMembers:\n manifest.json: validated (no self-integrity record)\n model.skops: 15011 bytes sha256=13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6\n schema.json: 388 bytes sha256=e770ad53af881faf368c686de86b2b00f0a64a1444e241985643349bf7b0bba4\n structure.json: 511 bytes sha256=8cf658be94b826ba4f81f1a78ed2c8e5107fce656d212dc888bd8621a9068baa\nTraining: {\"python\": \"3.14.7\", \"implementation\": \"CPython\", \"os\": \"Windows\", \"os_version\": \"11\", \"architecture\": \"AMD64\"}\nPackages: {\"catboost\": \"1.2.10\", \"mlbundle\": \"0.2.0\", \"mlschema\": \"0.3.0\", \"numpy\": \"2.5.4\", \"onnx\": \"1.23.0\", \"onnxruntime\": \"1.30.0\", \"pandas\": \"3.0.6\", \"scikit-learn\": \"1.9.1\", \"scipy\": \"1.18.1\", \"skops\": \"0.16.0\", \"xgboost\": \"3.4.1\"}\nONNX environment: null\nPresent: schema=yes, structure=yes, samples=no\nUser metadata: {}",
"stdout": "Metadata and integrity validated; model claims NOT verified.\nFormat: mlbundle 1\nBundle id: 869eb397-2dce-44b4-b0d0-b6a85d757b0b\nCreated: 2026-10-19T13:27:32.304933+00:00\nWriter: \"0.2.1\"\nModel: sklearn \"DecisionTreeClassifier\" (classifier)\nCounts: features=2, outputs=1, classes=[2]\nFeatures (model): [\"temperature\", \"humidity\"]\nClass labels (output-major): [[\"cold\", \"warm\"]]\nTarget names (output-major): [\"target\"]\nMembers:\n manifest.json: validated (no self-integrity record)\n model.skops: 15011 bytes sha256=b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2\n schema.json: 388 bytes sha256=e770ad53af881faf368c686de86b2b00f0a64a1444e241985643349bf7b0bba4\n structure.json: 511 bytes sha256=8cf658be94b826ba4f81f1a78ed2c8e5107fce656d212dc888bd8621a9068baa\nTraining: {\"python\": \"3.14.7\", \"implementation\": \"CPython\", \"os\": \"Windows\", \"os_version\": \"11\", \"architecture\": \"AMD64\"}\nPackages: {\"catboost\": \"1.2.10\", \"mlbundle\": \"0.2.1\", \"mlschema\": \"0.3.0\", \"numpy\": \"2.5.4\", \"onnx\": \"1.23.0\", \"onnxruntime\": \"1.30.0\", \"pandas\": \"3.0.6\", \"scikit-learn\": \"1.9.1\", \"scipy\": \"1.18.1\", \"skops\": \"0.16.0\", \"xgboost\": \"3.4.1\"}\nONNX environment: null\nPresent: schema=yes, structure=yes, samples=no\nUser metadata: {}",
"stderr": ""
},
"verify": {
Expand All @@ -169,7 +169,7 @@
},
"editedInspect": {
"exit": 0,
"stdout": "Metadata and integrity validated; model claims NOT verified.\nFormat: mlbundle 1\nBundle id: a9e717a9-b071-4d23-bbd1-6deedc47e6b5\nCreated: 2026-10-19T11:17:33.114088+00:00\nWriter: \"0.2.0\"\nModel: sklearn \"DecisionTreeClassifier\" (classifier)\nCounts: features=2, outputs=1, classes=[2]\nFeatures (model): [\"temperature\", \"humidity\"]\nClass labels (output-major): [[\"cold\", \"warm\"]]\nTarget names (output-major): [\"target\"]\nMembers:\n manifest.json: validated (no self-integrity record)\n model.skops: 15011 bytes sha256=13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6\n schema.json: 388 bytes sha256=e770ad53af881faf368c686de86b2b00f0a64a1444e241985643349bf7b0bba4\n structure.json: 511 bytes sha256=8cf658be94b826ba4f81f1a78ed2c8e5107fce656d212dc888bd8621a9068baa\nTraining: {\"python\": \"3.14.7\", \"implementation\": \"CPython\", \"os\": \"Windows\", \"os_version\": \"11\", \"architecture\": \"AMD64\"}\nPackages: {\"catboost\": \"1.2.10\", \"mlbundle\": \"0.2.0\", \"mlschema\": \"0.3.0\", \"numpy\": \"2.5.4\", \"onnx\": \"1.23.0\", \"onnxruntime\": \"1.30.0\", \"pandas\": \"3.0.6\", \"scikit-learn\": \"1.9.1\", \"scipy\": \"1.18.1\", \"skops\": \"0.16.0\", \"xgboost\": \"3.4.1\"}\nONNX environment: null\nPresent: schema=yes, structure=yes, samples=no\nUser metadata: {}",
"stdout": "Metadata and integrity validated; model claims NOT verified.\nFormat: mlbundle 1\nBundle id: 869eb397-2dce-44b4-b0d0-b6a85d757b0b\nCreated: 2026-10-19T13:27:32.304933+00:00\nWriter: \"0.2.1\"\nModel: sklearn \"DecisionTreeClassifier\" (classifier)\nCounts: features=2, outputs=1, classes=[2]\nFeatures (model): [\"temperature\", \"humidity\"]\nClass labels (output-major): [[\"cold\", \"warm\"]]\nTarget names (output-major): [\"target\"]\nMembers:\n manifest.json: validated (no self-integrity record)\n model.skops: 15011 bytes sha256=b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2\n schema.json: 388 bytes sha256=e770ad53af881faf368c686de86b2b00f0a64a1444e241985643349bf7b0bba4\n structure.json: 511 bytes sha256=8cf658be94b826ba4f81f1a78ed2c8e5107fce656d212dc888bd8621a9068baa\nTraining: {\"python\": \"3.14.7\", \"implementation\": \"CPython\", \"os\": \"Windows\", \"os_version\": \"11\", \"architecture\": \"AMD64\"}\nPackages: {\"catboost\": \"1.2.10\", \"mlbundle\": \"0.2.1\", \"mlschema\": \"0.3.0\", \"numpy\": \"2.5.4\", \"onnx\": \"1.23.0\", \"onnxruntime\": \"1.30.0\", \"pandas\": \"3.0.6\", \"scikit-learn\": \"1.9.1\", \"scipy\": \"1.18.1\", \"skops\": \"0.16.0\", \"xgboost\": \"3.4.1\"}\nONNX environment: null\nPresent: schema=yes, structure=yes, samples=no\nUser metadata: {}",
"stderr": ""
},
"editedVerify": {
Expand Down
2 changes: 1 addition & 1 deletion playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Intended host: <https://playground.mlbundle.eshkra.dev/>. Documentation: <https:

Python runs in your browser on [Pyodide](https://pyodide.org/), inside a dedicated Web Worker. There is no server that executes code, and neither your code nor your files are uploaded anywhere.

- The build bundles this repository's `src/mlbundle` (every `.py` file plus the `state_fields.json` package data) into the worker, with distribution metadata whose version is read from `pyproject.toml`. The page always runs the mlbundle source it was built from, at version 0.2.0 today.
- The build bundles this repository's `src/mlbundle` (every `.py` file plus the `state_fields.json` package data) into the worker, with distribution metadata whose version is read from `pyproject.toml`. The page always runs the mlbundle source it was built from, at version 0.2.1 today.
- Each run executes your code as written, in a fresh namespace and an empty working directory. Every `*.mlbundle` the code writes into that directory (at most 8 per run) is passed to `mlbundle.inspect`, then to `mlbundle.load`, which deserializes it through the closed sklearn adapter and recomputes model claims and structure. Only then is it marked **Verified**. Loading is done automatically only for bundles your own code just wrote.
- **Download .mlbundle** saves the exact bytes mlbundle wrote.
- **Inspect a .mlbundle file** (the upload icon) reads a local file in the browser and runs `mlbundle.inspect` on it. It never calls `load` and never deserializes the model, so uploaded files show **Inspected only** and their structure is withheld: structure is only trustworthy once `load` has recomputed it. Files that mlbundle rejects show **Rejected** with mlbundle's error.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mlbundle"
version = "0.2.0"
version = "0.2.1"
description = "Pickle-free, verifiable model bundles with a manifest, schema and structure."
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion scripts/package_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def _base(path: Path) -> None:

import mlbundle

assert mlbundle.__version__ == "0.2.0"
assert mlbundle.__version__ == "0.2.1"
view = mlbundle.inspect(path)
assert view.manifest.model.library == "sklearn"
assert "cannot attest" in _console("--help")
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading