Add named taste profiles for per-person recommendations

Named profiles allow each household member to get personalized
recommendations without polluting each other's taste. Includes
profile CRUD API, speaker→profile auto-attribution, recent listen
history endpoint, and profile param on all existing endpoints.
All endpoints backward compatible (no profile param = "default").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 19:14:34 -06:00
parent 1b739fbd20
commit 094621a9a8
14 changed files with 556 additions and 33 deletions

View File

@@ -49,10 +49,17 @@ docker exec haunt-fm alembic upgrade head
| GET | `/api/status` | Full pipeline status JSON |
| GET | `/` | HTML status dashboard |
| POST | `/api/history/webhook` | Log a listen event (from HA automation) |
| GET | `/api/history/recent?limit=20&profile=name` | Recent listen events (optional profile filter) |
| POST | `/api/admin/discover` | Expand listening history via Last.fm |
| POST | `/api/admin/build-taste-profile` | Rebuild taste profile from embeddings |
| GET | `/api/recommendations?limit=50&vibe=chill+ambient` | Get ranked recommendations (optional vibe) |
| POST | `/api/playlists/generate` | Generate and optionally play a playlist |
| GET | `/api/profiles` | List all named profiles with stats |
| POST | `/api/profiles` | Create a named profile |
| GET | `/api/profiles/{name}` | Get profile details + stats |
| DELETE | `/api/profiles/{name}` | Delete profile (reassigns events to default) |
| PUT | `/api/profiles/{name}/speakers` | Set speaker→profile mappings |
| GET | `/api/profiles/{name}/speakers` | List speaker mappings |
## Usage
@@ -89,6 +96,7 @@ curl -X POST http://192.168.86.51:8321/api/playlists/generate \
- `auto_play``true` to immediately play on the speaker
- `vibe` — text description of the desired mood/vibe (e.g. "chill lo-fi beats", "upbeat party music"). Uses CLAP text embeddings to match tracks in the same vector space as audio.
- `alpha` — blend factor between taste profile and vibe (default 0.5). `1.0` = pure taste profile, `0.0` = pure vibe match, `0.5` = equal blend. Ignored when no vibe is provided.
- `profile` — named taste profile to use (default: "default"). Each profile has its own listening history and taste embedding.
### Speaker entities
@@ -110,6 +118,40 @@ The `speaker_entity` **must** be a Music Assistant entity (the `_2` suffix ones)
| downstairs | `media_player.downstairs_2` |
| upstairs | `media_player.upstairs_2` |
### Named profiles
Named profiles let each household member get personalized recommendations without polluting each other's taste.
```bash
# Create a profile
curl -X POST http://192.168.86.51:8321/api/profiles \
-H "Content-Type: application/json" \
-d '{"name":"antialias","display_name":"Me"}'
# Map speakers to auto-attribute listens
curl -X PUT http://192.168.86.51:8321/api/profiles/antialias/speakers \
-H "Content-Type: application/json" \
-d '{"speakers":["Study speaker","Master bathroom speaker"]}'
# Log a listen event with explicit profile
curl -X POST http://192.168.86.51:8321/api/history/webhook \
-H "Content-Type: application/json" \
-d '{"title":"Song","artist":"Artist","profile":"antialias"}'
# Get recommendations for a profile
curl "http://192.168.86.51:8321/api/recommendations?limit=20&profile=antialias"
# Generate playlist for a profile
curl -X POST http://192.168.86.51:8321/api/playlists/generate \
-H "Content-Type: application/json" \
-d '{"total_tracks":20,"profile":"antialias","speaker_entity":"media_player.study_speaker_2","auto_play":true}'
# Build taste profile manually
curl -X POST "http://192.168.86.51:8321/api/admin/build-taste-profile?profile=antialias"
```
All endpoints are backward compatible — omitting `profile` uses the "default" profile. Events with no profile assignment (including all existing events) belong to "default".
### Other operations
```bash