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:
@@ -82,7 +82,7 @@ func (q *Queries) ListTuningAudit(ctx context.Context, limit int32) ([]Recommend
|
||||
}
|
||||
|
||||
const listWeightProfiles = `-- name: ListWeightProfiles :many
|
||||
SELECT profile, base_weight, like_boost, recency_weight, skip_penalty, jitter_magnitude, context_weight, similarity_weight, taste_weight, updated_at FROM recommendation_weight_profiles ORDER BY profile
|
||||
SELECT profile, base_weight, like_boost, recency_weight, skip_penalty, jitter_magnitude, context_weight, similarity_weight, taste_weight, updated_at, context_time_weight FROM recommendation_weight_profiles ORDER BY profile
|
||||
`
|
||||
|
||||
func (q *Queries) ListWeightProfiles(ctx context.Context) ([]RecommendationWeightProfile, error) {
|
||||
@@ -105,6 +105,7 @@ func (q *Queries) ListWeightProfiles(ctx context.Context) ([]RecommendationWeigh
|
||||
&i.SimilarityWeight,
|
||||
&i.TasteWeight,
|
||||
&i.UpdatedAt,
|
||||
&i.ContextTimeWeight,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,29 +164,31 @@ func (q *Queries) UpdateTasteTuning(ctx context.Context, arg UpdateTasteTuningPa
|
||||
|
||||
const updateWeightProfile = `-- name: UpdateWeightProfile :one
|
||||
UPDATE recommendation_weight_profiles
|
||||
SET base_weight = $2,
|
||||
like_boost = $3,
|
||||
recency_weight = $4,
|
||||
skip_penalty = $5,
|
||||
jitter_magnitude = $6,
|
||||
context_weight = $7,
|
||||
similarity_weight = $8,
|
||||
taste_weight = $9,
|
||||
updated_at = now()
|
||||
SET base_weight = $2,
|
||||
like_boost = $3,
|
||||
recency_weight = $4,
|
||||
skip_penalty = $5,
|
||||
jitter_magnitude = $6,
|
||||
context_weight = $7,
|
||||
similarity_weight = $8,
|
||||
taste_weight = $9,
|
||||
context_time_weight = $10,
|
||||
updated_at = now()
|
||||
WHERE profile = $1
|
||||
RETURNING profile, base_weight, like_boost, recency_weight, skip_penalty, jitter_magnitude, context_weight, similarity_weight, taste_weight, updated_at
|
||||
RETURNING profile, base_weight, like_boost, recency_weight, skip_penalty, jitter_magnitude, context_weight, similarity_weight, taste_weight, updated_at, context_time_weight
|
||||
`
|
||||
|
||||
type UpdateWeightProfileParams struct {
|
||||
Profile string
|
||||
BaseWeight float64
|
||||
LikeBoost float64
|
||||
RecencyWeight float64
|
||||
SkipPenalty float64
|
||||
JitterMagnitude float64
|
||||
ContextWeight float64
|
||||
SimilarityWeight float64
|
||||
TasteWeight float64
|
||||
Profile string
|
||||
BaseWeight float64
|
||||
LikeBoost float64
|
||||
RecencyWeight float64
|
||||
SkipPenalty float64
|
||||
JitterMagnitude float64
|
||||
ContextWeight float64
|
||||
SimilarityWeight float64
|
||||
TasteWeight float64
|
||||
ContextTimeWeight float64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateWeightProfile(ctx context.Context, arg UpdateWeightProfileParams) (RecommendationWeightProfile, error) {
|
||||
@@ -199,6 +202,7 @@ func (q *Queries) UpdateWeightProfile(ctx context.Context, arg UpdateWeightProfi
|
||||
arg.ContextWeight,
|
||||
arg.SimilarityWeight,
|
||||
arg.TasteWeight,
|
||||
arg.ContextTimeWeight,
|
||||
)
|
||||
var i RecommendationWeightProfile
|
||||
err := row.Scan(
|
||||
@@ -212,6 +216,7 @@ func (q *Queries) UpdateWeightProfile(ctx context.Context, arg UpdateWeightProfi
|
||||
&i.SimilarityWeight,
|
||||
&i.TasteWeight,
|
||||
&i.UpdatedAt,
|
||||
&i.ContextTimeWeight,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -249,21 +254,23 @@ const upsertWeightProfileDefaults = `-- name: UpsertWeightProfileDefaults :exec
|
||||
|
||||
INSERT INTO recommendation_weight_profiles (
|
||||
profile, base_weight, like_boost, recency_weight, skip_penalty,
|
||||
jitter_magnitude, context_weight, similarity_weight, taste_weight
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
jitter_magnitude, context_weight, similarity_weight, taste_weight,
|
||||
context_time_weight
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
ON CONFLICT (profile) DO NOTHING
|
||||
`
|
||||
|
||||
type UpsertWeightProfileDefaultsParams struct {
|
||||
Profile string
|
||||
BaseWeight float64
|
||||
LikeBoost float64
|
||||
RecencyWeight float64
|
||||
SkipPenalty float64
|
||||
JitterMagnitude float64
|
||||
ContextWeight float64
|
||||
SimilarityWeight float64
|
||||
TasteWeight float64
|
||||
Profile string
|
||||
BaseWeight float64
|
||||
LikeBoost float64
|
||||
RecencyWeight float64
|
||||
SkipPenalty float64
|
||||
JitterMagnitude float64
|
||||
ContextWeight float64
|
||||
SimilarityWeight float64
|
||||
TasteWeight float64
|
||||
ContextTimeWeight float64
|
||||
}
|
||||
|
||||
// Recommendation tuning lab queries (#1250). Seeding happens via the
|
||||
@@ -281,6 +288,7 @@ func (q *Queries) UpsertWeightProfileDefaults(ctx context.Context, arg UpsertWei
|
||||
arg.ContextWeight,
|
||||
arg.SimilarityWeight,
|
||||
arg.TasteWeight,
|
||||
arg.ContextTimeWeight,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user