test: end-to-end user journey in CI; fix ALS vectors for small libraries
ci / api / api (push) Successful in 30s
ci / recommender / recommender (push) Successful in 30s
ci / db / db (push) Failing after 1m8s

- newApp (extracted from main) is the exact production wiring; the
  `ci / db` job now also runs it on PostgreSQL, Redis and Meilisearch
  and walks the main user flows over HTTP: first admin sign-in and
  credential change, local source and scan, search, album / artist /
  top tracks, signed stream, events and history, likes, fallback wave,
  playback state, upload queue up to review.
- ALS on a small library produced fewer than 64 factors and the insert
  into vector(64) failed; vectors are now zero-padded to the column size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 18:26:36 +03:00
1 parent f5c276092f
commit 2285107d04
5 files changed
+382 -11

No files matched your search

@@ -108,9 +108,20 @@ def should_activate(new: dict[str, Any], current: dict[str, Any] | None) -> bool
return float(new_ndcg) >= float(cur_ndcg) * (1 - MAX_REGRESSION)
# Размерность колонок track_embeddings.cf / user_profiles_rec.taste_cf (vector(64)).
CF_DIM = 64
def pad(v: np.ndarray, dim: int = CF_DIM) -> np.ndarray:
"""Маленькая библиотека даёт меньше факторов, чем колонка: добиваем нулями (косинус не меняется)."""
if v.shape[1] >= dim:
return v[:, :dim]
return np.pad(v, ((0, 0), (0, dim - v.shape[1])))
def save_als(conn: psycopg.Connection[Any], model: ALSModel, version: str, metrics: dict[str, Any]) -> None:
items = normalize(model.item_factors)
users = normalize(model.user_factors)
items = pad(normalize(model.item_factors))
users = pad(normalize(model.user_factors))
with conn.transaction():
with conn.cursor() as cur:
cur.executemany(