Skip to content

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_meta column list, best.* hyperparameters.
  • The data_version tag 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.