Spaces:
Running
Fix result-set truncation, prefer canonical copies, add name channel
Browse filesFound via merve's hf-find traces (merve/hf-find-traces).
- Relevance path fetched n=k, so _knn's LIMIT 2k left near-duplicate collapse
no room to backfill and searches silently returned fewer than k (9 of 10 for
"RF-DETR segmentation", 5 of 10 with a param filter). Pool is now k*10 on the
relevance path; the explicit sort modes keep k*4 so widening the pool cannot
trade their relevance for popularity.
- Dup-collapse kept whichever copy scored a hair higher on cosine, which is
routinely a 0-download mirror. It now keeps the most-downloaded member of a
near-duplicate group, in the group's existing rank position.
- Summary embeddings cannot retrieve a repo by name: "iSAID" returned Icelandic
census records and chip-design netlists, while ariG23498/iSAID (whose summary
never says "iSAID") was unreachable. Added an id-matching channel, RRF-fused
at half weight so it rescues a miss without outvoting the vector ranking.
Tokens must look like identifiers and match at an id boundary -- a bare
substring put RASSAISAID/finance-deepseek-prompts above the real iSAID repos.
- Refresh likes/downloads at load from the pipeline's metadata snapshot. Corpus
rows freeze those counts at the moment a card was last summarised, so
dronefreak/UAVid-2020 read 0 downloads against 2454 live. Missing snapshot is
non-fatal.
- Added POPULARITY_WEIGHT, defaulting to OFF. It scores well on a canonical-repo
eval but a blind 3-judge A/B over 24 unseen queries split by domain: mainstream
14-2 for the prior, niche/historical/GLAM 3-5 against it. Rationale in-file.
Verified by test_ranking.py (14 fixture assertions, no corpus or model needed).
|
@@ -10,6 +10,7 @@ backend so query vectors stay aligned with the seed document embeddings.
|
|
| 10 |
"""
|
| 11 |
import logging
|
| 12 |
import os
|
|
|
|
| 13 |
import time
|
| 14 |
from contextlib import asynccontextmanager
|
| 15 |
from typing import List, Optional
|
|
@@ -133,11 +134,39 @@ def load_table(cfg: str, table: str, has_param: bool):
|
|
| 133 |
)
|
| 134 |
)
|
| 135 |
""")
|
|
|
|
| 136 |
n = con.execute(f"SELECT count(*) FROM {table}").fetchone()[0]
|
| 137 |
STATE["counts"][table] = n
|
| 138 |
logger.info(f"loaded {table} (from {cfg}): {n:,} rows @ {EMB_DIM}d")
|
| 139 |
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
@asynccontextmanager
|
| 142 |
async def lifespan(app: FastAPI):
|
| 143 |
t0 = time.time()
|
|
@@ -273,23 +302,41 @@ def _vec_literal(vec):
|
|
| 273 |
DUP_SIM = 0.985 # near-identical cards (mirrors/re-uploads) collapse to the top-ranked copy
|
| 274 |
|
| 275 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
def _knn(table, qvec, k, where_sql, cols, params=None, return_emb=False):
|
| 277 |
"""Top-k by cosine, with near-duplicate collapse.
|
| 278 |
|
| 279 |
-
Overfetches
|
| 280 |
-
(cos >= DUP_SIM) to a higher-ranked kept row — mirror repos
|
| 281 |
-
adjacent result slots.
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
"""
|
| 284 |
import numpy as np
|
| 285 |
q = f"""SELECT {cols}, emb, array_cosine_similarity(emb, {_vec_literal(qvec)}) AS sim
|
| 286 |
-
FROM {table} {where_sql} ORDER BY sim DESC LIMIT {int(k) *
|
| 287 |
rows = con.execute(q, params or []).fetchall()
|
| 288 |
kept, kept_vecs = [], []
|
| 289 |
for r in rows:
|
| 290 |
v = np.asarray(r[-2], dtype=np.float32)
|
| 291 |
-
if kept_vecs
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
kept.append(r if return_emb else r[:-2] + (r[-1],))
|
| 294 |
kept_vecs.append(v)
|
| 295 |
if len(kept) >= int(k):
|
|
@@ -319,21 +366,88 @@ def _bm25_ids(kind: str, text: str, n: int) -> list:
|
|
| 319 |
return [r[0] for r in rows]
|
| 320 |
|
| 321 |
|
| 322 |
-
|
| 323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
similarity) and must pass the same metadata filters as the vector channel.
|
|
|
|
|
|
|
| 327 |
Returns rows in fused order, same tuple shape as _knn output.
|
| 328 |
"""
|
| 329 |
-
|
| 330 |
-
if not bm_ids:
|
| 331 |
return raw
|
| 332 |
rrf: dict = {}
|
| 333 |
for rank, rid in enumerate(r[0] for r in raw):
|
| 334 |
rrf[rid] = rrf.get(rid, 0.0) + 1.0 / (60 + rank)
|
| 335 |
-
for rank, rid in enumerate(
|
| 336 |
-
rrf[rid] = rrf.get(rid, 0.0) +
|
| 337 |
order = sorted(rrf, key=rrf.get, reverse=True)
|
| 338 |
known = {r[0]: r for r in raw}
|
| 339 |
missing = [rid for rid in order if rid not in known][: k * 2]
|
|
@@ -349,6 +463,11 @@ def _hybrid_fuse(table, kind, query, qvec, raw, k, where_sql, wparams):
|
|
| 349 |
return [known[rid] for rid in order if rid in known]
|
| 350 |
|
| 351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
async def _sort_results(rows, id_field, sort_by, k):
|
| 353 |
"""rows: list of dicts with keys id, similarity, summary, likes, downloads, [param_count]."""
|
| 354 |
if sort_by == "trending":
|
|
@@ -367,10 +486,64 @@ async def _sort_results(rows, id_field, sort_by, k):
|
|
| 367 |
rows.sort(key=lambda r: r.get("last_modified") or "", reverse=True)
|
| 368 |
rows = rows[:k]
|
| 369 |
else:
|
| 370 |
-
rows = rows[:k]
|
| 371 |
return rows
|
| 372 |
|
| 373 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
def _rows_to_dataset(raw):
|
| 375 |
out = []
|
| 376 |
for id_, summary, likes, downloads, param, task, license_, language, last_modified, sim in raw:
|
|
@@ -447,13 +620,17 @@ async def search_datasets(
|
|
| 447 |
):
|
| 448 |
try:
|
| 449 |
qvec = embed_query(f"Instruct: {DS_TASK}\nQuery:{query}")
|
| 450 |
-
n =
|
| 451 |
where_sql, wparams = _where(min_likes, min_downloads,
|
| 452 |
task=task, license=license, language=language,
|
| 453 |
modified_after=modified_after)
|
| 454 |
raw = _knn("dataset_cards", qvec, n, where_sql, COLS, wparams)
|
| 455 |
if hybrid:
|
| 456 |
raw = _hybrid_fuse("dataset_cards", "datasets", query, qvec, raw, k, where_sql, wparams)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
rows = await _sort_results(_rows_to_dataset(raw), "dataset", sort_by, k)
|
| 458 |
return QueryResponse(results=[QueryResult(**{k2: v for k2, v in r.items() if k2 != "_id"}) for r in rows])
|
| 459 |
except Exception as e:
|
|
@@ -512,13 +689,17 @@ async def search_models(
|
|
| 512 |
try:
|
| 513 |
use_param = min_param_count > 0 or max_param_count is not None
|
| 514 |
qvec = embed_query(f"search_query: {query}")
|
| 515 |
-
n =
|
| 516 |
where_sql, wparams = _where(min_likes, min_downloads, min_param_count, max_param_count, use_param,
|
| 517 |
task=task, license=license, language=language,
|
| 518 |
modified_after=modified_after)
|
| 519 |
raw = _knn("model_cards", qvec, n, where_sql, COLS, wparams)
|
| 520 |
if hybrid:
|
| 521 |
raw = _hybrid_fuse("model_cards", "models", query, qvec, raw, k, where_sql, wparams)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
rows = await _sort_results(_rows_to_model(raw), "model", sort_by, k)
|
| 523 |
return ModelQueryResponse(results=[ModelQueryResult(**{k2: v for k2, v in r.items() if k2 != "_id"}) for r in rows])
|
| 524 |
except Exception as e:
|
|
|
|
| 10 |
"""
|
| 11 |
import logging
|
| 12 |
import os
|
| 13 |
+
import re
|
| 14 |
import time
|
| 15 |
from contextlib import asynccontextmanager
|
| 16 |
from typing import List, Optional
|
|
|
|
| 134 |
)
|
| 135 |
)
|
| 136 |
""")
|
| 137 |
+
_refresh_metadata(cfg, table)
|
| 138 |
n = con.execute(f"SELECT count(*) FROM {table}").fetchone()[0]
|
| 139 |
STATE["counts"][table] = n
|
| 140 |
logger.info(f"loaded {table} (from {cfg}): {n:,} rows @ {EMB_DIM}d")
|
| 141 |
|
| 142 |
|
| 143 |
+
def _refresh_metadata(cfg: str, table: str) -> None:
|
| 144 |
+
"""Overwrite likes/downloads from the pipeline's metadata snapshot, if present.
|
| 145 |
+
|
| 146 |
+
Corpus rows carry the counts a repo had when its card was last summarised, and
|
| 147 |
+
cards rarely change — so a repo can sit at 0 downloads long after it is widely
|
| 148 |
+
used. Ranking now leans on those counts, so refresh them from the small
|
| 149 |
+
(id, downloads, likes) file the delta pipeline publishes each run. Absent file
|
| 150 |
+
or unreadable snapshot is not fatal: the corpus values still work, just stale.
|
| 151 |
+
"""
|
| 152 |
+
try:
|
| 153 |
+
path = hf_hub_download(EMBEDDINGS_REPO, f"_state/metadata/{cfg}.parquet", repo_type="dataset")
|
| 154 |
+
except Exception as e:
|
| 155 |
+
logger.warning(f"{table}: no metadata snapshot ({e}); using corpus counts")
|
| 156 |
+
return
|
| 157 |
+
try:
|
| 158 |
+
n = con.execute(f"""
|
| 159 |
+
UPDATE {table} AS t
|
| 160 |
+
SET downloads = COALESCE(m.downloads, 0)::BIGINT,
|
| 161 |
+
likes = COALESCE(m.likes, 0)::BIGINT
|
| 162 |
+
FROM read_parquet('{path}') AS m
|
| 163 |
+
WHERE m.id = t.id
|
| 164 |
+
""").fetchone()
|
| 165 |
+
logger.info(f"{table}: refreshed metadata for {(n[0] if n else 0):,} rows")
|
| 166 |
+
except Exception as e:
|
| 167 |
+
logger.warning(f"{table}: metadata refresh failed ({e}); using corpus counts")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
@asynccontextmanager
|
| 171 |
async def lifespan(app: FastAPI):
|
| 172 |
t0 = time.time()
|
|
|
|
| 302 |
DUP_SIM = 0.985 # near-identical cards (mirrors/re-uploads) collapse to the top-ranked copy
|
| 303 |
|
| 304 |
|
| 305 |
+
DUP_OVERFETCH = 6 # candidates per requested row, so dup-collapse can be backfilled
|
| 306 |
+
_DL_IDX = 3 # position of `downloads` within COLS (id, summary, likes, downloads, ...)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
def _knn(table, qvec, k, where_sql, cols, params=None, return_emb=False):
|
| 310 |
"""Top-k by cosine, with near-duplicate collapse.
|
| 311 |
|
| 312 |
+
Overfetches DUP_OVERFETCH x, then greedily drops rows whose embedding is
|
| 313 |
+
~identical (cos >= DUP_SIM) to a higher-ranked kept row — mirror repos
|
| 314 |
+
otherwise eat adjacent result slots. When a duplicate is more downloaded than
|
| 315 |
+
the copy already kept, it replaces it in place: the group keeps its ranking
|
| 316 |
+
position but is represented by the canonical repo rather than whichever
|
| 317 |
+
mirror happened to score a hair higher.
|
| 318 |
+
|
| 319 |
+
The overfetch has to exceed the dup rate or collapse silently returns fewer
|
| 320 |
+
than k (popular model families are dominated by near-identical conversion
|
| 321 |
+
cards); the LIMIT is cheap because the full cosine scan dominates either way.
|
| 322 |
+
Returns (cols..., sim) tuples; with return_emb=True, (cols..., emb, sim).
|
| 323 |
"""
|
| 324 |
import numpy as np
|
| 325 |
q = f"""SELECT {cols}, emb, array_cosine_similarity(emb, {_vec_literal(qvec)}) AS sim
|
| 326 |
+
FROM {table} {where_sql} ORDER BY sim DESC LIMIT {int(k) * DUP_OVERFETCH}"""
|
| 327 |
rows = con.execute(q, params or []).fetchall()
|
| 328 |
kept, kept_vecs = [], []
|
| 329 |
for r in rows:
|
| 330 |
v = np.asarray(r[-2], dtype=np.float32)
|
| 331 |
+
if kept_vecs:
|
| 332 |
+
sims = np.stack(kept_vecs) @ v
|
| 333 |
+
j = int(np.argmax(sims))
|
| 334 |
+
if float(sims[j]) >= DUP_SIM:
|
| 335 |
+
# Same card, different repo: keep whichever copy people actually use.
|
| 336 |
+
if (r[_DL_IDX] or 0) > (kept[j][_DL_IDX] or 0):
|
| 337 |
+
kept[j] = r if return_emb else r[:-2] + (r[-1],)
|
| 338 |
+
kept_vecs[j] = v
|
| 339 |
+
continue
|
| 340 |
kept.append(r if return_emb else r[:-2] + (r[-1],))
|
| 341 |
kept_vecs.append(v)
|
| 342 |
if len(kept) >= int(k):
|
|
|
|
| 366 |
return [r[0] for r in rows]
|
| 367 |
|
| 368 |
|
| 369 |
+
# A query that is short and has no spaces is a repo name, not a description
|
| 370 |
+
# ("iSAID", "rtdetr"). Summary embeddings cannot retrieve those: the summary of
|
| 371 |
+
# ariG23498/iSAID never says "iSAID", and "iSAID" alone retrieves Icelandic
|
| 372 |
+
# census records via ID-ish subword tokens. Matching the id directly is the only
|
| 373 |
+
# channel that can find them.
|
| 374 |
+
_NAME_MAX_TOKENS = 3 # "iSAID instance segmentation" still carries a name token
|
| 375 |
+
_NAME_MIN_CHARS = 4 # shorter tokens ("ocr", "sam") match far too much
|
| 376 |
+
NAME_CHANNEL_WEIGHT = 0.5 # below the vector channel: rescue a miss, never dominate
|
| 377 |
+
|
| 378 |
+
# Hyphenated descriptors that pass the identifier test but name no repo.
|
| 379 |
+
_NAME_STOPWORDS = frozenset({"real-time", "state-of-the-art", "open-source", "fine-tuned"})
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def _is_identifier_like(tok: str) -> bool:
|
| 383 |
+
"""True for tokens shaped like a repo/model name rather than an English word.
|
| 384 |
+
|
| 385 |
+
Names carry a digit (yolov10), an internal capital (iSAID, RF-DETR), or a
|
| 386 |
+
separator (rf-detr). Ordinary description words ("herbarium", "element") carry
|
| 387 |
+
none of these, and matching those against ids returns download-ranked noise.
|
| 388 |
+
"""
|
| 389 |
+
if tok.lower() in _NAME_STOPWORDS:
|
| 390 |
+
return False
|
| 391 |
+
return bool(
|
| 392 |
+
any(c.isdigit() for c in tok)
|
| 393 |
+
or any(c in "-_." for c in tok)
|
| 394 |
+
or re.search(r"[a-z][A-Z]|^[A-Z]{2,}$", tok)
|
| 395 |
+
)
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
def _name_tokens(query: str) -> list:
|
| 399 |
+
"""Tokens from the query that plausibly name a repo rather than describe one."""
|
| 400 |
+
if len(query.strip()) > 60 or len(query.split()) > _NAME_MAX_TOKENS:
|
| 401 |
+
return []
|
| 402 |
+
return [
|
| 403 |
+
tok
|
| 404 |
+
for raw in re.split(r"[\s,]+", query.strip())
|
| 405 |
+
if len(tok := raw.strip("\"'()[]")) >= _NAME_MIN_CHARS and _is_identifier_like(tok)
|
| 406 |
+
]
|
| 407 |
+
|
| 408 |
|
| 409 |
+
def _name_ids(table, tokens, n, where_sql, wparams):
|
| 410 |
+
"""Top-n ids whose name contains any of these tokens, most-downloaded first.
|
| 411 |
+
|
| 412 |
+
The token must start at an id boundary (start, or after / _ . -). A bare
|
| 413 |
+
substring match puts `RASSAISAID/finance-deepseek-prompts` above the real
|
| 414 |
+
`ariG23498/iSAID` for the query "iSAID". The trailing side is deliberately left
|
| 415 |
+
open so name variants still match — "yolov10" has to find `jameslahm/yolov10x`.
|
| 416 |
+
|
| 417 |
+
Ordering by downloads (not cosine) is deliberate: this channel exists to find
|
| 418 |
+
the repo actually named, and RRF only uses the rank, so the canonical copy of
|
| 419 |
+
a name should lead its own channel.
|
| 420 |
+
"""
|
| 421 |
+
if not tokens:
|
| 422 |
+
return []
|
| 423 |
+
cond = f"AND {where_sql[6:]}" if where_sql else ""
|
| 424 |
+
ors = " OR ".join("regexp_matches(id, ?, 'i')" for _ in tokens)
|
| 425 |
+
pats = [f"(^|[/_.-]){re.escape(t)}" for t in tokens]
|
| 426 |
+
rows = con.execute(
|
| 427 |
+
f"""SELECT id FROM {table}
|
| 428 |
+
WHERE ({ors}) {cond}
|
| 429 |
+
ORDER BY downloads DESC LIMIT {int(n)}""",
|
| 430 |
+
pats + list(wparams or []),
|
| 431 |
+
).fetchall()
|
| 432 |
+
return [r[0] for r in rows]
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
def _fuse(table, qvec, raw, extra_ids, k, where_sql, wparams, weight=1.0):
|
| 436 |
+
"""RRF-fuse the vector KNN rows with a second channel's id list.
|
| 437 |
+
|
| 438 |
+
Channel-only hits are fetched from the in-memory table (with their true cosine
|
| 439 |
similarity) and must pass the same metadata filters as the vector channel.
|
| 440 |
+
`weight` scales the second channel's contribution, so a speculative channel can
|
| 441 |
+
rescue a miss without outvoting the vector ranking.
|
| 442 |
Returns rows in fused order, same tuple shape as _knn output.
|
| 443 |
"""
|
| 444 |
+
if not extra_ids:
|
|
|
|
| 445 |
return raw
|
| 446 |
rrf: dict = {}
|
| 447 |
for rank, rid in enumerate(r[0] for r in raw):
|
| 448 |
rrf[rid] = rrf.get(rid, 0.0) + 1.0 / (60 + rank)
|
| 449 |
+
for rank, rid in enumerate(extra_ids):
|
| 450 |
+
rrf[rid] = rrf.get(rid, 0.0) + weight / (60 + rank)
|
| 451 |
order = sorted(rrf, key=rrf.get, reverse=True)
|
| 452 |
known = {r[0]: r for r in raw}
|
| 453 |
missing = [rid for rid in order if rid not in known][: k * 2]
|
|
|
|
| 463 |
return [known[rid] for rid in order if rid in known]
|
| 464 |
|
| 465 |
|
| 466 |
+
def _hybrid_fuse(table, kind, query, qvec, raw, k, where_sql, wparams):
|
| 467 |
+
"""RRF-fuse vector KNN rows with a BM25 channel over full card text."""
|
| 468 |
+
return _fuse(table, qvec, raw, _bm25_ids(kind, query, k * 2), k, where_sql, wparams)
|
| 469 |
+
|
| 470 |
+
|
| 471 |
async def _sort_results(rows, id_field, sort_by, k):
|
| 472 |
"""rows: list of dicts with keys id, similarity, summary, likes, downloads, [param_count]."""
|
| 473 |
if sort_by == "trending":
|
|
|
|
| 486 |
rows.sort(key=lambda r: r.get("last_modified") or "", reverse=True)
|
| 487 |
rows = rows[:k]
|
| 488 |
else:
|
| 489 |
+
rows = _apply_popularity_prior(rows)[:k]
|
| 490 |
return rows
|
| 491 |
|
| 492 |
|
| 493 |
+
# Optional log-scaled download term, added to cosine to break ties toward the copy
|
| 494 |
+
# people actually use (a 0-download mirror otherwise outranks the canonical repo,
|
| 495 |
+
# since their cards read the same).
|
| 496 |
+
#
|
| 497 |
+
# OFF by default, deliberately. On hf-find's eval/queries.jsonl it looks like a
|
| 498 |
+
# clear win (mean recall 0.363 -> 0.435, zero-download slots 28.3% -> 6.2% at 0.08),
|
| 499 |
+
# but that eval's expected repos were chosen *because* they are canonical, so it
|
| 500 |
+
# cannot help but reward a popularity prior. A blind 3-judge A/B over 24 unseen
|
| 501 |
+
# queries split by domain instead:
|
| 502 |
+
#
|
| 503 |
+
# mainstream queries prior 14 - 2 baseline
|
| 504 |
+
# niche / historical / GLAM prior 3 - 5 baseline
|
| 505 |
+
#
|
| 506 |
+
# The mechanism: a 150k-download repo gets the full weight while a 50-download
|
| 507 |
+
# exact match gets ~a third of it, so any similarity gap under ~0.054 flips — and
|
| 508 |
+
# niche queries live in exactly that regime. Concretely, at 0.08 the query "OCR for
|
| 509 |
+
# historical printed text" ranks generic dots.ocr-1.5 above
|
| 510 |
+
# wjbmattingly/LightOnOCR-2-1B-cultural-heritage-english.
|
| 511 |
+
#
|
| 512 |
+
# So this is a domain trade, not a free improvement. Enable per-deployment with
|
| 513 |
+
# POPULARITY_WEIGHT=0.08 if the audience is mainstream model/dataset discovery.
|
| 514 |
+
POPULARITY_WEIGHT = float(os.getenv("POPULARITY_WEIGHT", "0"))
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def _rerank_pool(k: int, sort_by: str) -> int:
|
| 518 |
+
"""Candidate rows to pull before re-ranking down to k.
|
| 519 |
+
|
| 520 |
+
The relevance path re-ranks (popularity prior, optional name fusion) and gains
|
| 521 |
+
from a deeper pool: on eval/queries.jsonl mean recall is 0.363 at k*1, 0.399 at
|
| 522 |
+
k*4 and 0.435 at k*10, flat thereafter. The cosine scan touches every row
|
| 523 |
+
regardless, so a larger LIMIT is close to free.
|
| 524 |
+
|
| 525 |
+
The explicit sort modes keep the old k*4: they sort hard by likes/downloads, so
|
| 526 |
+
widening the pool would trade relevance for popularity rather than break ties.
|
| 527 |
+
"""
|
| 528 |
+
return k * (10 if sort_by == "similarity" else 4)
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def _apply_popularity_prior(rows):
|
| 532 |
+
"""Re-rank by similarity + a log-scaled, per-result-set-normalised download term."""
|
| 533 |
+
if POPULARITY_WEIGHT <= 0 or not rows:
|
| 534 |
+
return rows
|
| 535 |
+
import math
|
| 536 |
+
mx = max((r.get("downloads") or 0) for r in rows)
|
| 537 |
+
if mx <= 0:
|
| 538 |
+
return rows
|
| 539 |
+
denom = math.log1p(mx)
|
| 540 |
+
return sorted(
|
| 541 |
+
rows,
|
| 542 |
+
key=lambda r: r["similarity"] + POPULARITY_WEIGHT * (math.log1p(r.get("downloads") or 0) / denom),
|
| 543 |
+
reverse=True,
|
| 544 |
+
)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
def _rows_to_dataset(raw):
|
| 548 |
out = []
|
| 549 |
for id_, summary, likes, downloads, param, task, license_, language, last_modified, sim in raw:
|
|
|
|
| 620 |
):
|
| 621 |
try:
|
| 622 |
qvec = embed_query(f"Instruct: {DS_TASK}\nQuery:{query}")
|
| 623 |
+
n = _rerank_pool(k, sort_by)
|
| 624 |
where_sql, wparams = _where(min_likes, min_downloads,
|
| 625 |
task=task, license=license, language=language,
|
| 626 |
modified_after=modified_after)
|
| 627 |
raw = _knn("dataset_cards", qvec, n, where_sql, COLS, wparams)
|
| 628 |
if hybrid:
|
| 629 |
raw = _hybrid_fuse("dataset_cards", "datasets", query, qvec, raw, k, where_sql, wparams)
|
| 630 |
+
elif (name_toks := _name_tokens(query)):
|
| 631 |
+
raw = _fuse("dataset_cards", qvec, raw,
|
| 632 |
+
_name_ids("dataset_cards", name_toks, k, where_sql, wparams),
|
| 633 |
+
k, where_sql, wparams, weight=NAME_CHANNEL_WEIGHT)
|
| 634 |
rows = await _sort_results(_rows_to_dataset(raw), "dataset", sort_by, k)
|
| 635 |
return QueryResponse(results=[QueryResult(**{k2: v for k2, v in r.items() if k2 != "_id"}) for r in rows])
|
| 636 |
except Exception as e:
|
|
|
|
| 689 |
try:
|
| 690 |
use_param = min_param_count > 0 or max_param_count is not None
|
| 691 |
qvec = embed_query(f"search_query: {query}")
|
| 692 |
+
n = _rerank_pool(k, sort_by)
|
| 693 |
where_sql, wparams = _where(min_likes, min_downloads, min_param_count, max_param_count, use_param,
|
| 694 |
task=task, license=license, language=language,
|
| 695 |
modified_after=modified_after)
|
| 696 |
raw = _knn("model_cards", qvec, n, where_sql, COLS, wparams)
|
| 697 |
if hybrid:
|
| 698 |
raw = _hybrid_fuse("model_cards", "models", query, qvec, raw, k, where_sql, wparams)
|
| 699 |
+
elif (name_toks := _name_tokens(query)):
|
| 700 |
+
raw = _fuse("model_cards", qvec, raw,
|
| 701 |
+
_name_ids("model_cards", name_toks, k, where_sql, wparams),
|
| 702 |
+
k, where_sql, wparams, weight=NAME_CHANNEL_WEIGHT)
|
| 703 |
rows = await _sort_results(_rows_to_model(raw), "model", sort_by, k)
|
| 704 |
return ModelQueryResponse(results=[ModelQueryResult(**{k2: v for k2, v in r.items() if k2 != "_id"}) for r in rows])
|
| 705 |
except Exception as e:
|