2026 builds on the foundation of :ref:`2025 <2025>` to improve the ergonomics of data access, querying, and metrics, and clean up some APIs that seemed good at the time, while making it even easier to use LensKit for recommendation scenarios besides ID-based personalized recommendation.
There are no new major paradigm shifts, though — pipelines, datasets, and components work as they do in the 2025 series, but with more features, some rough corners polished off the interfaces, and hopefully fewer bugs.
Third release in 2026, with more feature updates and bug fixes. A full list of fixed issues and merged PRs is on milestone 2026.3.
Important
This release updates :meth:`lenskit.data.DatasetBuilder.filter_interactions` to explicitly and consistently handle time zones, independent of the local time zone. In some cases, such as filtering by a numeric timestamp, the previous code was (incorrectly) time-zone-dependent.
- Fixed bug in FlexMF's
"misranked"sampler (for WARP), where it did not correctly continue sampling (:pr:`1121`). - Fixed a bug in :class:`~lenskit.data.ItemListCollection` where item lists missing fields would fail to convert to Arrow (:issue:`1164`, :pr:`1165`).
- Made time zone handling in :meth:`lenskit.data.DatasetBuilder.filter_interactions` explicit (:pr:`1162`). This may affect filter results by a few hours, because previous behavior was unspecified and time-zone-dependent.
- Upgraded to Ruff 0.16 and fixed new default lints, including upgrade lints (:pr:`1161`). This also improves the handling modern type aliases in a few places.
- Reworked type checking logic in the pipeline to fix type bugs with NumPy 2.5, and hopefully reduce brittleness (:pr:`1120`).
- Added initial support for importing Steam user/item data (:py:mod:`lenskit.data.sources.steam`, :pr:`1111`).
- Support simultaneous multithreaded running in accelerator tasks (:issue:`1124`, :pr:`1125`).
- Fix Torch compilation support bugs (:pr:`1113`).
- Optuna fixes for random seeds, continuing when a trial fails, collecting results (:pr:`1132`, :pr:`1134`).
- Added support for boolean and categorical hyperparameters to the tuner (:pr:`1139`).
- Added
"pow2"scale for integer hyperparameters to select powers of 2 (:pr:`1144`). - Optuna now includes the default hyperparameters as the first ones tested (:pr:`1145`).
- Moved configuration file schemas to :mod:`lenskit.schemas` (:pr:`1148`).
- Expose batch inference results as a row-by-row iterator (:pr:`1128`).
- Certain per-request warnings in batch inference are now aggregated (:issue:`1113`, :pr:`1168`).
- Added :class:`lenskit.metrics.UniqueItemCount` (:issue:`1005`, :pr:`1169`).
- Added support for
interaction="default"to :meth:`~lenskit.data.DatasetBuilder.add_relationship_class` (:pr:`1174`). - Fix Steam dataset import to properly import interactions (previously was incomplete and broken, :issue:`1176`, :pr:`1179`).
Bug-fix release (with one very small feature).
- Fixed correct running of CLI when CLI plugins cannot be imported.
- Fixed graded NDCG when input item lists are empty and do not have the grade field (:issue:`1095`, :pr:`1096`).
- Added support for PyArrow tables to :meth:`lenksit.data.DatsetBuilder.add_scalar_attribute` (:pr:`1109`).
Second release in the 2026 series, bringing a few smaller feature updates:
- Added :meth:`~lenskit.data.EntityAttribute.list` and :meth:`~lenskit.data.EntityAttribute.value` accessors to entity attributes.
- Improved typecasting of entity IDs in :meth:`lenskit.data.Vocabulary.numbers`.
- Added JSON export to :class:`~lenskit.data.ItemList` and :class:`~lenskit.data.ItemListCollection` (:pr:`1089`).
- Added
--releated-itemsand--jsonoptions to thelenskit recommendcommand (:pr:`1087`). - Added a new scorer, :class:`lenskit.knn.AssociationScorer`, implementing conditional probability and lift-based association rule recommendations for both personalized and related-item recommendations (:issue:`930`, :pr:`1086`).
- Fixed a bug in pipeline merging that required derived pipelines to specify component classes in order to configure component options (:issue:`1097`, :pr:`1099`).
- Added a
lenskit pipeline expandcommand to render a pipeline configuration to complete JSON for debugging / inspection (:issue:`1098`, :pr:`1099`). - Added a new
lenskit pipeline diagramcommand to render a pipeline configuration to a Mermaid diagram for visualization (:pr:`1100`). - Re-add support for Pandas 2.2 to improve Google Colab support (:issue:`1103`, :pr:`1104`).
- Handle parameterized types for component inputs (:issue:`1101`, :pr:`1102`).
PRs and issues are listed in milestone 2026.2.
This is the first major feature and update release of the 2026 series! Key highlights:
- The :class:`~lenskit.training.Trainable` protocol now has a method to query whether a model has been trained, instead of relying on every trainable to correctly implement skip-on-retrain logic.
- Batch inference inputs are simplified and more explicit.
- Cleanup of several deprecated interfaces, and simplifying the public module hierarchy.
- Dependency updates with SPEC0.
- FlexMF is nearing stability, and will be stabilized in an upcoming 2026.x point release. It also supports LightGCN.
- Updated default embedding sizes to a power of 2 (64) throughout LensKit.
- Improved metric interface to better support complex metric aggregations. Some automatic aggregation and analysis is no longer supported or will soon be removed (:class:`~lenskit.metrics.RunAnalysis` is deprecated), in favor of clear metric collection within a single run and letting the user aggregate and summarize in whatever tool they prefer.
Additional changes are described here. See the milestone GitHub release for further change-level details.
These are the major changes that will affect users who are writing code to generate recommendations with LensKit, or implementing recommendation components.
Many submodules (such as most modules under :mod:`lenskit.pipeline`) have been renamed to be private modules. Code importing from their original locations will need to be updated to import from the higher-level module (:issue:`947`).
The :class:`~lenskit.training.Trainable` protocol now has a separate method :meth:`~lenskit.training.Trainable.is_trained` to query if a component has been trained, and responsibility for skipping retraining of already-trained components when :attr:`~lenskit.training.TrainingOptions.retrain` is
Falsehas been moved from individual components to :meth:`lenskit.pipeline.Pipeline.train` (:issue:`1042`, :pr:`1044`). Components that implement :class:`~lenskit.training.Trainable` need to implement the new method as well.The pipeline builder will issue a warning if a component implements :meth:`~lenskit.training.Trainable.train` but not :meth:`~lenskit.training.Trainable.is_trained`.
Default embedding sizes for all embedding-based models (matrix factorizers, etc.) have changed to be 64 instead of 50, with the exception of :mod:`lenskit.implicit`, which continues to default to Implicit's defaults (:issue:`846`).
:class:`~lenskit.metrics.RunAnalysis` is deprecated, and no longer supports explicit defaults or global metrics. Use :class:`~lenskit.metrics.MeasurementCollector` instead. Its global metric columns have also changed in some cases.
LensKit now requires Python 3.12.5 or newer, along with NumPy 2.x, Pandas 2.3 or newer, and SciPy 1.13 or newer (see :ref:`dep-policy`, :pr:`954`). Earlier versions of Python 3.12 have a bug in the standard library that affects LensKit.
Stopped providing wheels for macOS on Intel. Users who still need to run LensKit on Intel-based Macs should use the Conda packages (available in conda-forge and prefix.dev).
Removed the deprecated unrated and all-items candidate selectors, in favor of :class:`~lenskit.basic.TrainingItemsCandidateSelector` (:issue:`935`).
Several input types to batch inference are no longer supported, to reduce ambiguity and implicit behavior (:issue:`958`, :pr:`1074`).
These changes are more minor, and won't directly affect most users who are using LensKit components and metrics as-is.
- Pipeline components and inputs now have restrictions on their names, and cannot
have names beginning with
_. See :ref:`pipeline-names` for details. - Pipeline configurations serialized with previous versions cannot be re-loaded in LensKit 2026, due to moves of module paths. Import path canonicalization (:issue:`948`) reduces the risk of such breakage in future releases. Handwritten configurations will often still work.
- The
AttributeSetclass has been renamed to :class:`~lenskit.data.EntityAttribute` (:issue:`946`), to better clarify its design and role. - We no longer publish 32-bit binary wheels.
- Removed
DecomposedMetric, as the :class:`~lenskit.metrics.Metric` interface is now decomposed. All metrics based on listwise measurements or intermediate results should directly extend fromMetricor :class:`~lenskit.metrics.ListMetric`. (:pr:`983`) GlobalMetrichas been removed. Code needing to compute global metrics should directly compute over item lists.- :class:`~lenskit.metrics.MeasurementCollector` no longer supports defaults when adding metrics.
- :class:`~lenskit.metrics.ListGini` and :class:`~lenskit.metrics.ExposureGini` now require an item vocabulary or dataset as input.
- Removed the deprecated
lenskit.training.IterativeTrainingbase class in favor of :class:`lenskit.training.UsesTrainer`. - Component class members have been renamed to no longer use the Scikit-Learn
pattern of trailing
_in their names. - Removed the
lenskit.stats.argtopnfunction (:issue:`833`). :meth:`lenskit.data.ItemList.top_n` now uses a Rust-accelerated top-N implementation (:pr:`1049`). - :meth:`lenskit.data.ItemList.to_arrow` no longer accepts mappings for its
optional
columnsargument, sequences of names are used instead (:pr:`1052`). - Removed the
noption to :func:`~lenskit.pipeline.predict_pipeline` (:issue:`835`). - Revised :class:`~lenskit.data.RecQuery` to only accept keyword arguments, and
removed the legacy
user_itemsattribute.
- Added environment variables to :class:`~lenskit.training.TrainingOptions`, along with the :meth:`~lenskit.training.TrainingOptions.env_var` method to query them. This allows for open-ended configuration of training processes.
- Added support for free-threaded Python, including binary distributions for Python 3.14t on Linux and macOS (:issue:`916`, :pr:`1022`).
- Added support for parallel batch inference using thread pools on free-threaded Python (:issue:`921`, :pr:`1025`).
- Added LightGCN support to FlexMF (:issue:`1019`, :pr:`1033`).
- Added SLIM and fsSLIM to :mod:`lenskit.knn` (:issue:`894`, :pr:`896`).
- :meth:`lenskit.data.RelationshipSet.co_occurrances` is much faster and uses parallel computation, at the expense of increased memory use in the symmetric (non-ordered) case (:issue:`970`, :pr:`1007`).
- :func:`lenskit.metrics.call_metric` has been renamed to :func:`~lenskit.metrics.measure_list`, and the old name preserved as a deprecated alias.
- Pipeline type-checking for ArrayLike component inputs no longer works, due to a breaking change in NumPy 2.4. No LensKit components used ArrayLike as an input or output data type.
- Pipeline component inputs with default values can now have missing inputs (:issue:`1000`, :pr:`1001`).
- Rust progress updates now use a background thread to simplify logic and keep locks out of the work path (:pr:`1008`).
- Fixed a bug saving item list collections of emtpy item lists to Parquet files (:issue:`1051`, :pr:`1052`).