feat(taste): time-of-day / weekday context conditioning — #1531
Milestone #160 Opt 3 (temporal half). A new additive scoring term that boosts a candidate when its artist's play history concentrates in the CURRENT daypart × weekday-type cell, in the user's local timezone. - Migration 0046: recommendation_weight_profiles.context_time_weight (per-profile scoring weight, DEFAULT 1.0). - Query ListArtistContextPlayCountsForUser: per-artist completed-play counts split by the current cell (daypart night[22,5)/morning[5,12)/ afternoon[12,17)/evening[17,22) × weekday-vs-weekend) via started_at AT TIME ZONE users.timezone; 365-day window, skips excluded. - internal/recommendation/context.go: LoadContextAffinity computes each artist's shrunk cell-share minus the user's baseline share, clamped to [-1,1]; sparse artists shrink toward baseline (pseudo-count 5), unknown artists → 0 (cold-start neutral). - Score() gains context_affinity_score · ContextTimeWeight; both candidate loaders set it per candidate. - Tuning lab: ContextTimeWeight threaded through recsettings + admin API + web card ("Time-of-day weight" row) + Go/web tests. Shipped 1.0 both profiles (uniform start, re-bakeable). Device-class axis deferred to #1551 (needs a client_id → device-class mapping that doesn't exist yet). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ export type WeightProfile = {
|
||||
context_weight: number;
|
||||
similarity_weight: number;
|
||||
taste_weight: number;
|
||||
context_time_weight: number;
|
||||
};
|
||||
|
||||
export type TasteTuning = {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
{ key: 'jitter_magnitude', label: 'Jitter', hint: 'Random reshuffle magnitude for near-ties.' },
|
||||
{ key: 'context_weight', label: 'Context weight', hint: 'Session-vector similarity contribution.' },
|
||||
{ key: 'similarity_weight', label: 'Similarity weight', hint: 'Seed-similarity contribution.' },
|
||||
{ key: 'taste_weight', label: 'Taste weight', hint: 'Learned taste-profile fit, in [-1, +1].' }
|
||||
{ key: 'taste_weight', label: 'Taste weight', hint: 'Learned taste-profile fit, in [-1, +1].' },
|
||||
{ key: 'context_time_weight', label: 'Time-of-day weight', hint: "Artist's time-of-day/weekday affinity for the current context, in [-1, +1]. 0 = ignore when you listen." }
|
||||
];
|
||||
|
||||
const tasteFields: { key: keyof TasteTuning; label: string; hint: string }[] = [
|
||||
|
||||
@@ -28,6 +28,7 @@ const weights = (over: Partial<Record<string, number>> = {}) => ({
|
||||
context_weight: 0.5,
|
||||
similarity_weight: 1.5,
|
||||
taste_weight: 1.5,
|
||||
context_time_weight: 1,
|
||||
...over
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user