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

+215 -1
View File
@@ -17,6 +17,7 @@ tags:
- name: search
- name: playback
- name: collection
- name: wave
security:
- bearerAuth: []
paths:
@@ -974,6 +975,93 @@ paths:
description: Снято
default:
$ref: "#/components/responses/Problem"
/api/v1/wave/options:
get:
operationId: getWaveOptions
tags: [wave]
summary: Занятия и настроения (ключи для i18n и иконки)
responses:
"200":
description: Варианты
content:
application/json:
schema:
$ref: "#/components/schemas/WaveOptions"
default:
$ref: "#/components/responses/Problem"
/api/v1/wave/sessions:
post:
operationId: startWave
tags: [wave]
summary: Начать «Мою волну»
requestBody:
required: false
content:
application/json:
schema:
$ref: "#/components/schemas/WaveStart"
responses:
"201":
description: Сессия и первая пачка
content:
application/json:
schema:
$ref: "#/components/schemas/WaveBatch"
default:
$ref: "#/components/responses/Problem"
/api/v1/wave/sessions/{id}:
parameters:
- $ref: "#/components/parameters/EntityId"
patch:
operationId: updateWave
tags: [wave]
summary: Сменить занятие / настроение на лету — пачка пересобирается
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/WaveContext"
responses:
"200":
description: Новая пачка
content:
application/json:
schema:
$ref: "#/components/schemas/WaveBatch"
default:
$ref: "#/components/responses/Problem"
delete:
operationId: endWave
tags: [wave]
summary: Завершить сессию волны
responses:
"204":
description: Завершена
default:
$ref: "#/components/responses/Problem"
/api/v1/wave/sessions/{id}/next:
parameters:
- $ref: "#/components/parameters/EntityId"
post:
operationId: nextWave
tags: [wave]
summary: Следующая пачка с учётом реакций на выданные треки
requestBody:
required: false
content:
application/json:
schema:
$ref: "#/components/schemas/WaveNext"
responses:
"200":
description: Пачка
content:
application/json:
schema:
$ref: "#/components/schemas/WaveBatch"
default:
$ref: "#/components/responses/Problem"
components:
parameters:
SourceId:
@@ -1566,8 +1654,12 @@ components:
nullable: true
LibraryStatus:
type: object
required: [artists, albums, tracks, files, missing_files, file_errors, sources]
required: [artists, albums, tracks, files, missing_files, file_errors, sources, analysis, wave]
properties:
analysis:
$ref: "#/components/schemas/AnalysisStatus"
wave:
$ref: "#/components/schemas/WaveMetrics"
artists:
type: integer
albums:
@@ -2022,6 +2114,128 @@ components:
properties:
reaction:
$ref: "#/components/schemas/Reaction"
WaveOption:
type: object
required: [key, icon]
properties:
key:
type: string
example: running
icon:
type: string
description: Имя иконки Material Symbols
WaveOptions:
type: object
required: [activities, moods]
properties:
activities:
type: array
items:
$ref: "#/components/schemas/WaveOption"
moods:
type: array
items:
$ref: "#/components/schemas/WaveOption"
WaveContext:
type: object
properties:
activity:
type: string
nullable: true
mood:
type: string
nullable: true
WaveStart:
type: object
properties:
activity:
type: string
nullable: true
mood:
type: string
nullable: true
seed:
type: object
required: [type, id]
properties:
type:
type: string
enum: [track, album, artist]
id:
type: string
format: uuid
WaveFeedback:
type: object
required: [track_id, outcome]
properties:
track_id:
type: string
format: uuid
outcome:
type: string
enum: [complete, skip, skip_early, like, dislike]
WaveNext:
type: object
properties:
feedback:
type: array
maxItems: 50
items:
$ref: "#/components/schemas/WaveFeedback"
WaveBatch:
type: object
required: [session_id, activity, mood, tracks, fallback]
properties:
session_id:
type: string
format: uuid
activity:
type: string
nullable: true
mood:
type: string
nullable: true
tracks:
type: array
items:
$ref: "#/components/schemas/Track"
fallback:
type: boolean
description: Рекомендатель недоступен — пачка собрана упрощённо (лайки, любимые исполнители)
AnalysisStatus:
type: object
required: [queued, running, done, failed, analyzer_ver]
properties:
queued:
type: integer
running:
type: integer
done:
type: integer
failed:
type: integer
analyzer_ver:
type: string
nullable: true
WaveMetrics:
type: object
required: [served, listen_through_rate, early_skip_rate, like_rate, sessions]
description: Метрики волны за 7 дней
properties:
served:
type: integer
listen_through_rate:
type: number
nullable: true
early_skip_rate:
type: number
nullable: true
like_rate:
type: number
nullable: true
description: Лайков на 100 треков
sessions:
type: integer
Session:
type: object
required: [id, device_name, client_type, created_at, last_used_at, current]