This release is a pre-release and may not be stable for production use.
FlexPredict
FlexPredict is a small, composable inference layer for tabular machine-learning models. It gives NumPy, scikit-learn-compatible, PyTorch, TensorFlow/Keras and custom models a consistent input and output contract, while allowing every model to keep its own schema and preprocessing pipeline.
FlexPredict 0.2 is an alpha redesign. It intentionally does not preserve the old
unipredictAPI.
Why FlexPredict?
Application inputs rarely look exactly like a model matrix. A service may receive one JSON object, a dictionary of columns or a list of records, while different models in an ensemble require different normalization, devices and output handling. FlexPredict composes those concerns without becoming a training framework or a model server.
Core properties:
- zero-configuration inference for array inputs;
- named and schema-validated tabular inputs;
- callable, sklearn-style and built-in preprocessing;
- canonical prediction results with shape
(n_samples, n_outputs); - lazy optional dependencies for PyTorch and TensorFlow;
- safe regression and classification ensembles;
- global ensemble weights from lists, arrays or
.npyfiles; - a registry for custom inference engines.
Installation
Development install:
git clone https://github.com/NewMrPotato/ml-flex-predict.git
cd ml-flex-predict
pip install -e .
Optional frameworks:
pip install -e ".[sklearn]"
pip install -e ".[torch]"
pip install -e ".[tensorflow]"
pip install -e ".[pandas]"
pip install -e ".[dev]"
The base package depends only on NumPy. Importing flexpredict does not import or
require PyTorch, TensorFlow, pandas or scikit-learn.
Quick start
Array input with no configuration
from flexpredict import Predictor
predictor = Predictor(model)
result = predictor.predict([[1.0, 2.0], [3.0, 4.0]])
print(result.values) # always a 2D NumPy array
print(result.task) # inferred for sklearn-compatible estimators
Output contract
PredictionResult.values is always a non-empty two-dimensional array. FlexPredict rejects
non-finite numeric output and applies the following dtype rules:
| Output kind | Supported values |
|---|---|
Regression or generic values |
finite real integers and floats |
probabilities |
finite real numbers between 0 and 1 |
logits |
finite real numbers |
Classification labels |
strings, booleans and finite real numbers |
Complex numbers and arbitrary Python objects such as dictionaries, nested lists and None
are rejected. A custom classifier returning string labels should declare its semantics when
they cannot be inferred:
predictor = Predictor(
custom_classifier,
task="classification",
output_kind="labels",
)
Named input
predictor = Predictor(model, features=["age", "income", "score"])
result = predictor.predict({
"age": 31,
"income": 150_000,
"score": 0.82,
})
print(result.single())
The same predictor accepts a dictionary of columns or a list of records:
predictor.predict({
"age": [31, 45],
"income": [150_000, 90_000],
"score": [0.82, 0.61],
})
predictor.predict([
{"age": 31, "income": 150_000, "score": 0.82},
{"age": 45, "income": 90_000, "score": 0.61},
])
features=[...] is a feature-selection shortcut: declared features are required, selected
from a named input and passed to the model in the declared order; unrelated fields are
ignored. This allows one shared request to feed ensemble members with different feature
subsets. Use an explicit InputSchema when extra fields must be rejected.
Pandas DataFrame input remains a DataFrame for generic and sklearn-compatible models.
Column names, their declared order and pandas dtypes are preserved, so sklearn pipelines
using ColumnTransformer with named columns or dtype selectors continue to work. PyTorch
and TensorFlow engines convert DataFrame input to a NumPy matrix before tensor inference.
Schema-aware input
Use an explicit schema only when validation is useful:
from flexpredict import FeatureSpec, InputSchema, Predictor
schema = InputSchema((
FeatureSpec("age", int, validators=(lambda value: 18 <= value <= 120,)),
FeatureSpec("income", float, validators=(lambda value: value >= 0,)),
FeatureSpec("score", float, default=0.5),
))
predictor = Predictor(model, schema=schema)
result = predictor.predict({"age": "31", "income": 150_000})
By default, values are coerced to their declared types and extra fields are rejected for an explicit schema. Errors identify the invalid or missing feature before the model is called.
Preprocessing
FlexPredict accepts a callable or an object with transform(X):
from flexpredict import Predictor, Standardizer
predictor = Predictor(
model,
features=["x1", "x2", "x3"],
preprocessor=Standardizer(
mean=[10.0, 20.0, 30.0],
std=[2.0, 5.0, 10.0],
),
)
Each member of an ensemble can use a different schema and preprocessor.
Ensembles and weights files
aggregation_weights are coefficients controlling the contribution of each model.
They are not the trained parameters inside a neural network.
from flexpredict import EnsemblePredictor
ensemble = EnsemblePredictor(
predictors=[predictor_a, predictor_b, predictor_c],
aggregation="weighted_mean",
aggregation_weights="models/global_weights.npy",
)
result = ensemble.predict(data)
Weights files are loaded with np.load(path, allow_pickle=False). The array must be
one-dimensional, contain one finite non-negative weight per predictor and have a positive
sum. Weights are normalized automatically and exposed as the read-only
ensemble.aggregation_weights array in predictor order. A Python list or NumPy array can
be passed instead of a file path.
Every member receives the original input independently, so its schema can select and order features for that model. Members must still agree on task, output kind, batch shape and classification classes before aggregation. Errors identify the failing member by index and name. Majority-vote ties are deterministic: the prediction from the earliest member wins.
Loading model artifacts
Complete sklearn/joblib and Keras artifacts can be turned into predictors directly:
sklearn_predictor = Predictor.from_file(
"models/random_forest.joblib",
features=["age", "income", "score"],
)
keras_predictor = Predictor.from_file(
"models/network.keras",
features=["age", "income", "score"],
)
PyTorch state dicts need a model factory because weights alone do not describe the Python architecture:
torch_predictor = Predictor.from_torch_weights(
"models/network_weights.pth",
model_factory=lambda: MyNetwork(input_size=3),
features=["age", "income", "score"],
task="regression",
engine_options={"device": "cuda", "dtype": "float32"},
)
PyTorch weights are loaded with weights_only=True. A complete trusted PyTorch model can
be loaded explicitly with loader="torch_model"; .pt and .pth are never guessed
because the same extensions are commonly used for incompatible artifact types.
Supported regression aggregations are mean, weighted_mean, median, min, max
and a callable. Classification probabilities can be averaged after verifying class
metadata. Class labels require the explicit voting strategy.
Classification
predictor = Predictor(
classifier,
features=["age", "income", "score"],
task="classification", # inferred for sklearn classifiers
)
labels = predictor.predict(data)
probabilities = predictor.predict_proba(data)
print(labels.values)
print(probabilities.values)
print(probabilities.classes)
For a PyTorch or Keras model whose regular forward output already contains
probabilities, configure output_kind="probabilities".
Engine options
Framework-specific runtime settings stay grouped and optional:
predictor = Predictor(
torch_model,
features=["x1", "x2", "x3"],
task="regression",
engine_options={
"device": "cuda",
"dtype": "float32",
},
)
Unknown engine options are rejected instead of being silently ignored.
Custom InferenceEngine implementations receive NumPy input by default. An engine that
intentionally supports pandas semantics can declare preserves_dataframe = True.
Development
python -m pytest
python -m pytest --cov=flexpredict --cov-fail-under=85
python -m ruff check flexpredict tests examples
python -m mypy flexpredict
python -m compileall -q flexpredict tests examples
python -m build
The fast unit suite requires only NumPy and pytest. Framework integration suites are kept separate so that schema and ensemble tests do not require heavy optional packages. The current fast suite enforces at least 85% coverage; this threshold will increase as the optional-framework integration matrix grows.
GitHub Actions additionally installs and tests the sklearn, torch and tensorflow
extras in isolated Python 3.11 jobs.
See the contribution guide for the development workflow and the release checklist for the alpha-release gate.
Security
Only load model artifacts and preprocessing objects from trusted sources. Python pickle, joblib and some framework serialization formats may execute code while loading. NumPy ensemble-weight files are always loaded with pickle support disabled.
License
MIT
Metadata
Release files for flexpredict 0.2.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flexpredict-0.2.0a1.tar.gz | 33.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flexpredict-0.2.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.3 kB
Release files / flexpredict-0.2.0a1.tar.gz
| Download URL | flexpredict-0.2.0a1.tar.gz |
|---|---|
| Size | 33.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e1d888ef35161299d6e9e963145303525225e4c3ea32da9cce981c4ffa660224
|
|
BLAKE2b-256 checksum How to use checksums |
ef7594387508f4af5cdc3c8c0dd3e4409282f1100ede0479617c1fdec4e25240
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency logRelease files / flexpredict-0.2.0a1-py3-none-any.whl
| Download URL | flexpredict-0.2.0a1-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12b5459b10385ada391a3f7eabf1ba22d7c4eb5d79d8b4e581d8bb386c19b872
|
|
BLAKE2b-256 checksum How to use checksums |
31ae0da9426a8921f1d93b88279f5d37db1f126ec5c7d7d377ac52880b720499
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency log