ArthurZ's picture
ArthurZ HF Staff
Add triage pipeline code
cc857c8 verified
Raw History Blame Contribute Delete
3.5 kB
import json, re, subprocess, sys
from collections import Counter
from datetime import datetime, timezone
prs = json.load(open(sys.argv[1]))
closed = set(int(l.split()[0]) for l in open("close_fail.log")) if False else set()
done = set(int(m) for m in re.findall(r"close (\d+)", open("cmds.sh").read()))
main = set(subprocess.run(["git", "-C", "/Users/arthurzucker/Work/transformers", "ls-tree", "-r", "--name-only", "origin/main"],
capture_output=True, text=True).stdout.split())
NOW = datetime(2026, 10, 20, tzinfo=timezone.utc)
age = lambda s: (NOW - datetime.fromisoformat(s.replace("Z", "+00:00"))).days
PHIL = [(re.compile(r"modeling_(tf|flax)_|_tf_utils|_flax_utils|/tf_|/flax_"), "TF/Flax code (removed in v5)"),
(re.compile(r"^examples/research_projects/|^examples/legacy/|^templates/|^examples/(tensorflow|flax)/"), "removed/legacy examples or templates"),
(re.compile(r"^src/transformers/models/deprecated/"), "deprecated model"),
(re.compile(r"^src/transformers/(benchmark|onnx|commands/(pt_to_tf|convert))/"), "removed subsystem (benchmark/onnx/pt_to_tf)")]
out = []
for p in prs:
if p["number"] in done: continue
files = [f["path"] for f in p["files"]["nodes"]]
last_commit = age(p["commits"]["nodes"][0]["commit"]["committedDate"]) if p["commits"]["nodes"] else 9999
upd = age(p["updatedAt"])
# new modeling files added count as present only if PR adds them; missing = existing-path edits that vanished
missing = [f for f in files if f not in main and not re.search(r"models/[^/]+/(modeling|modular|configuration|processing|image_processing|tokenization|convert)_|^tests/models/|^docs/", f)]
reasons, cats = [], set()
if p["baseRefName"] != "main":
cats.add("old_branch"); reasons.append(f"base branch is `{p['baseRefName']}`")
if files and len(missing) / len(files) >= 0.5:
cats.add("old_branch"); reasons.append(f"{len(missing)}/{len(files)} touched files no longer exist on main")
for rx, why in PHIL:
hits = [f for f in files if rx.search(f)]
if hits: cats.add("philosophy"); reasons.append(f"{why}: {hits[0]}" + (f" (+{len(hits)-1})" if len(hits) > 1 else ""))
if last_commit > 365 and upd > 90:
cats.add("outdated"); reasons.append(f"last commit {last_commit}d ago, last activity {upd}d ago")
elif p["mergeable"] == "CONFLICTING" and last_commit > 180 and upd > 60:
cats.add("outdated"); reasons.append(f"merge conflicts, last commit {last_commit}d ago")
if cats:
out.append({"number": p["number"], "title": p["title"], "author": (p["author"] or {}).get("login"),
"assoc": p["authorAssociation"], "draft": p["isDraft"], "base": p["baseRefName"],
"mergeable": p["mergeable"], "review": p["reviewDecision"], "created": p["createdAt"][:10],
"last_commit_days": last_commit, "updated_days": upd, "labels": [l["name"] for l in p["labels"]["nodes"]],
"files": files[:15], "nfiles": p["files"]["totalCount"], "cats": sorted(cats), "signals": reasons,
"last_comments": [{"by": (c["author"] or {}).get("login"), "assoc": c["authorAssociation"], "at": c["createdAt"][:10], "text": c["body"][:300]} for c in p["comments"]["nodes"]]})
json.dump(out, open(sys.argv[2], "w"), indent=1)
print(len(prs), "open PRs ->", len(out), "flagged;", Counter(c for o in out for c in o["cats"]))
print("assoc:", Counter(o["assoc"] for o in out))