feat(social): add listening activity, shared rooms and modern chat
ci / api / api (push) Canceled after 0s
ci / module boundaries / boundaries (push) Canceled after 0s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Canceled after 11m29s
ci / db / db (push) Canceled after 11m22s
ci / mobile / mobile (Metro-бандл Android) (push) Canceled after 6m43s
ci / recommender / recommender (push) Canceled after 6m47s
ci / ts / ts (packages + web) (push) Canceled after 1s

This commit is contained in:
mrleo1nid committed 2026-10-23 10:45:40 +03:00
1 parent 0a05bfe2cd
commit 11a16cd023
59 files changed
+5022 -542

No files matched your search

+10 -1
View File
@@ -313,6 +313,7 @@ def build_batch(
rng: np.random.Generator | None = None,
ranker: Ranker | None = None,
ctx: Context | None = None,
friend_ids: list[str] | None = None,
) -> list[Pick]:
"""Кандидаты → фильтры → оценка → соответствие контексту → MMR с правилами разнообразия."""
rng = rng or np.random.default_rng()
@@ -363,6 +364,8 @@ def build_batch(
if seeds:
q = index.emb[seeds].mean(axis=0)
add(_top((index.emb @ q).astype(np.float64), allowed & index.has_emb, 100), "seed")
friends = [index.pos[t] for t in (friend_ids or []) if t in index.pos and allowed[index.pos[t]]][:10]
add(friends, "friends")
query = np.maximum(sim_session, sim_taste) if svec is not None or len(centroids) else None
if query is not None:
# «Новое» помечаем раньше audio_sim: неслышанный трек рядом со вкусом считается исследованием.
@@ -446,6 +449,8 @@ def build_batch(
pred = ranker.predict(x)
score = np.argsort(np.argsort(pred)) / max(len(pred) - 1, 1)
# Social influence is small, context-sensitive, and survives the learned ranker.
score = score + 0.06 * np.isin(cand, friends) * f
# 5. MMR + правила: 1 трек исполнителя на пачку, не больше 2 на последние 10; доля «нового».
picks: list[Pick] = []
chosen: list[int] = []
@@ -457,6 +462,8 @@ def build_batch(
best_j, best_v = -1, -np.inf
for j in order:
i = int(cand[j])
if sources[i] == "friends" and any(p.source == "friends" for p in picks):
continue
a = index.artist[i]
if any(index.artist[c] == a for c in chosen) or recent_artists.count(a) >= 2:
continue
@@ -471,7 +478,9 @@ def build_batch(
continue
if chosen:
chosen_artists = {index.artist[c] for c in chosen}
rest = [j for j in order if index.artist[int(cand[j])] in chosen_artists]
social_chosen = any(p.source == "friends" for p in picks)
rest = [j for j in order if index.artist[int(cand[j])] in chosen_artists
and not (sources[int(cand[j])] == "friends" and social_chosen)]
if not rest:
break
best_j = max(rest, key=lambda j: score[j])