millwright 0.2.0__tar.gz → 0.2.1__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.
- {millwright-0.2.0 → millwright-0.2.1}/CHANGELOG.md +26 -0
- {millwright-0.2.0 → millwright-0.2.1}/Cargo.lock +2 -1
- {millwright-0.2.0 → millwright-0.2.1}/Cargo.toml +6 -1
- {millwright-0.2.0 → millwright-0.2.1}/PKG-INFO +1 -1
- {millwright-0.2.0 → millwright-0.2.1}/docs/deploy.html +1 -0
- {millwright-0.2.0 → millwright-0.2.1}/index.html +12 -12
- {millwright-0.2.0 → millwright-0.2.1}/pyproject.toml +1 -1
- {millwright-0.2.0 → millwright-0.2.1}/src/monitor.rs +9 -0
- millwright-0.2.1/src/onnx.rs +1075 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/pipeline.rs +29 -34
- {millwright-0.2.0 → millwright-0.2.1}/src/registry.rs +37 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/selection/mod.rs +27 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/selection/search.rs +9 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/serve.rs +12 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/traits.rs +11 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/transform.rs +15 -0
- millwright-0.2.1/tests/hero_snippet.rs +51 -0
- millwright-0.2.1/tests/onnx_export.rs +151 -0
- millwright-0.2.0/src/onnx.rs +0 -327
- {millwright-0.2.0 → millwright-0.2.1}/.github/workflows/ci.yml +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/.github/workflows/release-crate.yml +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/.github/workflows/release-python.yml +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/.gitignore +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/CNAME +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/CONTRIBUTING.md +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/GUIDE.md +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/LICENSE +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/README.md +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/RELEASING.md +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/benches/throughput.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/data.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/index.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/insight.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/pipelines.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/python.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/docs/site.css +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/automl.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/backends.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/explore.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/insight.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/operations.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/portability.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/specialized.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/spine.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/trust.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/examples/workflow.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/guide.html +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/scripts/release.sh +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/anomaly.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/automl.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/backends/chronos.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/backends/incremental.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/backends/linfa.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/backends/mod.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/backends/smartcore.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/balance.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/calibration.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/diagnostics.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/ensemble.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/error.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/evaluate.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/explain.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/frame.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/lib.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/logistic.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/profile.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/python.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/rng.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/selection/cv.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/selection/scoring.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/table.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/src/viz.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/tests/ensemble_zoo.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/tests/golden.rs +0 -0
- {millwright-0.2.0 → millwright-0.2.1}/tests/real_data.rs +0 -0
|
@@ -6,6 +6,32 @@ All notable changes to Millwright are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.1] - 2026-08-23
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Exported forests now serve in Millwright.** `InferenceModel` runs linear / NN
|
|
13
|
+
ONNX graphs through tract as before, and evaluates the ONNX-ML tree-ensemble
|
|
14
|
+
ops tract doesn't implement (from an exported `RandomForest`) with a small
|
|
15
|
+
native interpreter. A model exported by Millwright always round-trips back into
|
|
16
|
+
`InferenceModel`/`Server` — forests included — while the artifact stays portable
|
|
17
|
+
to any ONNX runtime.
|
|
18
|
+
- **Imputers and one-hot encoders are ONNX-exportable.** A `SimpleImputer` step
|
|
19
|
+
exports as `Where(IsNaN(x), fill, x)`; a `OneHotEncoder` step exports as a
|
|
20
|
+
per-column `Gather` → `Round` → `Equal` → `Cast` → `Concat` expansion (with the
|
|
21
|
+
graph input re-declared at the raw feature width). So the whole realistic
|
|
22
|
+
`impute → one-hot → scale → model` pipeline exports and serves as one graph —
|
|
23
|
+
through tract (linear) or the native interpreter (behind a forest), verified
|
|
24
|
+
identical to the in-memory pipeline. The pipeline export generalized from
|
|
25
|
+
folding one affine map to splicing an ordered chain of transformer "prefixes"
|
|
26
|
+
(new `Transformer::onnx_prefix`). Only steps with no ONNX form now error.
|
|
27
|
+
- **`SearchResult::export_onnx`** — a `GridSearch`/`RandomSearch` winner can now
|
|
28
|
+
be exported to ONNX (it could only `predict` before). Backed by a new
|
|
29
|
+
object-safe `Estimator::to_onnx_proto` on `Pipeline`.
|
|
30
|
+
- **`Server::from_registry(®, name, tag)`** and
|
|
31
|
+
**`DriftMonitor::from_registry(&version)`** — serve a tagged artifact straight
|
|
32
|
+
from a registry, and build a PSI monitor from the version's stored reference
|
|
33
|
+
distribution.
|
|
34
|
+
|
|
9
35
|
## [0.2.0] - 2026-08-23
|
|
10
36
|
|
|
11
37
|
### Added
|
|
@@ -1935,7 +1935,7 @@ dependencies = [
|
|
|
1935
1935
|
|
|
1936
1936
|
[[package]]
|
|
1937
1937
|
name = "millwright"
|
|
1938
|
-
version = "0.2.
|
|
1938
|
+
version = "0.2.1"
|
|
1939
1939
|
dependencies = [
|
|
1940
1940
|
"axum",
|
|
1941
1941
|
"chronos-ts",
|
|
@@ -1956,6 +1956,7 @@ dependencies = [
|
|
|
1956
1956
|
"plotters",
|
|
1957
1957
|
"plotters-statistical",
|
|
1958
1958
|
"polars",
|
|
1959
|
+
"prost 0.13.5",
|
|
1959
1960
|
"pyo3",
|
|
1960
1961
|
"rayon",
|
|
1961
1962
|
"regression-diagnostics",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "millwright"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
rust-version = "1.95"
|
|
6
6
|
description = "A unified ML framework for Rust — proven Rust crates, assembled into one machine."
|
|
@@ -57,6 +57,10 @@ polars = { version = "=0.55.2", default-features = false, features = ["csv", "pa
|
|
|
57
57
|
pyo3 = { version = "0.24", features = ["extension-module", "abi3-py39"], optional = true }
|
|
58
58
|
ndarray = { version = "0.16", optional = true }
|
|
59
59
|
rayon = { version = "1", optional = true }
|
|
60
|
+
# Decode a saved ONNX file back into the proto, to natively evaluate ONNX-ML ops
|
|
61
|
+
# (tree ensembles) that tract does not implement. Same version onnx-export-rs
|
|
62
|
+
# uses, so the generated proto types decode cleanly.
|
|
63
|
+
prost = { version = "0.13", optional = true }
|
|
60
64
|
plotters = { version = "0.3.7", default-features = false, features = ["svg_backend", "all_series"], optional = true }
|
|
61
65
|
tract-onnx = { version = "0.23", optional = true }
|
|
62
66
|
axum = { version = "0.8", optional = true }
|
|
@@ -146,6 +150,7 @@ onnx = [
|
|
|
146
150
|
"onnx-export-rs/smartcore-compat",
|
|
147
151
|
"dep:tract-onnx",
|
|
148
152
|
"dep:ndarray",
|
|
153
|
+
"dep:prost",
|
|
149
154
|
"smartcore-backend",
|
|
150
155
|
"smartcore/serde",
|
|
151
156
|
]
|
|
@@ -71,6 +71,7 @@ reg.tag(<span class="s">"demand"</span>, &v1.id, <span class="s">"prod"</spa
|
|
|
71
71
|
.with_monitor(<span class="f">DriftMonitor</span>::psi(&reference)?)
|
|
72
72
|
.serve(<span class="s">"0.0.0.0:8080"</span>).<span class="k">await</span>?; <span class="c">// POST /predict, GET /metrics</span></pre>
|
|
73
73
|
<p class="run">cargo run --example operations --features "onnx registry monitor serve"</p>
|
|
74
|
+
<div class="callout"><b>Serving any model.</b> The <code class="inl">Server</code> runs linear / NN ONNX graphs through <code class="inl">tract</code>, and evaluates ONNX-ML tree ensembles (a forest) with a small native interpreter — so a model exported by Millwright always serves in Millwright, and the artifact stays portable to any ONNX runtime.</div>
|
|
74
75
|
</div>
|
|
75
76
|
</section>
|
|
76
77
|
|
|
@@ -381,7 +381,7 @@ grid! {
|
|
|
381
381
|
<span class="c">// 1 — compose a pipeline: preprocessing + a model, one object</span>
|
|
382
382
|
<span class="k">let</span> pipe = <span class="f">Pipeline</span>::new()
|
|
383
383
|
.step(<span class="s">"impute"</span>, <span class="f">SimpleImputer</span>::median())
|
|
384
|
-
.step(<span class="s">"encode"</span>, <span class="f">
|
|
384
|
+
.step(<span class="s">"encode"</span>, <span class="f">OneHotEncoder</span>::infer())
|
|
385
385
|
.step(<span class="s">"scale"</span>, <span class="f">StandardScaler</span>::new())
|
|
386
386
|
.balance(<span class="f">Smote</span>::default()) <span class="c">// imbalance-rs · train-time only</span>
|
|
387
387
|
.estimator(<span class="s">"rf"</span>, <span class="f">RandomForest</span>::new());
|
|
@@ -392,15 +392,15 @@ grid! {
|
|
|
392
392
|
.scoring(<span class="f">Metric</span>::F1)
|
|
393
393
|
.fit(&train)?;
|
|
394
394
|
|
|
395
|
-
<span class="c">// 3 — assess & explain</span>
|
|
396
|
-
<span class="k">let</span> report = model.evaluate(&test)?; <span class="c">//
|
|
397
|
-
<span class="k">let</span> shap = model.explain(
|
|
395
|
+
<span class="c">// 3 — assess & explain the best model</span>
|
|
396
|
+
<span class="k">let</span> report = model.evaluate(&test)?; <span class="c">// accuracy · precision · recall · f1</span>
|
|
397
|
+
<span class="k">let</span> shap = model.explain(&<span class="f">Explainer</span>::kernel(), test.features())?; <span class="c">// shap-rs</span>
|
|
398
398
|
|
|
399
399
|
<span class="c">// 4 — ship it: one ONNX artifact, served, with drift on every request</span>
|
|
400
400
|
model.export_onnx(<span class="s">"churn.onnx"</span>)?; <span class="c">// onnx-export-rs</span>
|
|
401
401
|
|
|
402
|
-
<span class="f">Server</span>::from_onnx(<span class="s">"churn.onnx"</span>)
|
|
403
|
-
.with_monitor(<span class="f">DriftMonitor</span>::psi(&
|
|
402
|
+
<span class="f">Server</span>::from_onnx(<span class="s">"churn.onnx"</span>)? <span class="c">// forests run natively in Millwright</span>
|
|
403
|
+
.with_monitor(<span class="f">DriftMonitor</span>::psi(&reference)?) <span class="c">// driftwatch · training preds</span>
|
|
404
404
|
.route(<span class="s">"/predict"</span>)
|
|
405
405
|
.serve(<span class="s">"0.0.0.0:8080"</span>).<span class="k">await</span>?; <span class="c">// axum</span></pre>
|
|
406
406
|
<p class="tiny" style="margin-top:1rem">The <code class="inl">timeseries</code> feature swaps the estimator for a <span class="mono">chronos-ts</span> auto-ARIMA forecaster behind the same <code class="inl">fit</code>/<code class="inl">predict</code> contract; <code class="inl">incremental</code> swaps <code class="inl">.fit()</code> for <code class="inl">.partial_fit()</code> over batches that never fully load into memory.</p>
|
|
@@ -638,17 +638,17 @@ pipe.export_onnx(<span class="s">"churn.onnx"</span>) <span class=
|
|
|
638
638
|
<div>
|
|
639
639
|
<h3>Register, serve, roll back</h3>
|
|
640
640
|
<pre><span class="c">// version a trained pipeline — artifact + ONNX + metrics + lineage</span>
|
|
641
|
-
<span class="k">let</span>
|
|
642
|
-
|
|
643
|
-
|
|
641
|
+
<span class="k">let</span> reg = <span class="f">Registry</span>::local(<span class="s">"./models"</span>);
|
|
642
|
+
<span class="k">let</span> v = reg.register(<span class="s">"churn"</span>, &model, meta)?; <span class="c">// meta: metrics + reference dist</span>
|
|
643
|
+
reg.tag(<span class="s">"churn"</span>, &v.id, <span class="s">"prod"</span>)?; <span class="c">// a movable pointer</span>
|
|
644
644
|
|
|
645
645
|
<span class="c">// serve straight from the registry; the monitor uses the stored reference</span>
|
|
646
|
-
<span class="f">Server</span>::from_registry(<span class="s">"churn"</span>, <span class="s">"prod"</span>)
|
|
647
|
-
.with_monitor(<span class="f">DriftMonitor</span>::from_registry(&v))
|
|
646
|
+
<span class="f">Server</span>::from_registry(&reg, <span class="s">"churn"</span>, <span class="s">"prod"</span>)?
|
|
647
|
+
.with_monitor(<span class="f">DriftMonitor</span>::from_registry(&v)?)
|
|
648
648
|
.serve(<span class="s">"0.0.0.0:8080"</span>).<span class="k">await</span>?;
|
|
649
649
|
|
|
650
650
|
<span class="c">// when drift fires: retrain on the recorded lineage — or revert in one line</span>
|
|
651
|
-
|
|
651
|
+
reg.rollback(<span class="s">"churn"</span>, <span class="s">"prod"</span>)?;</pre>
|
|
652
652
|
</div>
|
|
653
653
|
<div>
|
|
654
654
|
<h3>Closing the loop</h3>
|
|
@@ -46,6 +46,15 @@ impl DriftMonitor {
|
|
|
46
46
|
})
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/// Build a monitor from the reference distribution a registry [`Version`]
|
|
50
|
+
/// stored at registration time.
|
|
51
|
+
///
|
|
52
|
+
/// [`Version`]: crate::registry::Version
|
|
53
|
+
#[cfg(feature = "registry")]
|
|
54
|
+
pub fn from_registry(version: &crate::registry::Version) -> Result<Self> {
|
|
55
|
+
Self::psi(&version.metadata.reference)
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
/// Record a batch of live predictions.
|
|
50
59
|
pub fn observe(&self, predictions: &[f64]) {
|
|
51
60
|
self.live
|