โ† All projects
In progress v0.1.0 Updated In development

Viewr

A video app with a real recommendation engine behind it, not a chronological list.

FlutterPythonRecommendationsBackend

The two-stage funnel

Every surface runs the same shape: candidate generation reduces the catalogue to hundreds by approximate nearest-neighbour retrieval over two-tower embeddings, then a ranking model scores those candidates. What changes per surface is the objective and the signal weighting, not the structure.

One item store, many surfaces

Home, watch-next, search and subscriptions read from a single shared item store. Keeping four disjoint databases is the mistake this design exists to avoid, because it makes every signal four times more expensive to collect.

Signals

Ranking weights engagement signals against satisfaction signals. Engagement is cheap and misleading on its own; satisfaction is sparse. The per-surface weighting is where most of the tuning lives.

surface: home
  engagement   click, watch_time
  satisfaction completion, explicit_rating
  weights      0.6 / 0.4

Rebuilding the index

Embeddings are recomputed in a batch job and the ANN index is rebuilt from them. Retrieval reads the built index; nothing computes embeddings at request time.

$ python -m viewr.jobs.embeddings
$ python -m viewr.jobs.build_index --out index/current