Version 1 composite pipelines
Normative source: docs/format-pipeline.md. This page renders that file unchanged; the guides explain it, this text defines it.
Normative section of the format contract. This format extension preserves reading of standalone bundles. Older readers reject composites.
Representation and declarations
Only an exact outer sklearn.pipeline.Pipeline ending in a supported exact
XGBClassifier/XGBRegressor or CatBoostClassifier/CatBoostRegressor is composite.
All-scikit-learn pipelines retain their single skops member and null structure.
Every native adapter’s standalone scope, preflight, claims and structure rules apply.
No native estimator or booster type is added to the skops trust list.
model.library is sklearn, serialization is pipeline, estimator_class
is Pipeline, and state_sha256 is null. Kind, class/output counts and target
labels equal the native component claims. model.n_features and manifest
features describe original inputs. Outer hyperparameters have exactly memory
(null), verbose (boolean), transform_input (null). ONNX fields are absent/null.
model.pipeline is required exactly for this serialization. Its strict frozen
records reject unknown fields and coercions. The record contains:
step_names: 1–128 unique strings, 1–256 characters, without__or any ofsteps,memory,verbose,transform_input. Names are data.transformers: null for one step; otherwisemember="model.skops",estimator_class="Pipeline",hyperparametersand non-nullstate_sha256. Parameters use the existing sklearn projection, including$estimator_refin constructor collections and full records in named deep keys, with the existing 4,096-key, 1 MiB and depth-16 bounds.estimator:library(xgboostorcatboost), pairedserializationandmember, plus the complete sharedModelClaimsrecord underclaims. No ONNX fields or absent classifier labels are admitted.transformed_features:n_features(1–100,000), ordered uniquenames(1–1,024 characters each),origin(transformer,model,input,generated). Width equals the native component feature count. Generated names are exactlytransformed_feature_1throughtransformed_feature_N.
Final step names and outer options are reconstruction declarations. Prefix names/order/options are also verified against the loaded skops member. Rebuilding declarations preserves them; it does not authenticate their historical values. No manifest class name, parameter projection or step name selects loading code.
Example (native claims and transformer parameters shortened for readability):
{ "library":"sklearn", "serialization":"pipeline", "estimator_class":"Pipeline", "kind":"classifier", "n_features":3, "n_classes":[2], "n_outputs":1, "state_sha256":null, "hyperparameters":{"memory":null,"verbose":false,"transform_input":null}, "pipeline":{ "step_names":["scale","est"], "transformers":{ "member":"model.skops", "estimator_class":"Pipeline", "hyperparameters":{"steps":[["scale",{"$estimator_ref":"StandardScaler"}]]}, "state_sha256":"506feb7a8fd45118e204d24081be7ec4d8b8998e73fa4581863741fb1852533f" }, "estimator":{"library":"xgboost","serialization":"ubjson","member":"model.ubj", "claims":{"estimator_class":"XGBClassifier","n_features":3}}, "transformed_features":{"n_features":3,"names":["a","b","c"],"origin":"transformer"} }}Members and component I/O
Standalone bundles have exactly one of the four model members. A composite
with a prefix has exactly model.skops plus model.ubj or model.cbm.
A single-step composite has only its native member and null transformers.
Passthrough/None preceding steps still require the prefix member to preserve
the recipe. A pair without its declaration, a missing declared member, ONNX
pairs, two boosters or three model members fail. Manifest integrity covers
exactly the declared model members, required schema and referenced optional members.
Composite structure.json and schema are required; explicit samples are optional.
Six members permit all with the two model members and manifest. Every other
archive ceiling is unchanged. Callers may lower member count and other limits.
Export gates scope/options before serialization. The leaf adapter receives
ExportDeclarations, prepares its native copy where required, serializes and
deserializes bytes with its own preflight. Shared claims and native structure
must survive that round trip. The prefix is a transformer-only exact Pipeline:
the shared skops byte route applies the owned trust/catalog, payload/buffer
bounds and payload-versus-reserialized commitment comparison. It checks fitted
transformers and projects their parameters with the standalone owner. Each component
reuses verified snapshots within the synchronous operation, as defined in
state verification; public calls never share that state.
Shared composite extraction verifies fitted widths and names, recomputes the recipe and outer claims for both original and restored components, and compares canonical bytes. The reconstructed Pipeline and extended native structure must survive the full comparison before atomic publication. Nothing fits a model, transforms sample/dummy rows as verification, or changes the caller’s pipeline.
Load first validates container/manifest/integrity and decodes each JSON member once. Schema/samples remain core-owned. The native leaf and optional prefix load from bytes through their own checked routes. A constant imported Pipeline class combines the loaded steps; no manifest-directed import or constructor is used. The writer’s extraction recomputes every component/outer claim and the extended structure. Only exact canonical agreement permits a loaded result.
Fitted widths and feature names
Widths derive from fitted state without data. Every effective transformer input width agrees with the preceding output. Intermediate/final widths are positive and bounded by 100,000; an empty ColumnTransformer branch contributes zero. Nesting is at most 16. Input width/names come from the first effective fitted transformer, or the native leaf for an identity-only prefix/single-step pipeline.
The closed exact classes and rules are:
| Transformer | Width rule and consistency checks |
|---|---|
| StandardScaler, MinMaxScaler, MaxAbsScaler, RobustScaler, Normalizer | Preserve width; fitted vectors/options and feature dimensions agree. |
| FunctionTransformer | Identity or the nine owned NumPy elementwise functions in state; preserves width. Nonempty kw_args/inv_kw_args are excluded. |
| PCA | components_.shape[0] equals bounded n_components_; component input width and fitted mean/variance/singular-value vectors agree. |
| VarianceThreshold | Count true entries in the validated fitted support mask; variance vector has input width. |
| SelectKBest, SelectPercentile, SelectFpr, SelectFdr, SelectFwe, GenericUnivariateSelect | Count the checked support mask; fitted scores/p-values align; score functions remain the owned closed list. |
| SimpleImputer | Keep input width when keep_empty_features; otherwise count non-NaN fitted statistics, including dropped empty columns. Add the checked MissingIndicator feature count when requested. |
| KBinsDiscretizer | ordinal preserves width; onehot/onehot-dense sum fitted n_bins_. Edge lengths/order and the internal encoder counts/categories/sparsity agree. |
| PolynomialFeatures | Bounded combinatorial count from degree range, interaction-only and bias options; checked against both fitted output counts. No powers array expansion for width proof. |
| OneHotEncoder | Sum category counts after checked infrequent grouping and drop state; cross-check cached output counts, mapping lengths/indices, and original/grouped drop indices. |
| OrdinalEncoder | Preserve width; categories, missing indices and infrequent mappings align. |
| Pipeline, ColumnTransformer | Recursively compose/sum the closed rules. ColumnTransformer selectors, cached input indices, remainder complement, branch names/order, declared/fitted parameters, sparsity flag and output slices agree. |
"passthrough", None |
Preserve width and recipe position. |
Other transformers (including KNNImputer and FeatureUnion) have no composite width rule. Existing skops/parameter restrictions also apply: callable selectors, scalar/slice-valued constructor selectors and arbitrary function/type parameters have no supported projection. Catalog admission alone does not guarantee export.
Effective original names use the existing model/dataframe/declared/generated
rules. Checked get_feature_names_out methods propagate them; known elementwise
operations propagate directly. Otherwise use verified native leaf names, then
generated transformed names. Names must match width and remain unique/bounded. Polynomial names are generated
without dense powers_ arrays, with at most 100,000 term visits. ColumnTransformer
verbose_feature_names_out must be boolean; arbitrary format expansion is excluded.
Native names/dtypes stay separate in leaf claims: null XGBoost names or positional
CatBoost names are preserved. Native tree dumps are never renamed. This supplies
display coordinates, not inverse transformations or original-feature attribution.
Structure, prediction and inspection
All native structure fields remain unchanged, with one sibling pipeline:
"pipeline": { "step_name":"est", "features":{"n_features":3,"names":["a","b","c"],"origin":"transformer"}}Family remains xgboost/catboost. Tree indices/importances address transformed features. This extension is recomputed and allowed only for composites. Stored structure bytes are compared with fresh canonical extraction through the existing single-decode owner. Legacy all-sklearn structure remains null.
LoadedBundle.native_model is a real Pipeline. Bundle input features/schema/
dtypes/samples describe original inputs. The composite predictor checks original
rank/width and numeric cells, admitting strings only for supported fitted encoders;
it transforms once, checks unchanged row count and delegates to a cached validated
native predictor. Native input/output checks, probability tolerances and prediction
settings remain authoritative. Single-step prediction delegates directly, preserving
native categorical admission. Transform uses default global output and disabled
metadata routing while retaining explicit per-step default/pandas output.
For XGBoost, bundle probability columns use bound original labels while the native
Pipeline’s classes/predict remain encoded. CatBoost retains its native labels and
rejects class_labels=. Mutating a loaded native object invalidates verification.
Inspect imports no model library and attests only metadata/integrity. CLI inspect and verify display escaped/bounded steps, final estimator, input/transformed widths and transformed names. Inspect retains its unverified-model notice; JSON includes the complete typed recipe. Verify loads and checks both parts and structure.
Rejections
Existing skops/native/schema/sample/resource messages retain their owners.
Recipe/pairing failures use InvalidManifestError: Invalid mlbundle manifest.
Candidate model sets use InvalidContainerError: Invalid model member combination.
Actual/reference disagreement remains Members disagree with manifest references.
Canonical disagreement uses ClaimMismatchError with ordinary outer fields,
pipeline.transformers, pipeline.step_names, pipeline.estimator.<field>,
pipeline.transformed_features, or structure. Unsupported native placements
list sanitized unsupported types after the placement message below.
The new scope, state and prediction messages are:
Composite models require an exact outer Pipeline with 1 to 128 steps.Pipeline step names must be unique bounded sklearn names.Unknown outer Pipeline state.Composite Pipeline memory must be None; caching is unsupported.Composite Pipeline verbose must be a boolean.Composite Pipeline transform_input and metadata routing are unsupported.Composite export requires default global transform_output; use per-step set_output.Composite transformers support only default or pandas set_output.Native estimators are supported only as the last step of the outermost Pipeline.Composite final estimator must be a supported XGBoost or CatBoost wrapper.Transformer member must be a nonempty exact Pipeline.Transformer component contains a non-transformer.Composite transformers must be fitted.Composite input width cannot be recovered from fitted state.Transformer output width cannot be verified without data.Transformer nesting exceeds the limit.Transformer feature-name work exceeds the limit.Invalid transformer feature width.Transformer input widths disagree.Transformer fitted dimensions disagree.Invalid ColumnTransformer input mapping.Invalid ColumnTransformer output mapping.ColumnTransformer declared and fitted transformers disagree.Transformer output width disagrees with final estimator.Invalid transformed feature names.Transformed names and width disagree.Invalid composite Pipeline state.(bounded unexpected-state wrapper)Composite Pipeline transformation failed.Composite transformation must preserve prediction rows.
Native estimators in ColumnTransformer, FeatureUnion, stacking, voting, calibration, search, TransformedTargetRegressor, nested pipelines or any nonfinal position remain excluded. Caching, metadata requests, non-null transform_input, unknown outer state and unverifiable fitted widths fail before publication. Separate byte preflights do not sandbox native parsers or prediction. Host process, memory/CPU/row limits and dependency maintenance remain necessary.
