Files
minstrel/internal/db/dbq/recommendation_tuning.sql.go
T
bvandeusen 40384cc05e
test-go / test (push) Successful in 34s
test-web / test (push) Successful in 41s
test-go / integration (push) Successful in 4m40s
feat(taste): era/decade taste facet — #1530
Milestone #160 Opt 2 (era half). A third taste facet alongside artists
+ genre tags: signed weights over decade buckets ("1990s") derived from
albums.release_date, rebuilt daily and scored into the taste match.

- Migration 0045: taste_profile_eras table (mirrors taste_profile_tags)
  + taste_tuning.era_scale column (DEFAULT 0.5).
- Build side (internal/taste): Config.EraScale ([0,1] damper, mirrors
  EnrichedTagScale), accumulate folds each play/like's decade at
  base*EraScale, persist atomic-replaces the era rows.
- Scorer (internal/recommendation): TasteProfile gains an era term (own
  tanh scale + additive 0.15 share so it never weakens the existing
  artist/tag signal when a track is undated); candidate queries return
  album release_date; decadeOf mirrors the builder helper.
- Tuning lab: era_scale threaded through recsettings + admin API + web
  card (auto-renders the new row) + Go/web tests.

Mood facet deferred to #1534 (partial enrichment coverage + needs
candidate-side enriched-tag loading).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 09:01:00 -04:00

287 lines
7.5 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: recommendation_tuning.sql
package dbq
import (
"context"
)
const getTasteTuning = `-- name: GetTasteTuning :one
SELECT singleton, half_life_days, engagement_hard_skip, engagement_neutral, engagement_full, updated_at, enriched_tag_scale, era_scale FROM taste_tuning WHERE singleton = true
`
func (q *Queries) GetTasteTuning(ctx context.Context) (TasteTuning, error) {
row := q.db.QueryRow(ctx, getTasteTuning)
var i TasteTuning
err := row.Scan(
&i.Singleton,
&i.HalfLifeDays,
&i.EngagementHardSkip,
&i.EngagementNeutral,
&i.EngagementFull,
&i.UpdatedAt,
&i.EnrichedTagScale,
&i.EraScale,
)
return i, err
}
const insertTuningAudit = `-- name: InsertTuningAudit :exec
INSERT INTO recommendation_tuning_audit (scope, action, changes)
VALUES ($1, $2, $3)
`
type InsertTuningAuditParams struct {
Scope string
Action string
Changes []byte
}
// changes is a jsonb array of {field, old, new} objects.
func (q *Queries) InsertTuningAudit(ctx context.Context, arg InsertTuningAuditParams) error {
_, err := q.db.Exec(ctx, insertTuningAudit, arg.Scope, arg.Action, arg.Changes)
return err
}
const listTuningAudit = `-- name: ListTuningAudit :many
SELECT id, changed_at, scope, action, changes
FROM recommendation_tuning_audit
ORDER BY changed_at DESC, id DESC
LIMIT $1
`
// Newest first; consumed by the metrics trend view (#1251) to annotate
// knob turns on the timeline.
func (q *Queries) ListTuningAudit(ctx context.Context, limit int32) ([]RecommendationTuningAudit, error) {
rows, err := q.db.Query(ctx, listTuningAudit, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []RecommendationTuningAudit
for rows.Next() {
var i RecommendationTuningAudit
if err := rows.Scan(
&i.ID,
&i.ChangedAt,
&i.Scope,
&i.Action,
&i.Changes,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
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
`
func (q *Queries) ListWeightProfiles(ctx context.Context) ([]RecommendationWeightProfile, error) {
rows, err := q.db.Query(ctx, listWeightProfiles)
if err != nil {
return nil, err
}
defer rows.Close()
var items []RecommendationWeightProfile
for rows.Next() {
var i RecommendationWeightProfile
if err := rows.Scan(
&i.Profile,
&i.BaseWeight,
&i.LikeBoost,
&i.RecencyWeight,
&i.SkipPenalty,
&i.JitterMagnitude,
&i.ContextWeight,
&i.SimilarityWeight,
&i.TasteWeight,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateTasteTuning = `-- name: UpdateTasteTuning :one
UPDATE taste_tuning
SET half_life_days = $1,
engagement_hard_skip = $2,
engagement_neutral = $3,
engagement_full = $4,
enriched_tag_scale = $5,
era_scale = $6,
updated_at = now()
WHERE singleton = true
RETURNING singleton, half_life_days, engagement_hard_skip, engagement_neutral, engagement_full, updated_at, enriched_tag_scale, era_scale
`
type UpdateTasteTuningParams struct {
HalfLifeDays float64
EngagementHardSkip float64
EngagementNeutral float64
EngagementFull float64
EnrichedTagScale float64
EraScale float64
}
func (q *Queries) UpdateTasteTuning(ctx context.Context, arg UpdateTasteTuningParams) (TasteTuning, error) {
row := q.db.QueryRow(ctx, updateTasteTuning,
arg.HalfLifeDays,
arg.EngagementHardSkip,
arg.EngagementNeutral,
arg.EngagementFull,
arg.EnrichedTagScale,
arg.EraScale,
)
var i TasteTuning
err := row.Scan(
&i.Singleton,
&i.HalfLifeDays,
&i.EngagementHardSkip,
&i.EngagementNeutral,
&i.EngagementFull,
&i.UpdatedAt,
&i.EnrichedTagScale,
&i.EraScale,
)
return i, err
}
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()
WHERE profile = $1
RETURNING profile, base_weight, like_boost, recency_weight, skip_penalty, jitter_magnitude, context_weight, similarity_weight, taste_weight, updated_at
`
type UpdateWeightProfileParams struct {
Profile string
BaseWeight float64
LikeBoost float64
RecencyWeight float64
SkipPenalty float64
JitterMagnitude float64
ContextWeight float64
SimilarityWeight float64
TasteWeight float64
}
func (q *Queries) UpdateWeightProfile(ctx context.Context, arg UpdateWeightProfileParams) (RecommendationWeightProfile, error) {
row := q.db.QueryRow(ctx, updateWeightProfile,
arg.Profile,
arg.BaseWeight,
arg.LikeBoost,
arg.RecencyWeight,
arg.SkipPenalty,
arg.JitterMagnitude,
arg.ContextWeight,
arg.SimilarityWeight,
arg.TasteWeight,
)
var i RecommendationWeightProfile
err := row.Scan(
&i.Profile,
&i.BaseWeight,
&i.LikeBoost,
&i.RecencyWeight,
&i.SkipPenalty,
&i.JitterMagnitude,
&i.ContextWeight,
&i.SimilarityWeight,
&i.TasteWeight,
&i.UpdatedAt,
)
return i, err
}
const upsertTasteTuningDefaults = `-- name: UpsertTasteTuningDefaults :exec
INSERT INTO taste_tuning (
singleton, half_life_days, engagement_hard_skip,
engagement_neutral, engagement_full, enriched_tag_scale, era_scale
) VALUES (true, $1, $2, $3, $4, $5, $6)
ON CONFLICT (singleton) DO NOTHING
`
type UpsertTasteTuningDefaultsParams struct {
HalfLifeDays float64
EngagementHardSkip float64
EngagementNeutral float64
EngagementFull float64
EnrichedTagScale float64
EraScale float64
}
func (q *Queries) UpsertTasteTuningDefaults(ctx context.Context, arg UpsertTasteTuningDefaultsParams) error {
_, err := q.db.Exec(ctx, upsertTasteTuningDefaults,
arg.HalfLifeDays,
arg.EngagementHardSkip,
arg.EngagementNeutral,
arg.EngagementFull,
arg.EnrichedTagScale,
arg.EraScale,
)
return err
}
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)
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
}
// Recommendation tuning lab queries (#1250). Seeding happens via the
// recsettings boot reconcile; shipped defaults live in Go only.
// Boot reconcile: insert the shipped defaults for a profile if the row
// doesn't exist yet. Never overwrites operator-tuned values.
func (q *Queries) UpsertWeightProfileDefaults(ctx context.Context, arg UpsertWeightProfileDefaultsParams) error {
_, err := q.db.Exec(ctx, upsertWeightProfileDefaults,
arg.Profile,
arg.BaseWeight,
arg.LikeBoost,
arg.RecencyWeight,
arg.SkipPenalty,
arg.JitterMagnitude,
arg.ContextWeight,
arg.SimilarityWeight,
arg.TasteWeight,
)
return err
}