The wave logs each served candidate's feature vector to rec_impression_features.
Nightly training joins them with impression outcomes, validates on the last week
and activates the model only when it beats the v1 order and the active model.
The service loads the active ranker and uses its order in place of the v1 score.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Implicit scores from play events and reactions per the signals table
(90-day half-life, weaker skips outside the wave, dislikes override).
- numpy ALS for implicit feedback; time-split evaluation on the last
7 days (NDCG@10, Recall@50) against a popularity baseline; a new model
is activated only if NDCG@10 does not regress by more than 2%.
- Vectors are written to track_embeddings.cf and user_profiles_rec.taste_cf,
versions to model_versions; NightlyTrainer runs at TRAIN_HOUR and on
first start, `pipu-rec train` runs it manually.
- Wave: cf and item_cf candidate sources and a 0.10 cf term in the v1
score; impressions are tagged heuristic-v1+cf.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- migration 00006: track features and embeddings (pgvector, HNSW),
analysis queue filled by a trigger on new tracks, wave sessions and
impressions, recommender profiles, model versions
- analyzer basic-1 (ADR-014): ffmpeg decode, three 30 s excerpts,
256-d timbre/rhythm embedding, tempo by onset autocorrelation, key,
loudness, energy, danceability, acousticness, intro length; worker
with SKIP LOCKED claims, retries and re-analysis on version change
- wave engine v1: candidates (session/taste similarity, fresh,
liked artists, rediscover, popular, exploration, seed), filters
(dislikes, played in 3 h, already served), heuristic score, activity
and mood fit from contexts.yaml with relaxing thresholds, MMR with
one track per artist, exploration share, session vector updated by
reactions and a change of course after three skips
- POST /internal/wave/batch with the catalog index cached in memory
and the session kept in Redis
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
Security and robustness:
- storage-agent rejects an empty INTERNAL_TOKEN (config notEmpty + middleware)
- api takes the client IP only from X-Real-IP (chi RealIP trusted True-Client-IP)
- /readyz reports ok/fail and logs the cause; checks run in parallel with a timeout
- storage-agent shuts down gracefully; shared httpx.NewServer/Serve with IdleTimeout
- goose migrations take a Postgres advisory lock
- recommender readyz: per-check timeout, redis socket timeouts
Clients:
- Android release allows cleartext HTTP for LAN servers (expo-build-properties)
- checkServer times out after 10s; connect button is disabled while checking
Deploy and CI:
- install.sh hands cache/uploads/updates/models to the non-root service users
- update.sh refuses downgrades unless PIPU_ALLOW_DOWNGRADE=1
- recommender/analyzer get only the env they need; required secrets fail fast
- nginx security headers and CSP; strip True-Client-IP
- dev compose binds ports to 127.0.0.1; postgres/redis images pinned
- version.mjs fails on a malformed explicit tag instead of releasing 0.0.0-dev
- contract check catches untracked generated files
- final releases fail without a release keystore; changelog skips rc tags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>