Recommender¶
Recommendation engine internals from musicseed-core (musicseed.recommender.*). Production
retrieval scores all eligible non-seed tracks in scalar SQL batches and retains an exact,
artist-constrained top-k. Only seeds and selected tracks load ORM graphs. Average uses one
library scan; frequency uses one per distinct seed and ranks mean scores, then votes, then IDs.
See recommendation resolvers for the full flow and retrieval decisions for measurements and limits.
Scoring¶
musicseed.recommender.scoring
¶
Scoring primitives for MusicSeed recommendations.
SignalStatus = Literal['observed', 'neutral_missing', 'not_applicable', 'missing', 'mixed', 'unknown']
module-attribute
¶
Availability of one scoring signal.
observed means the component reflects real data; neutral_missing means
it fell back to the neutral 0.5 because required data was absent (no sonic
vector, unknown popularity or year); not_applicable means the seed profile
has no tag basis (empty seed styles or genres), retaining the weighted 0.5.
missing labels absent candidate tags whose historical score is
zero (not a neutral fallback). mixed aggregates different evidence states;
unknown means an older score supplied no availability metadata.
Weights
¶
Bases: BaseModel
Normalized recommendation weights.
Popularity means proximity to the seed popularity, not an absolute boost. Artist diversity is enforced as a selection constraint, not as a score component. Weights are normalized by their sum at scoring time, so absolute values only control relative importance.
SeedProfile
¶
Bases: BaseModel
Aggregated recommendation signals from one or more seed tracks.
ScoreBreakdown
¶
Bases: BaseModel
Component-level score details for explainable service and surface output.
availability describes each component's evidence, not a confidence probability:
observed (a real comparison), neutral_missing (neutral fallback),
not_applicable (no seed basis), missing (absent candidate tags),
mixed (different per-seed evidence states), or unknown (legacy score).
SonicCoverage
¶
Bases: BaseModel
How many scored candidates have a usable (finite, nonzero) sonic vector.
This is candidate-side availability; a missing/unusable seed profile can still make sonic comparisons neutral. Per-track availability explains that distinction. Stored zero vectors do not count as usable coverage.
has_usable_vector(value)
¶
Whether a vector can supply finite, nonzero sonic evidence.
sonic_comparable(left, right)
¶
Both sides must be usable and have matching dimensions.
cosine_similarity(a, b)
¶
Return cosine similarity normalized from [-1, 1] into [0, 1].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
Sequence[float] | ndarray | None
|
first vector; None or empty means "unknown". |
required |
b
|
Sequence[float] | ndarray | None
|
second vector; None or empty means "unknown". |
required |
Returns:
| Type | Description |
|---|---|
float
|
The normalized similarity, or the neutral |
float
|
is missing, zero-norm, non-finite, or dimensionally incompatible. |
jaccard(left, right)
¶
Return the Jaccard similarity of two tag sets.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
set[str]
|
first tag set (e.g. seed styles). |
required |
right
|
set[str]
|
second tag set (e.g. candidate styles). |
required |
Returns:
| Type | Description |
|---|---|
float
|
|
float
|
when both sets are empty (no information either way) and |
float
|
when exactly one side is empty (historical missing-candidate policy, |
float
|
not proof of an observed mismatch). |
average_or_none(values)
¶
Return the mean of the non-None values, or None when there are none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Iterable[float | int | None]
|
numbers, any of which may be None. |
required |
Returns:
| Type | Description |
|---|---|
float | None
|
The arithmetic mean over the concrete values, or None when every |
float | None
|
value is None. |
popularity_value(normalized, spotify)
¶
Project the existing normalized-first/provider-fallback policy to 0-100.
track_popularity_value(track)
¶
Return the best available popularity value on a 0-100 scale.
build_seed_profile(seed_tracks, vectors)
¶
Aggregate one or more seed tracks into a single recommendation profile.
The sonic embedding is the element-wise mean of the seed vectors (None when no seed has a vector); styles and genres are the union across seeds; year and popularity are averaged over the seeds that have them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed_tracks
|
Sequence[Track]
|
the resolved seed tracks. |
required |
vectors
|
SonicVectors
|
the query-time Plex sonic vector store. |
required |
Returns:
| Type | Description |
|---|---|
SeedProfile
|
The aggregated seed profile used for candidate generation and |
SeedProfile
|
scoring. |
popularity_proximity(seed_popularity, candidate_popularity)
¶
Score how close a candidate's popularity is to the seed's.
Popularity is a proximity signal, not an absolute boost: a candidate at the seed's popularity scores 1.0, and the score decays linearly to 0.0 at 100 popularity points of distance (the full 0-100 scale).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed_popularity
|
float | None
|
seed popularity on a 0-100 scale, or None. |
required |
candidate_popularity
|
float | None
|
candidate popularity on a 0-100 scale, or None. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Proximity in [0, 1], or the neutral |
float
|
unknown. |
era_proximity(seed_year, candidate_year)
¶
Score how close a candidate's release year is to the seed's.
Same-year releases score 1.0; the score decays linearly to 0.0 at 50 years of distance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed_year
|
int | None
|
seed release year, or None. |
required |
candidate_year
|
int | None
|
candidate release year, or None. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Proximity in [0, 1], or the neutral |
float
|
unknown. |
novelty_score(play_count)
¶
Score a candidate's novelty from its local play count.
Never-played tracks score 1.0; the score decays as 1 / (1 + 0.2 *
plays), so 5 plays yields ~0.5 and 20 plays ~0.2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
play_count
|
int | None
|
local play count; None is treated as 0. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Novelty in (0, 1]. |
calculate_score(candidate, seed, weights, vectors)
¶
Score one candidate against a seed profile on all six signals.
Components lie in [0, 1]. Missing sonic/popularity/year uses neutral 0.5. Absent seed tags also use 0.5; absent candidate tags with a tagged seed keep the historical score of zero, explicitly labelled missing rather than an observed mismatch. The total is the weighted mean of the components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
candidate
|
Track
|
the track to score. |
required |
seed
|
SeedProfile
|
the aggregated seed profile. |
required |
weights
|
Weights
|
per-signal weights. |
required |
vectors
|
SonicVectors
|
the query-time Plex sonic vector store. |
required |
Returns:
| Type | Description |
|---|---|
ScoreBreakdown
|
The total score plus every component score for explainability. |
score_signals(*, candidate_styles, candidate_genres, play_count, candidate_vector, candidate_popularity, candidate_year, seed, weights)
¶
Score scalar facts without an ORM graph; shared with calculate_score.
Component formulas, availability and normalization are identical for the streaming and ORM adapters. No scoring policy is changed by retrieval.
Eligible retrieval and constrained selection¶
musicseed.recommender.retrieval
¶
Exact constrained top-k scoring of streamed scalar metadata, without ORM graphs.
ScoredTrack
dataclass
¶
Only selected IDs, artist identity and explanations survive the scalar scan.
rank
property
¶
Rank by score, frequency vote count where relevant, then lower local ID.
ConstrainedTopK
¶
Exact streaming top-k under a per-artist cap, with O(limit) retained scores.
An incoming track displaces the worst from its artist if that group is full; otherwise it displaces the global worst if the selection is full. This is the exchange rule for a cardinality-truncated partition constraint and is equivalent to globally sorting then applying the same artist cap.
score_eligible_tracks(session, seed, vectors, *, limit, weights, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, exclude_ids=None)
¶
Filter years, exclude all seeds, score scalar batches and retain exact top-k.
No candidate budget truncates eligibility. SQL reads only scoring columns, stats and tag names. Seeds are excluded before tags/scoring/selection, using membership rather than an unbounded SQL IN list. The caller materializes only the final selected tracks. No artist/album/mood/history graphs are read.
Playlist¶
musicseed.recommender.playlist
¶
Playlist recommendation orchestration.
RecommendMethod = Literal['average', 'frequency']
module-attribute
¶
Seed aggregation strategies: "average" scores every eligible track
against the seeds' mean profile in one scan; "frequency" gathers per-seed
votes and ranks by their average score with vote count as a tiebreaker.
Recommendation
¶
Bases: BaseModel
One recommended track with its score breakdown and candidate sources.
resolve_seed_tracks(session, *, seed_texts=None, seed_ids=None)
¶
Resolve seed IDs and seed text queries into loaded Track objects.
Text seeds accept "Artist - Title" or a bare title; exact
case-insensitive matches win, otherwise a substring search must narrow to
exactly one track. Results are deduplicated, preserving input order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
seed_texts
|
Sequence[str] | None
|
seed tracks as text queries. |
None
|
seed_ids
|
Sequence[int] | None
|
seed tracks by local database id. |
None
|
Returns:
| Type | Description |
|---|---|
list[Track]
|
The resolved seed tracks with artist, album, tags, and stats eagerly |
list[Track]
|
loaded. |
Raises:
| Type | Description |
|---|---|
ValueError
|
when a seed id or text matches no track, a text seed is ambiguous (multiple matches), or no seeds were given at all. |
recommend_from_profile(session, seed_profile, vectors, *, limit, weights, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, exclude_ids=None)
¶
Score eligible scalar facts and materialize only the selected ORM tracks.
recommend_frequency(session, seed_tracks, *, limit, per_seed_limit=30, weights, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, vectors, exclude_ids=None)
¶
Recommend tracks voted for by multiple individual seed tracks.
Each seed is used as its own single-track profile to score all eligible
candidates; every seed (and any exclude_ids) is excluded up front, and
one vector-cache snapshot is reused across seeds. This costs one scalar
scan per seed; prefer average mode for large seed sets. A candidate's
score is the average of its per-seed scores across every seed that
recommended it (its "votes"); results are ranked by that average score,
with vote count as a tiebreaker, so limit cuts at the highest-scoring
candidates. Each recommendation's sources lists the voting seed ids.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
seed_tracks
|
list[Track]
|
resolved seed tracks, each used as an individual seed. |
required |
limit
|
int
|
maximum number of recommendations to return. |
required |
per_seed_limit
|
int
|
candidates gathered per seed. |
30
|
weights
|
Weights
|
signal weights. |
required |
year_min
|
int | None
|
only recommend tracks released in this year or later. |
None
|
year_max
|
int | None
|
only recommend tracks released in this year or earlier. |
None
|
max_tracks_per_artist
|
int
|
artist diversity cap applied during selection. |
3
|
min_score
|
float | None
|
drop recommendations with a total score below this value. |
None
|
vectors
|
SonicVectors
|
Plex sonic vectors to score against. |
required |
exclude_ids
|
set[int] | None
|
additional track ids to exclude before per-seed budgets; seed track ids are always excluded. |
None
|
Returns:
| Type | Description |
|---|---|
list[Recommendation]
|
Aggregated recommendations, best first, and the sonic coverage of the |
SonicCoverage
|
candidate pool (identical for every per-seed scan). |
recommend_tracks(session, *, seed_texts=None, seed_ids=None, limit=50, method='average', per_seed_limit=30, weights=None, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, vectors=None)
¶
Score every eligible non-seed track and return the exact constrained top-k.
Resolve seeds, filter years, stream scalar scoring facts, and retain the
best limit under the artist cap and score threshold. method="average"
(the default) aggregates the seeds into one mean profile and scores in a
single scan; method="frequency" scores each seed individually and ranks
by per-seed vote average with vote count as a tiebreaker. This is
equivalent to globally sorting by score then local ID, but loads ORM graphs
only for seeds and selected tracks. No source budget can omit an eligible
candidate. Average-populate uses this same pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
seed_texts
|
Sequence[str] | None
|
seed tracks as text queries (see |
None
|
seed_ids
|
Sequence[int] | None
|
seed tracks by local database id. |
None
|
limit
|
int
|
maximum number of recommendations to select. |
50
|
method
|
RecommendMethod
|
|
'average'
|
per_seed_limit
|
int
|
candidates gathered per seed ("frequency" method only). |
30
|
weights
|
Weights | None
|
signal weights; defaults to |
None
|
year_min
|
int | None
|
only recommend tracks released in this year or later. |
None
|
year_max
|
int | None
|
only recommend tracks released in this year or earlier. |
None
|
max_tracks_per_artist
|
int
|
artist diversity cap applied during selection. |
3
|
min_score
|
float | None
|
drop recommendations with a total score below this value. |
None
|
vectors
|
SonicVectors | None
|
Plex sonic vectors to score against; defaults to the default context's cached vectors. |
None
|
Returns:
| Type | Description |
|---|---|
list[Track]
|
|
list[Recommendation]
|
reports how many candidate tracks had a real Plex sonic vector. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Populate¶
musicseed.recommender.populate
¶
Recommendation strategies for populating an existing playlist.
PopulateMethod = Literal['average', 'frequency']
module-attribute
¶
Playlist populate strategies: "average" scores against the playlist's
mean profile; "frequency" aggregates per-track votes.
populate_average(session, playlist_track_ids, *, limit, weights=None, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, vectors=None)
¶
Recommend tracks against the mean sonic/metadata profile of a playlist.
Tracks already in the playlist are excluded automatically because they are
the seed set, and recommend_tracks never returns seed tracks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
playlist_track_ids
|
list[int]
|
local ids of the playlist's tracks, used as seeds. |
required |
limit
|
int
|
maximum number of recommendations to return. |
required |
weights
|
Weights | None
|
signal weights; defaults to |
None
|
year_min
|
int | None
|
only recommend tracks released in this year or later. |
None
|
year_max
|
int | None
|
only recommend tracks released in this year or earlier. |
None
|
max_tracks_per_artist
|
int
|
artist diversity cap applied during selection. |
3
|
min_score
|
float | None
|
drop recommendations with a total score below this value. |
None
|
vectors
|
SonicVectors | None
|
Plex sonic vectors to score against; defaults to the default context's cached vectors. |
None
|
Returns:
| Type | Description |
|---|---|
list[Recommendation]
|
Scored recommendations, best first. |
populate_frequency(session, playlist_track_ids, *, limit, per_seed_limit=30, weights=None, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, vectors=None)
¶
Recommend tracks voted for by multiple individual playlist tracks.
Each distinct playlist track is used as its own single-track seed to score all eligible candidates. The entire playlist is excluded before per-seed vote budgets, and one vector-cache snapshot is reused for the request. This costs one scalar scan per seed; prefer average mode for large playlists. A candidate's score is the average of its per-seed scores across every seed that recommended it (its "votes"); results are ranked by that average score, with vote count as a tiebreaker, so --limit cuts at the highest-scoring candidates. This avoids a literal set-intersection across seeds, which collapses to empty once a playlist has more than a handful of tracks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
playlist_track_ids
|
list[int]
|
local ids of the playlist's tracks, each used as an individual seed. |
required |
limit
|
int
|
maximum number of recommendations to return. |
required |
per_seed_limit
|
int
|
candidates gathered per playlist track. |
30
|
weights
|
Weights | None
|
signal weights; defaults to |
None
|
year_min
|
int | None
|
only recommend tracks released in this year or later. |
None
|
year_max
|
int | None
|
only recommend tracks released in this year or earlier. |
None
|
max_tracks_per_artist
|
int
|
artist diversity cap applied during selection. |
3
|
min_score
|
float | None
|
drop recommendations with a total score below this value. |
None
|
vectors
|
SonicVectors | None
|
Plex sonic vectors to score against; defaults to the default context's cached vectors. |
None
|
Returns:
| Type | Description |
|---|---|
list[Recommendation]
|
Aggregated recommendations, best first; each recommendation's |
list[Recommendation]
|
|
populate_playlist_recommendations(session, playlist_track_ids, *, method='average', limit=10, per_seed_limit=30, weights=None, year_min=None, year_max=None, max_tracks_per_artist=3, min_score=None, vectors=None)
¶
Dispatch to the requested populate strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
playlist_track_ids
|
list[int]
|
local ids of the playlist's tracks. |
required |
method
|
PopulateMethod
|
|
'average'
|
limit
|
int
|
maximum number of recommendations to return. |
10
|
per_seed_limit
|
int
|
candidates gathered per playlist track ("frequency" method only). |
30
|
weights
|
Weights | None
|
signal weights; defaults to |
None
|
year_min
|
int | None
|
only recommend tracks released in this year or later. |
None
|
year_max
|
int | None
|
only recommend tracks released in this year or earlier. |
None
|
max_tracks_per_artist
|
int
|
artist diversity cap applied during selection. |
3
|
min_score
|
float | None
|
drop recommendations with a total score below this value. |
None
|
vectors
|
SonicVectors | None
|
Plex sonic vectors to score against; defaults to the default context's cached vectors. |
None
|
Returns:
| Type | Description |
|---|---|
list[Recommendation]
|
Scored recommendations, best first. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Historical candidate pool¶
build_candidate_pool is retained for offline baseline comparisons. It is not used as the
production retrieval path or as a fallback.
musicseed.recommender.candidates
¶
Historical bounded-retrieval reference for offline evaluation and benchmarks.
Production uses retrieval.score_eligible_tracks. The original source
budgets (including their known omissions) stay here as an explicit comparator,
not a fallback or alternate user-facing retrieval mode.
CandidatePool
¶
Bases: BaseModel
Merged candidate IDs and the signal sources that produced each candidate.
track_ids
property
¶
All distinct candidate track ids in the pool.
add(track_id, source, seed_ids)
¶
Record that source produced track_id as a candidate.
Seed tracks are silently skipped — seeds are never their own candidates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track_id
|
int
|
local id of the candidate track. |
required |
source
|
str
|
name of the signal source (e.g. |
required |
seed_ids
|
set[int]
|
ids of the seed tracks to exclude. |
required |
add_many(track_ids, source, seed_ids)
¶
Record several candidate ids from one source (see add).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track_ids
|
list[int]
|
local ids of the candidate tracks. |
required |
source
|
str
|
name of the signal source that produced them. |
required |
seed_ids
|
set[int]
|
ids of the seed tracks to exclude. |
required |
sources_for(track_id)
¶
Return the sorted source names that produced a candidate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track_id
|
int
|
local id of the candidate track. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted source names, or an empty list for an unknown id. |
build_candidate_pool(session, seed, vectors, *, limit, year_min=None, year_max=None)
¶
Build the historical bounded pool for diagnostics, not production selection.
Each signal contributes its own bounded query (capped at
max(limit * 4, 50) ids) so the pool is a generous superset that the
scorer later trims — sources are only included when the seed profile has
data for them:
sonic— nearest neighbors of the seed embedding in Plex's vectors (ranked in memory, restricted to the year window before ranking),genre/style— tracks sharing any seed genre/style,era— tracks closest to the seed year,popularity— tracks closest to the seed popularity,novelty— least-played tracks first (always included).
The year window filters every source before its limit is applied, so a
narrow window searches within the window rather than truncating after
the fact. Seed tracks are excluded by CandidatePool.add.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
open database session. |
required |
seed
|
SeedProfile
|
the aggregated seed profile. |
required |
vectors
|
SonicVectors
|
the query-time Plex sonic vector store. |
required |
limit
|
int
|
requested number of final recommendations; per-source queries are bounded to a multiple of this. |
required |
year_min
|
int | None
|
only consider tracks released in this year or later. |
None
|
year_max
|
int | None
|
only consider tracks released in this year or earlier. |
None
|
Returns:
| Type | Description |
|---|---|
CandidatePool
|
The merged candidate pool, recording which sources produced each |
CandidatePool
|
candidate. |