main
rb 67 lines 2.05 KB
Raw
1 # Hugging Face-style discovery page for open-weights model repositories.
2 class ModelsController < ApplicationController
3 def index
4 @query = params[:q].to_s.strip
5 @pipeline_tag = params[:pipeline_tag].presence
6 @library = params[:library].presence
7 @sort = params.fetch(:sort, "trending")
8
9 repos = Repository.models
10 .visible_to(current_user)
11 .includes(:user)
12 .to_a
13
14 # Decorate each repo with its parsed model card once, then filter/sort in
15 # Ruby — the card metadata lives in git, not the database.
16 cards = repos.map { |repo| [ repo, repo.model_card ] }
17
18 if @query.present?
19 q = @query.downcase
20 cards.select! do |repo, card|
21 repo.full_name.downcase.include?(q) ||
22 repo.description.to_s.downcase.include?(q) ||
23 card.tags.any? { |t| t.downcase.include?(q) }
24 end
25 end
26
27 @facets = build_facets(cards)
28
29 cards.select! { |_repo, card| card.pipeline_tag == @pipeline_tag } if @pipeline_tag
30 cards.select! { |_repo, card| card.library_name == @library } if @library
31
32 @models = sort_cards(cards)
33 end
34
35 private
36
37 def build_facets(cards)
38 {
39 pipeline_tags: tally(cards) { |_repo, card| card.pipeline_tag },
40 libraries: tally(cards) { |_repo, card| card.library_name }
41 }
42 end
43
44 def tally(cards)
45 cards.filter_map { |pair| yield(*pair) }
46 .tally
47 .sort_by { |label, count| [ -count, label ] }
48 end
49
50 def sort_cards(cards)
51 case @sort
52 when "downloads"
53 cards.sort_by { |repo, _| -repo.downloads_count }
54 when "likes"
55 cards.sort_by { |repo, _| -repo.stars_count }
56 when "recent"
57 cards.sort_by { |repo, _| repo.updated_at }.reverse
58 else # "trending" — a blend of likes, downloads and recency
59 cards.sort_by { |repo, _| -trending_score(repo) }
60 end
61 end
62
63 def trending_score(repo)
64 age_days = [ (Time.current - repo.updated_at) / 1.day, 0.5 ].max
65 ((repo.stars_count * 3) + Math.log10(repo.downloads_count + 1) * 10) / age_days
66 end
67 end