Model Interface & Signature Contract¶
Purpose¶
Define the model's input/output contract, where it is enforced, what constitutes a breaking change, and what must be versioned together with the model artifact.
Why contracts matter¶
The model is consumed by two separate runtimes:
- Sync inference (POST /predict/) — features built at request time from pre-computed batch data.
- Batch lookup (GET /predict/{match_id}) — result from pre-computed batch_inference output.
In both cases, the feature vector must match the model's training schema exactly. A mismatch in feature names, order, or dtype causes a silent incorrect prediction or a runtime error. The contract is the only thing that makes this safe to evolve.
Input contract¶
The model accepts a flat feature vector. The expected feature set is determined at training time and recorded in the MLflow model signature.
Feature naming convention: {side}_{metric}_w{window}_{agg} for rolling stats,
e.g., diff_win_5_mean, diff_goals_for_3_mean, home_elo_pre, diff_rest_days.
The side is fixed at diff for all rolling stats (home − away differential) in the
current configuration (classification.side = "diff").
Categorical features: sex (0 = men's, 1 = women's competition), passed as a numeric
code and declared in cat_cols.
The full feature list is determined by features_meta.parquet — not hardcoded.
PredictRequest accepts a free-form dict[str, float | int | None] and validates against
the model signature at inference time.
Output contract¶
The model outputs a 3-class probability vector.
| Field | Type | Description |
|---|---|---|
predicted_class |
int |
Argmax of probabilities: 0 = Home Win, 1 = Draw, 2 = Away Win |
probabilities |
dict[str, float] |
Class probabilities keyed by outcome label string |
model_version |
str |
MLflow model stage/alias used for this prediction |
model_run_id |
str |
MLflow run ID for traceability |
Output schema is defined in src/app/schemas/predict.py (PredictionResult).
Where enforcement happens¶
| Layer | Mechanism |
|---|---|
| Model artifact | MLflow pyfunc model signature (inputs / outputs schema) |
| API request | Pydantic PredictRequest in src/app/schemas/predict.py |
| API response | Pydantic PredictionResult — must match model output structure |
| Batch inference | Feature column selection from features_meta.parquet |
The MLflow signature and Pydantic schemas form a two-layer enforcement boundary. Discrepancy between them is treated as a contract violation.
What is versioned with the model¶
Every MLflow model artifact includes:
- The serialised sklearn pipeline (including preprocessor + classifier).
- The model signature (input feature schema + output schema).
- Logged parameters:
features_metacolumn list,best.*hyperparameters. - The
data_versiontag linking to the DVC dataset hash. - The git commit that produced it.
This means a specific model version can be re-loaded and reproduced from first principles.
Breaking vs. non-breaking changes¶
Breaking changes — require a new model version:
| Change | Why it breaks |
|---|---|
| Adding or removing a feature column | Input schema changes; PredictRequest features no longer match |
| Renaming a feature column | Same as above |
Changing side from diff to home or away |
Column names and semantics change |
| Changing the output class encoding | PredictionResult.predicted_class semantics change |
| Changing preprocessing logic | Model was trained on different transformed values |
Non-breaking changes:
| Change | Why it is safe |
|---|---|
| Retuning hyperparameters with same feature set | Input/output schema unchanged |
| Retraining on new data with same feature set | Schema unchanged; new model version is registered |
Changing n_trials or frac in tuning |
Does not affect input/output contract |
When in doubt, register a new version. The registry supports rollback at zero cost.
Related¶
- Features — what determines the feature set
- MLflow — how signatures are logged
- Model Registry — how breaking changes trigger versioning
- Serving: API Contract