diff --git a/AGENTS.md b/AGENTS.md index adee395..8b59b4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/README.md b/README.md index a16d0a1..7c19263 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Website Documentation Playground - Version 0.2.0 + Version 0.2.1 Source on GitHub CI status MIT license @@ -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]"` diff --git a/SECURITY.md b/SECURITY.md index 5f1ed08..f52044a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -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, diff --git a/docs/publishing.md b/docs/publishing.md index ef847aa..5c8ad6c 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -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 @@ -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 diff --git a/docs/src/content/docs/getting-started/installation.md b/docs/src/content/docs/getting-started/installation.md index 17bd87e..854a464 100644 --- a/docs/src/content/docs/getting-started/installation.md +++ b/docs/src/content/docs/getting-started/installation.md @@ -1,15 +1,15 @@ --- 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 @@ -17,7 +17,7 @@ Replace `sklearn` with the extra for your model library; the extras are listed ## 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 @@ -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 ``` diff --git a/docs/src/content/docs/guides/cli.md b/docs/src/content/docs/guides/cli.md index 14e94c3..89d7bbe 100644 --- a/docs/src/content/docs/guides/cli.md +++ b/docs/src/content/docs/guides/cli.md @@ -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"] @@ -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: {} diff --git a/docs/src/content/docs/guides/inspect-and-load.md b/docs/src/content/docs/guides/inspect-and-load.md index 69ea76c..0d7d53c 100644 --- a/docs/src/content/docs/guides/inspect-and-load.md +++ b/docs/src/content/docs/guides/inspect-and-load.md @@ -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'] diff --git a/landing/src/data/example.json b/landing/src/data/example.json index acc01e0..6c0bc59 100644 --- a/landing/src/data/example.json +++ b/landing/src/data/example.json @@ -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", @@ -12,7 +12,7 @@ { "name": "model.skops", "size": 15011, - "sha256": "13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6" + "sha256": "b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2" }, { "name": "schema.json", @@ -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", @@ -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", @@ -105,7 +105,7 @@ }, "integrity": { "model.skops": { - "sha256": "13257b547ae5a12c2bc5cea20afaf68d1a36d6073c8f77f1ae676979c40375d6", + "sha256": "b332c1e060939f16a2f300797c6ee1625e99d684f215e45ba8772fd7dfee5da2", "size": 15011 }, "schema.json": { @@ -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": { @@ -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": { diff --git a/playground/README.md b/playground/README.md index 66acc0f..8d27486 100644 --- a/playground/README.md +++ b/playground/README.md @@ -8,7 +8,7 @@ Intended host: . Documentation: 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") diff --git a/uv.lock b/uv.lock index 2e6358f..29e8825 100644 --- a/uv.lock +++ b/uv.lock @@ -459,7 +459,7 @@ wheels = [ [[package]] name = "mlbundle" -version = "0.2.0" +version = "0.2.1" source = { editable = "." } dependencies = [ { name = "pydantic" },