Download pfbmax/tournament.py from rasynai/rasyn-iris: direct link, hf CLI and curl.
- Browser
- Download file 9.09 kB
-
https://huggingface.co/rasynai/rasyn-iris/resolve/main/pfbmax/tournament.py
- Command line
-
hf download hf://rasynai/rasyn-iris/pfbmax/tournament.py
-
curl -L -o tournament.py https://huggingface.co/rasynai/rasyn-iris/resolve/main/pfbmax/tournament.py
9.09 kB
| """Listwise tournament over the judge's contested band. | |
| Why this exists, in numbers: the pointwise judge strands 458/910 gold-perfect | |
| papers at scores 0.3-0.6 (only 35 clear the >0.99 gate), yet blind A/B tests | |
| prove the distinction IS visible in the text: gold vs same-score non-gold is | |
| called correctly 71.5% of the time by a strong reasoning model at high | |
| effort and 72.0% by gpt-5-mini (low), versus 64.7% for gpt-4o-mini. | |
| Absolute "is it Perfect?" scoring throws that comparative signal away; | |
| ordering papers directly recovers it. RankGPT-style sliding windows, Borda | |
| aggregation, and the pointwise score kept as a weak prior. | |
| Scope guard: only the CONTESTED ZONE is reordered: papers below the >0.99 | |
| gate, in score-sorted order, up to ZONE_MAX of them. Gate-passers stay | |
| locked on top (they are 63% precise and already sorted), and the tail below | |
| the zone is untouched, so a bad window can cost a few ranks, never a gold's | |
| window membership from above. | |
| Cost: ~2 passes x zone/stride windows x ~1.7k tokens with gpt-5-mini(low) | |
| ~= $0.02-0.05/query. | |
| Env: PFBMAX_TOURN=1 enables (router wiring), PFBMAX_TOURN_MODEL, | |
| PFBMAX_TOURN_ZONE (default 150), PFBMAX_TOURN_WIN (12), | |
| PFBMAX_TOURN_STRIDE (6), PFBMAX_TOURN_PASSES (2). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import re | |
| import urllib.request | |
| from concurrent.futures import ThreadPoolExecutor | |
| HERE = os.path.dirname(os.path.abspath(__file__)) | |
| ENDPOINT = "https://api.openai.com/v1/chat/completions" | |
| TIMEOUT_S = 180.0 | |
| ALIAS = "ABCDEFGHIJKLMNOP" | |
| SYS = ("You rank papers for a literature search. Given the request, its " | |
| "relevance criteria, and a lettered list of papers, order the papers " | |
| "from MOST to LEAST likely to satisfy EVERY criterion fully. Compare " | |
| "the papers against each other; do not grade them in isolation. " | |
| 'Reply JSON only: {"ranking": ["X", "Y", ...]} using each letter ' | |
| "exactly once.") | |
| def _key(): | |
| k = os.environ.get("OPENAI_API_KEY", "") | |
| if k: | |
| return k | |
| return open(os.path.join(HERE, ".openai_key"), | |
| encoding="utf-8").read().strip() | |
| def _envi(name, default): | |
| raw = (os.environ.get(name) or "").strip() | |
| try: | |
| return int(raw) if raw else default | |
| except ValueError: | |
| return default | |
| def _rank_window(query, criteria, docs, model, meter=None): | |
| """docs: [(cid, text)]; returns cids best-first, or None on failure.""" | |
| letters = ALIAS[:len(docs)] | |
| crit = "\n".join(f"- {c}" for c in criteria) | |
| papers = "\n\n".join(f"PAPER {l}:\n{t[:900]}" | |
| for l, (_c, t) in zip(letters, docs)) | |
| user = (f"request: {query}\ncriteria:\n{crit}\n\n{papers}\n\n" | |
| f"Order the {len(docs)} papers best-first.") | |
| payload = {"model": model, | |
| "messages": [{"role": "system", "content": SYS}, | |
| {"role": "user", "content": user}], | |
| "response_format": {"type": "json_object"}} | |
| if model.startswith(("gpt-5", "o")): | |
| payload["max_completion_tokens"] = 4096 | |
| payload["reasoning_effort"] = os.environ.get( | |
| "PFBMAX_TOURN_EFFORT", "low") | |
| else: | |
| payload["temperature"] = 0.0 | |
| payload["max_tokens"] = 120 | |
| req = urllib.request.Request( | |
| ENDPOINT, data=json.dumps(payload).encode(), | |
| headers={"Content-Type": "application/json", | |
| "Authorization": "Bearer " + _key()}) | |
| try: | |
| with urllib.request.urlopen(req, timeout=TIMEOUT_S) as r: | |
| obj = json.loads(r.read()) | |
| usage = obj.get("usage") or {} | |
| if meter is not None: | |
| try: | |
| meter.add(obj.get("model") or model, | |
| usage.get("prompt_tokens") or 0, | |
| usage.get("completion_tokens") or 0) | |
| except Exception: | |
| pass | |
| raw = json.loads(obj["choices"][0]["message"]["content"] or "{}") | |
| got = [str(x).strip().upper()[:1] for x in raw.get("ranking", [])] | |
| seen = [] | |
| for g in got: | |
| if g in letters and g not in seen: | |
| seen.append(g) | |
| for l in letters: # letters the model dropped keep order | |
| if l not in seen: | |
| seen.append(l) | |
| idx = {l: i for i, l in enumerate(letters)} | |
| return [docs[idx[l]][0] for l in seen] | |
| except Exception: | |
| return None | |
| def rerank_zone(query, criteria, ordered, scores, texts, | |
| model=None, meter=None, trace=None): | |
| """Reorder the contested zone of ``ordered`` (score-sorted cids). | |
| ordered: full head order AFTER pointwise sort (best first) | |
| scores: {cid: pointwise score} | |
| texts: {cid: doc text} | |
| Returns the full reordered list (locked top + tournament zone + tail). | |
| """ | |
| tr = trace if trace is not None else {} | |
| model = model or os.environ.get("PFBMAX_TOURN_MODEL", "gpt-5-mini") | |
| zone_max = _envi("PFBMAX_TOURN_ZONE", 150) | |
| win = _envi("PFBMAX_TOURN_WIN", 12) | |
| stride = _envi("PFBMAX_TOURN_STRIDE", 6) | |
| passes = _envi("PFBMAX_TOURN_PASSES", 2) | |
| locked = [c for c in ordered if (scores.get(c) or 0.0) > 0.99] | |
| rest = [c for c in ordered if (scores.get(c) or 0.0) <= 0.99] | |
| zone = [c for c in rest if texts.get(c)][:zone_max] | |
| tail = [c for c in rest if c not in set(zone)] | |
| if len(zone) < win: | |
| return ordered | |
| order = list(zone) | |
| base_rank = {c: i for i, c in enumerate(zone)} | |
| # Points POOL across passes: the offline sweep on dumped windows showed | |
| # pooled Borda (+cap 8) at +0.0151 vs +(-0.0105..-0.0001) for the | |
| # sequential per-pass reorder this loop originally applied. Pass 2 still | |
| # re-slides windows over the pass-1 order (new match-ups), but the final | |
| # order comes from ALL windows at once. | |
| pooled = {c: [] for c in zone} | |
| for _p in range(passes): | |
| windows = [] | |
| i = 0 | |
| while i < len(order): | |
| w = order[i:i + win] | |
| if len(w) >= 4: | |
| windows.append((i, w)) | |
| if i + win >= len(order): | |
| break | |
| i += stride | |
| results = {} | |
| with ThreadPoolExecutor(max_workers=_envi("PFBMAX_TOURN_WORKERS", 6)) as ex: | |
| futs = {ex.submit(_rank_window, query, criteria, | |
| [(c, texts[c]) for c in w], model, meter): (s, w) | |
| for s, w in windows} | |
| for f in futs: | |
| s, w = futs[f] | |
| got = f.result() | |
| if got: | |
| results[s] = got | |
| # accumulate Borda points into the cross-pass pool; the within-pass | |
| # reorder below only shapes the NEXT pass's window composition. | |
| for s, w in windows: | |
| got = results.get(s) | |
| if not got: | |
| continue | |
| for pos, c in enumerate(got): | |
| pooled[c].append(1.0 - pos / max(1, len(got) - 1)) | |
| try: | |
| prior_w = float(os.environ.get("PFBMAX_TOURN_PRIOR") or 0.4) | |
| except ValueError: | |
| prior_w = 0.4 | |
| def key(c): | |
| p = (sum(pooled[c]) / len(pooled[c])) if pooled[c] else 0.5 | |
| prior = 1.0 - base_rank[c] / max(1, len(zone) - 1) | |
| return -( (1 - prior_w) * p + prior_w * prior ) | |
| order = sorted(order, key=key) | |
| # dump raw window outputs: aggregation becomes a $0 offline sweep | |
| dump = os.environ.get("PFBMAX_TOURN_DUMP", "").strip() | |
| if dump: | |
| try: | |
| with open(dump, "a", encoding="utf-8") as fh: | |
| fh.write(json.dumps({ | |
| "query": query, "pass": _p, "zone": zone, | |
| "windows": [{"start": s, "in": w, | |
| "out": results.get(s)} | |
| for s, w in windows], | |
| }, ensure_ascii=False) + "\n") | |
| except Exception: | |
| pass | |
| # DEMOTION CAP. Pilot 1 (no cap): net -0.0001 -- one +0.094 rescue but | |
| # two catastrophes (-0.035, -0.071 to zero) where the tournament dragged | |
| # base-trusted papers out of the scored window. Rescues need unlimited | |
| # ascent; base papers need bounded descent: a paper may fall at most | |
| # DEMOTE_CAP ranks below where the base order had it. | |
| cap = _envi("PFBMAX_TOURN_DEMOTE_CAP", 8) | |
| if cap > 0: | |
| final = list(order) | |
| for c in zone: | |
| want = base_rank[c] + cap | |
| cur = final.index(c) | |
| if cur > want: | |
| final.remove(c) | |
| final.insert(want, c) | |
| order = final | |
| tr["tournament"] = {"model": model, "zone": len(zone), | |
| "windows": len(windows), "passes": passes, | |
| "failed": sum(1 for s, _w in windows | |
| if s not in results)} | |
| return locked + order + tail | |