Skip to content

fix(autoplay): hard genre-family penalty prevents rap → electronic drift - #639

Merged
LucasSantana-Dev merged 7 commits into
mainfrom
fix/autoplay-genre-family-penalty
Apr 15, 2026
Merged

LucasSantana-Dev merged 7 commits into
mainfrom
fix/autoplay-genre-family-penalty

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes autoplay genre drift (2Pac hip-hop → electronic music) by adding weighted genre-family penalty that dominates audio feature scoring.

Changes

  • getArtistGenres() in spotifyApi.ts fetches artist genres from Spotify with 24h LRU cache
  • 10 genre families defined (rap_hiphop, electronic, latin, rock_metal, rnb_soul, pop, country_folk, jazz_classical, world, ambient_chill)
  • calculateGenreFamilyPenalty() applies:
    • -0.6 when rap/rock/latin (strong anchors) cross to disjoint family
    • -0.3 when weak anchor crosses to disjoint family
    • 0 when families overlap
    • -0.1 fallback for unknown genres
  • enrichWithAudioFeatures() now fetches genres async and applies penalty after audio feature scoring

Testing

Manual verification: 2Pac (hip-hop) candidate will now see -0.6 penalty when electronic candidate arrives, preventing selection despite energy match.

References

  • User report: 'playing 2Pac (hip-hop/rap) → autoplay picked weird electronic music'
  • Root cause: energy-match scoring (+0.15) > genre mismatch penalty (-0.1), causing genre drift
  • Solution: genre family penalty (-0.6) dominates energy match for strong genre anchors

Summary by CodeRabbit

Release Notes

New Features

  • Recommendations now detect language patterns in your listening history and intelligently adjust suggestions based on detected language preferences
  • Genre-family alignment system ensures music recommendations maintain consistency within similar musical styles
  • Artist genre information integrated into the recommendation engine to enhance accuracy and quality of personalized suggestions
  • Improved recommendation blending algorithm with enhanced filtering capabilities

- Add getArtistGenres to spotifyApi.ts with Spotify artist search + caching
- Define 10 genre families (rap_hiphop, electronic, latin, rock_metal, etc)
- Implement calculateGenreFamilyPenalty: -0.6 for strong genre anchors (rap, rock, latin) crossing to different family, -0.3 for weak anchors, 0 for overlaps
- Integrate genre penalty into enrichWithAudioFeatures post-scoring
- Fetch candidate genres and apply penalty dynamically during audio feature enrichment
- Fixes 2Pac → electronic drift (user report)

Genre families:
- rap_hiphop: hip hop, rap, trap, drill
- electronic: edm, house, techno, trance, dubstep
- latin: reggaeton, forró, samba, bossa nova, latin trap
- rock_metal: rock, metal, punk, grunge, alternative
+ 6 more (pop, rnb_soul, country_folk, jazz_classical, world, ambient_chill)

Scoring:
- Same family overlap → 0 penalty (no drift)
- Disjoint families with strong genre anchor → -0.6 penalty (dominates energy match)
- Disjoint families with weak anchor → -0.3 penalty
- Unknown genres → -0.1 safety penalty (fallback to audio features)
@vercel

vercel Bot commented Apr 15, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 15, 2026 5:48pm

Request Review

@coderabbitai

coderabbitai Bot commented Apr 15, 2026 •

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

The PR adds language-aware music recommendation filtering, artist genre retrieval with caching, and genre-family-based track enrichment scoring. It introduces Spanish language detection heuristics, applies penalties to Spanish tracks in non-Spanish sessions, caches artist genre data from Spotify, and enhances queue candidate scoring by analyzing genre family alignment between current and candidate artists.

Changes

Cohort / File(s) Summary
Language Detection Utilities
packages/bot/src/utils/music/languageHeuristics.ts, packages/bot/src/utils/music/languageHeuristics.spec.ts
New utility exports detectSpanishMarkers() and detectSessionLanguageMarkers() using regex, stopword matching, and genre tag analysis to identify Spanish vs English track characteristics. Comprehensive test coverage for text markers, genre markers, and session-level language detection.
Recommendation Engine
packages/bot/src/services/musicRecommendation/recommendationEngine.ts, packages/bot/src/services/musicRecommendation/recommendationEngine.spec.ts
Integrated language heuristics into history-based recommendations. Exported new applySpanishLanguagePenalty() function applying score penalties and reasons to Spanish tracks in non-Spanish sessions. Updated blendRecommendations() to accept optional hasSpanishMarkers parameter and apply penalties to each seed's recommendations. Added extensive test suites validating penalty application, empty/mixed arrays, and sort order preservation.
Spotify API
packages/bot/src/spotify/spotifyApi.ts, packages/bot/src/spotify/spotifyApi.spec.ts
Implemented new exported getArtistGenres() function with LRU cache (5000 entries, 24h TTL). Fetches artist genres from Spotify search endpoint, caches both successful and failed results, returns empty array on errors. Added test coverage for network failures, parse errors, missing/invalid genre data, and edge cases.
Queue Manipulation & Enrichment
packages/bot/src/utils/music/queueManipulation.ts, packages/bot/src/utils/music/queueManipulation.spec.ts
Exported enrichWithAudioFeatures() with new currentArtistName parameter. Introduced GENRE_FAMILIES constant and helper functions: getGenreFamilies() maps genres to family identifiers, calculateGenreFamilyPenalty() scores genre-family alignment/drift between current and candidate artists. Integrated artist genre fetching into enrichment pipeline with appended "genre family drift" reason on penalties. Added test coverage for genre mapping, penalty calculation, and edge cases.

Sequence Diagrams

sequenceDiagram
    participant Session as Session<br/>(Tracks)
    participant LangDetect as Language<br/>Detection
    participant RecommendEngine as Recommendation<br/>Engine
    participant Penalty as Spanish<br/>Penalty Filter
    participant Blend as Blend<br/>Recommendations

    Session->>LangDetect: recentHistory tracks
    LangDetect->>LangDetect: detect Spanish markers<br/>(accents, stopwords, genres)
    LangDetect->>RecommendEngine: hasSpanish boolean
    RecommendEngine->>Penalty: primaryRecommendations +<br/>hasSpanish flag
    Penalty->>Penalty: apply score penalties<br/>to Spanish tracks<br/>(non-Spanish session)
    Penalty->>RecommendEngine: filtered recommendations
    RecommendEngine->>Blend: filtered primary +<br/>additionalSeeds +<br/>hasSpanish flag
    Blend->>Blend: for each seed:<br/>apply penalty filter
    Blend->>Blend: merge & sort<br/>by score (descending)
    Blend->>RecommendEngine: final blended results
Loading
sequenceDiagram
    participant Candidate as Candidate<br/>Track
    participant Enricher as Queue<br/>Enrichment
    participant SpotifyAPI as Spotify<br/>API
    participant GenreCache as Genre<br/>Cache
    participant Scorer as Genre<br/>Family Scorer

    Candidate->>Enricher: candidate artist
    Enricher->>SpotifyAPI: fetch current artist<br/>genres (once per session)
    SpotifyAPI->>GenreCache: check cache
    GenreCache->>SpotifyAPI: return cached or<br/>search Spotify
    SpotifyAPI->>Enricher: current genres
    Enricher->>SpotifyAPI: fetch candidate artist<br/>genres (per candidate)
    SpotifyAPI->>GenreCache: check cache
    GenreCache->>Enricher: candidate genres
    Enricher->>Scorer: compare genre families
    Scorer->>Scorer: map genres to families
    Scorer->>Scorer: calculate alignment<br/>penalty
    Scorer->>Enricher: penalty score
    Enricher->>Enricher: adjust candidate score +<br/>append drift reason
    Enricher->>Candidate: enriched with score
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement, size/xl

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding a genre-family penalty mechanism to prevent autoplay drift from rap to electronic music. The title is specific, concise, and directly reflects the primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/autoplay-genre-family-penalty

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

LucasSantana-Dev and others added 2 commits April 15, 2026 14:19
Mock getArtistGenres to return matching genres for current and candidate
tracks, preventing unknown-genre penalty from interfering with the
energy/valence boost assertion.
@github-actions

github-actions Bot commented Apr 15, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 330 kB

ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/ActionPanel-DxbYjZOV.js 400 B
packages/frontend/dist/assets/api-Blto5RPu.js 3.04 kB
packages/frontend/dist/assets/authStore-C1dwYeJT.js 556 B
packages/frontend/dist/assets/AutoMessages-CxAC26iZ.js 2.69 kB
packages/frontend/dist/assets/AutoMod-IPk4FC7g.js 4.09 kB
packages/frontend/dist/assets/avatar-B8wmY8_z.js 1.79 kB
packages/frontend/dist/assets/badge-Di6G1o1C.js 498 B
packages/frontend/dist/assets/Button-DNJBdlDI.js 761 B
packages/frontend/dist/assets/Card-BZdEz3Em.js 456 B
packages/frontend/dist/assets/chunk-QFMPRPBF-B0_TipZB.js 14.8 kB
packages/frontend/dist/assets/CommandsConfig-BHEBU2Zz.js 1.48 kB
packages/frontend/dist/assets/Config-CAxgqG6L.js 1.74 kB
packages/frontend/dist/assets/CustomCommands-B7S3fWE5.js 2.16 kB
packages/frontend/dist/assets/DashboardOverview-CgUjrOjS.js 3.43 kB
packages/frontend/dist/assets/dialog-nNJN7xxV.js 947 B
packages/frontend/dist/assets/dist-BYvsGl5s.js 414 B
packages/frontend/dist/assets/EmbedBuilder-BxVz8INr.js 3.35 kB
packages/frontend/dist/assets/EmptyState-SHSbVkpR.js 430 B
packages/frontend/dist/assets/Features-CrbsOLD9.js 2.85 kB
packages/frontend/dist/assets/GuildAutomation-brd_jKPZ.js 2.94 kB
packages/frontend/dist/assets/guildStore-wioLYb73.js 792 B
packages/frontend/dist/assets/index-CWgMrgG2.css 14.8 kB
packages/frontend/dist/assets/index-DLoOYAmP.js 11.9 kB
packages/frontend/dist/assets/input-ChG24niz.js 463 B
packages/frontend/dist/assets/label-q_xFnvVW.js 485 B
packages/frontend/dist/assets/LastFm-B07mRY72.js 1.74 kB
packages/frontend/dist/assets/Levels-KZV2nvlm.js 2.65 kB
packages/frontend/dist/assets/Login-Bh2-6TFH.js 2.54 kB
packages/frontend/dist/assets/Lyrics-Gtw6yX4k.js 1.33 kB
packages/frontend/dist/assets/Moderation-CyaN7lXF.js 3.86 kB
packages/frontend/dist/assets/Music-D1PNEUqr.js 6.19 kB
packages/frontend/dist/assets/MusicConfig-BCkOOtK9.js 1.62 kB
packages/frontend/dist/assets/PreferredArtists-BPKKUd81.js 3.51 kB
packages/frontend/dist/assets/PrivacyPolicy-D6bsnWOd.js 1.38 kB
packages/frontend/dist/assets/rbac-BN1DzQHh.js 152 B
packages/frontend/dist/assets/ReactionRoles-DBwT2uY_.js 1.9 kB
packages/frontend/dist/assets/rolldown-runtime-COnpUsM8.js 467 B
packages/frontend/dist/assets/scroll-area-C108baTt.js 519 B
packages/frontend/dist/assets/SectionHeader-CQJKLDod.js 383 B
packages/frontend/dist/assets/select-DphAEnHz.js 1.22 kB
packages/frontend/dist/assets/ServerLogs-C8ftKvHi.js 2.9 kB
packages/frontend/dist/assets/ServerSettings-BCZJ99-s.js 4.21 kB
packages/frontend/dist/assets/ServersPage-CFWg2_5N.js 2.95 kB
packages/frontend/dist/assets/Skeleton-C5XSiOK9.js 235 B
packages/frontend/dist/assets/Spotify-1jyiUDSb.js 1.75 kB
packages/frontend/dist/assets/Starboard-DpV4ppJ5.js 2.09 kB
packages/frontend/dist/assets/StatTile-BL59Oosl.js 603 B
packages/frontend/dist/assets/switch-C3IautHz.js 541 B
packages/frontend/dist/assets/TermsOfService-B5dZHvi1.js 1.37 kB
packages/frontend/dist/assets/TrackHistory-BGNzV6ha.js 2.18 kB
packages/frontend/dist/assets/TwitchNotifications-CTpayncX.js 2.44 kB
packages/frontend/dist/assets/useGuildSelection-CL4dEo7i.js 487 B
packages/frontend/dist/assets/usePageMetadata-B513X5D1.js 328 B
packages/frontend/dist/assets/utils-DhIi5ktf.js 147 B
packages/frontend/dist/assets/vendor-forms-B7m4p2yU.js 25.4 kB
packages/frontend/dist/assets/vendor-radix-CqxcePto.js 33.5 kB
packages/frontend/dist/assets/vendor-react-DRFstzzV.js 55.6 kB
packages/frontend/dist/assets/vendor-state-C5KtdxzA.js 22.2 kB
packages/frontend/dist/assets/vendor-ui-DqvmPbZd.js 64.4 kB

compressed-size-action

LucasSantana-Dev and others added 2 commits April 15, 2026 14:29
…ta.popularity

- Lines 108, 153, 170: Cast rec.track.metadata to TrackMetadata interface
- Fixes TS2339 errors: Property 'tags'/'popularity' does not exist on type '{}'
…penalty

- Export & test getGenreFamilies, calculateGenreFamilyPenalty, enrichWithAudioFeatures
- Add 21 tests covering all 10 genre families, penalty edge cases, empty states
- Export & test applySpanishLanguagePenalty & blendRecommendations
- Add 12 tests for Spanish detection, mixed batches, score sorting, filtering
- Add 7 tests for getArtistGenres API error handling & cache behavior
- Total 40+ new tests across 3 spec files
- All tests green: 2582 passed
@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit db24ec8 into main Apr 15, 2026
11 of 12 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the fix/autoplay-genre-family-penalty branch April 15, 2026 17:54
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
…ift (#639)

* fix(autoplay): hard genre-family penalty prevents rap → electronic drift

- Add getArtistGenres to spotifyApi.ts with Spotify artist search + caching
- Define 10 genre families (rap_hiphop, electronic, latin, rock_metal, etc)
- Implement calculateGenreFamilyPenalty: -0.6 for strong genre anchors (rap, rock, latin) crossing to different family, -0.3 for weak anchors, 0 for overlaps
- Integrate genre penalty into enrichWithAudioFeatures post-scoring
- Fetch candidate genres and apply penalty dynamically during audio feature enrichment
- Fixes 2Pac → electronic drift (user report)

Genre families:
- rap_hiphop: hip hop, rap, trap, drill
- electronic: edm, house, techno, trance, dubstep
- latin: reggaeton, forró, samba, bossa nova, latin trap
- rock_metal: rock, metal, punk, grunge, alternative
+ 6 more (pop, rnb_soul, country_folk, jazz_classical, world, ambient_chill)

Scoring:
- Same family overlap → 0 penalty (no drift)
- Disjoint families with strong genre anchor → -0.6 penalty (dominates energy match)
- Disjoint families with weak anchor → -0.3 penalty
- Unknown genres → -0.1 safety penalty (fallback to audio features)

* chore: bump version to 2.6.125 (artists grid UX tweaks + backend SPOTIFY env) (#638)

* fix: mock overlapping genres in energy/valence boost test

Mock getArtistGenres to return matching genres for current and candidate
tracks, preventing unknown-genre penalty from interfering with the
energy/valence boost assertion.

* fix(autoplay): hard-reject Spanish tracks when session has no Spanish markers

* fix: add TrackMetadata type for ts errors on metadata.tags and metadata.popularity

- Lines 108, 153, 170: Cast rec.track.metadata to TrackMetadata interface
- Fixes TS2339 errors: Property 'tags'/'popularity' does not exist on type '{}'

* test: add comprehensive coverage for genre family & Spanish language penalty

- Export & test getGenreFamilies, calculateGenreFamilyPenalty, enrichWithAudioFeatures
- Add 21 tests covering all 10 genre families, penalty edge cases, empty states
- Export & test applySpanishLanguagePenalty & blendRecommendations
- Add 12 tests for Spanish detection, mixed batches, score sorting, filtering
- Add 7 tests for getArtistGenres API error handling & cache behavior
- Total 40+ new tests across 3 spec files
- All tests green: 2582 passed

This branch was successfully deployed

1 active deployment
Preview — d696de73 Deployed Apr 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot enhancement New feature or request size/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant