Thank you for your interest in contributing to scikit-rec! We welcome bug reports, feature requests, and code contributions.
- Fork the repository and create a new branch.
- Make your changes in a focused branch.
- Add tests for bug fixes and new functionality.
- Run the test suite locally:
git clone https://github.com/intuit/scikit-rec.git
cd scikit-rec
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/For PyTorch-based estimators (NCF, Two-Tower, SASRec, HRNN, DeepFM), install with the torch extra:
pip install -e ".[dev,torch]"- Commit with a clear message and open a pull request against
main.
- Keep pull requests small and focused.
- Follow the existing code style and conventions.
- Add or update documentation when behavior changes.
- Use descriptive commit messages.
If you find a bug or have a feature idea, please open an issue at:
https://github.com/intuit/scikit-rec/issues
Include a clear description of the problem, a minimal reproduction example, and the expected behavior.
scikit-rec uses a 3-layer architecture: Recommender → Scorer → Estimator. Each layer has a well-defined abstract base class. Adding a new component means subclassing the right base, implementing a small set of abstract methods, and (for factory-accessible types) registering the new type in skrec/orchestrator/factory.py.
skrec/
estimator/
base_estimator.py ← BaseEstimator (tabular)
classification/ ← tabular classifiers
regression/ ← tabular regressors
embedding/ ← BaseEmbeddingEstimator + embedding models
sequential/ ← SequentialEstimator + SASRec, HRNN
scorer/
base_scorer.py ← BaseScorer
universal.py, independent.py, multiclass.py, multioutput.py, ...
recommender/
base_recommender.py ← BaseRecommender
ranking/, bandits/, sequential/, gcsl/, uplift_model/
orchestrator/
factory.py ← create_recommender_pipeline + capability_matrix
Tabular classifier or regressor:
- Subclass
BaseClassifier(inskrec/estimator/classification/base_classifier.py) orBaseRegressor. - Implement
fit(X, y)andpredict_proba(X)/predict(X). - Add the file to
skrec/estimator/classification/orskrec/estimator/regression/. - To expose it via
create_recommender_pipeline, add a branch increate_estimator()infactory.pyand extendEstimatorConfigwith the new config key. Also add the new key tocapability_matrix().
Embedding estimator:
- Subclass
BaseEmbeddingEstimator(skrec/estimator/embedding/base_embedding_estimator.py). - Implement
fit_embedding_model(users, items, interactions, ...)andpredict_proba_with_embeddings(...). - Add a lazy entry to
_EMBEDDING_ESTIMATOR_MAPinfactory.py— no eager import needed.
Sequential estimator (SASRec-style):
- Subclass
SequentialEstimator(skrec/estimator/sequential/base_sequential_estimator.py). - Add a lazy entry to
_SEQUENTIAL_ESTIMATOR_MAPinfactory.py.
- Subclass
BaseScorer(skrec/scorer/base_scorer.py). - Implement
score_items(interactions, users, items)andscore_fast(features_df)if supportingrecommend_online. - Add it to
SCORER_TYPESinfactory.py, add a branch increate_scorer(), and add an entry (even an emptyfrozenset) to_SCORER_CONFIG_ALLOWED.
MixedTypeMultiTargetScorer is polymorphic over estimator families via the runtime-checkable MultiTargetEstimator Protocol. To add a fourth family (e.g. a tree-based joint model, an MoE encoder, etc.):
- Implement a class with the four attributes/methods on
skrec.estimator.classification._multi_target_protocol.MultiTargetEstimator:target_specs: dict[str, TargetType | TargetGroupSpec]attributefit(X, y, X_valid=None, y_valid=None)predict_proba_dict(X) -> dict[str, np.ndarray](multilabel groups fanned out)predict_targets_dict(X) -> dict[str, np.ndarray](multilabel groups fanned out)
- The scorer's
__init__Protocol check accepts your class automatically — no changes toMixedTypeMultiTargetScorerare required. - If the family fits the joint pattern (shared encoder + per-target heads), subclass
JointMultiTargetBaseEstimatorand supply an encoder via_build_encoder(input_dim, label_input_dim). Seejoint_multi_target_mlp.pyfor the minimal template. - To expose via the factory, add a mode to
MULTI_TARGET_MODEL_TYPESand a branch in_create_multi_target_estimator()infactory.py. - For
mode="independent"extensions (adding a new sub-estimator type), extend_INDEPENDENT_TARGET_COMPATand_create_independent_sub_estimator().
Gate 1 (tests/test_mixed_type_multi_target_gates.py) asserts every family satisfies the Protocol; add your class to that test when contributing.
- Subclass
BaseRecommender(skrec/recommender/base_recommender.py). - Implement
train(...),recommend(...), and optionallyscore_items(...). - Add it to
RECOMMENDER_TYPESinfactory.pyand add a branch increate_recommender().
- Subclass
BaseCandidateRetriever(skrec/retriever/base_retriever.py). - Implement
retrieve(interactions, users, items). - Add an entry to
_RETRIEVER_MAPinfactory.py.
Tests live in tests/. The main patterns:
- Unit tests (
test_base_*.py,test_*_scorer.py, etc.) use small synthetic fixtures defined intests/conftest.pyandtests/utils.py. - Integration tests (
test_*_integration.py) run a full train → evaluate cycle on sample data and are the primary correctness check. - Smoke tests (
test_estimator_smoke.py) instantiate every estimator and verify they don't error on a tiny dataset — useful for catching import or API regressions.
When adding a new component, add at minimum:
- A unit test covering the abstract interface.
- An integration test that trains and evaluates end-to-end.
- If the component is factory-accessible, a test in
test_orchestrator_factory.py.
S3-dependent tests (test_s3.py) use moto to mock AWS — no real credentials needed.
By contributing, you agree to abide by the project's Code of Conduct:
CODE_OF_CONDUCT.md
We aim to make this project welcoming, inclusive, and respectful.