feat(api): My Wave sessions with a Go fallback and wave metrics (M4)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 40s
ci / mobile / mobile (Metro-бандл Android) (push) Failing after 9s
ci / api / api (push) Successful in 35s
ci / ts / ts (packages + web) (push) Successful in 51s

- /wave/options, POST /wave/sessions (activity, mood, seed),
  /next with reactions, PATCH to switch the context on the fly, DELETE
- the recommender is called with an 800 ms budget; on failure the api
  serves a fallback wave of likes, favourite artists and random
  available tracks with the same dislike / recent / served filters
- every served track is an impression with its source and model; wave
  events (context_type wave) set its outcome, reactions win
- library status shows the analysis queue and 7-day wave metrics:
  listen-through, early skip and like rate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 12:23:17 +03:00
1 parent ef29b85320
commit 3908814cda
13 files changed
+1811 -162

No files matched your search

+6 -1
View File
@@ -15,6 +15,7 @@ import (
"pipu/api/internal/playback"
"pipu/api/internal/search"
"pipu/api/internal/system"
"pipu/api/internal/wave"
)
// Deps — модули, реализующие части контракта API.
@@ -27,6 +28,8 @@ type Deps struct {
Search *search.Handler
// Playback — стрим, события, история, реакции, состояние плеера.
Playback *playback.Handler
// Wave — «Моя волна».
Wave *wave.Handler
// AuthService проверяет access-токены в middleware. nil — только для тестов служебных эндпоинтов.
AuthService *auth.Service
}
@@ -40,6 +43,7 @@ type (
libraryHandler = library.Handler
searchHandler = search.Handler
playbackHandler = playback.Handler
waveHandler = wave.Handler
)
type handlers struct {
@@ -48,6 +52,7 @@ type handlers struct {
*libraryHandler
*searchHandler
*playbackHandler
*waveHandler
}
var _ gen.StrictServerInterface = handlers{}
@@ -67,7 +72,7 @@ func NewRouter(d Deps) http.Handler {
r.NotFound(httpx.NotFound)
r.MethodNotAllowed(httpx.MethodNotAllowed)
strict := gen.NewStrictHandlerWithOptions(handlers{systemHandler: d.System, authHandler: d.Auth, libraryHandler: d.Library, searchHandler: d.Search, playbackHandler: d.Playback},
strict := gen.NewStrictHandlerWithOptions(handlers{systemHandler: d.System, authHandler: d.Auth, libraryHandler: d.Library, searchHandler: d.Search, playbackHandler: d.Playback, waveHandler: d.Wave},
[]gen.StrictMiddlewareFunc{httpx.StrictRequest}, gen.StrictHTTPServerOptions{
RequestErrorHandlerFunc: httpx.RequestError,
ResponseErrorHandlerFunc: httpx.ResponseError(log),