Spaces:
Running on Zero
Running on Zero
sync: from ael backend/space/
Browse files
rerank.py
CHANGED
|
@@ -135,6 +135,11 @@ MIN_KEEP = int(os.environ.get("AEL_RERANK_MIN_KEEP", "2"))
|
|
| 135 |
# bounds the work, and this is only a backstop against a pathologically long set.
|
| 136 |
MAX_SCORED = int(os.environ.get("AEL_RERANK_MAX", "10"))
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
# Recent (query, scores) for threshold tuning. Bounded so a long-lived Space
|
| 139 |
# cannot grow without limit.
|
| 140 |
RECENT = deque(maxlen=int(os.environ.get("AEL_RERANK_LOG", "200")))
|
|
@@ -289,10 +294,23 @@ def _question(query):
|
|
| 289 |
|
| 290 |
def _passage(result):
|
| 291 |
"""The text the model judges. Title carries most of the topical signal;
|
| 292 |
-
snippet is what we have of the page without fetching it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
title = str(result.get("title") or "").strip()
|
| 294 |
snippet = str(result.get("snippet") or "").strip()
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
|
| 298 |
def _stamp(out):
|
|
@@ -362,7 +380,12 @@ def rerank(query, results, budget_ms=800):
|
|
| 362 |
p = float(res["answers"]["relevant"]["noul"])
|
| 363 |
except Exception as e: # noqa: BLE001
|
| 364 |
print(f"[rerank] score failed for result {i}: {type(e).__name__}: {e}", flush=True)
|
| 365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
scored.append((p, i, r))
|
| 367 |
judged += 1
|
| 368 |
|
|
@@ -377,7 +400,11 @@ def rerank(query, results, budget_ms=800):
|
|
| 377 |
out["judged"] = judged
|
| 378 |
|
| 379 |
if not any(p is not None for p, _, _ in scored):
|
| 380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
# Highest score first. Unscored results keep their relative position at the
|
| 383 |
# end rather than being dropped: we have no opinion about them.
|
|
|
|
| 135 |
# bounds the work, and this is only a backstop against a pathologically long set.
|
| 136 |
MAX_SCORED = int(os.environ.get("AEL_RERANK_MAX", "10"))
|
| 137 |
|
| 138 |
+
# How much text the model is asked to judge. The model's context is 512 tokens;
|
| 139 |
+
# a title plus a full snippet can exceed it and the model raises rather than
|
| 140 |
+
# truncating, which used to surface as a silent per-query failure.
|
| 141 |
+
PASSAGE_MAX_CHARS = int(os.environ.get("AEL_RERANK_PASSAGE_CHARS", "1200"))
|
| 142 |
+
|
| 143 |
# Recent (query, scores) for threshold tuning. Bounded so a long-lived Space
|
| 144 |
# cannot grow without limit.
|
| 145 |
RECENT = deque(maxlen=int(os.environ.get("AEL_RERANK_LOG", "200")))
|
|
|
|
| 294 |
|
| 295 |
def _passage(result):
|
| 296 |
"""The text the model judges. Title carries most of the topical signal;
|
| 297 |
+
snippet is what we have of the page without fetching it.
|
| 298 |
+
|
| 299 |
+
Truncated to PASSAGE_MAX_CHARS. Two reasons, and only the first was
|
| 300 |
+
obvious: a snippet plus title can run past the model's context, and the
|
| 301 |
+
model raises rather than truncating — which surfaced as a per-query
|
| 302 |
+
`ok: false` with no reason, reproducible only for certain queries. The
|
| 303 |
+
second is latency: a document costs ~150ms on the Space's CPU and most of
|
| 304 |
+
that is attention over the input, so the cap pays for itself twice.
|
| 305 |
+
"""
|
| 306 |
title = str(result.get("title") or "").strip()
|
| 307 |
snippet = str(result.get("snippet") or "").strip()
|
| 308 |
+
text = (title + "\n" + snippet).strip()
|
| 309 |
+
if len(text) > PASSAGE_MAX_CHARS:
|
| 310 |
+
# Title first: it survives the cut, and it is the part carrying the
|
| 311 |
+
# topical signal.
|
| 312 |
+
text = title[:PASSAGE_MAX_CHARS] + "\n" + snippet[: max(0, PASSAGE_MAX_CHARS - len(title))]
|
| 313 |
+
return text
|
| 314 |
|
| 315 |
|
| 316 |
def _stamp(out):
|
|
|
|
| 380 |
p = float(res["answers"]["relevant"]["noul"])
|
| 381 |
except Exception as e: # noqa: BLE001
|
| 382 |
print(f"[rerank] score failed for result {i}: {type(e).__name__}: {e}", flush=True)
|
| 383 |
+
# Fail open, whole set untouched — and say why. A scoring failure
|
| 384 |
+
# used to be the same three words as a reranker that was not
|
| 385 |
+
# installed, which is how a per-query failure hid for a day.
|
| 386 |
+
out["results"] = results
|
| 387 |
+
out["no_opinion"] = f"scoring failed: {type(e).__name__}: {e}"[:300]
|
| 388 |
+
return out
|
| 389 |
scored.append((p, i, r))
|
| 390 |
judged += 1
|
| 391 |
|
|
|
|
| 400 |
out["judged"] = judged
|
| 401 |
|
| 402 |
if not any(p is not None for p, _, _ in scored):
|
| 403 |
+
# Every result was textless, so the model was never asked anything.
|
| 404 |
+
# That is a different thing from the model declining, and it is the
|
| 405 |
+
# kind of difference that is invisible until it is named.
|
| 406 |
+
out["no_opinion"] = "no result carried any text to judge"
|
| 407 |
+
return out
|
| 408 |
|
| 409 |
# Highest score first. Unscored results keep their relative position at the
|
| 410 |
# end rather than being dropped: we have no opinion about them.
|