anshy commited on
Commit
7f390fb
·
verified ·
1 Parent(s): 554a6d8

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ multilingual/train.txt filter=lfs diff=lfs merge=lfs -text
37
+ multilingual/val.txt filter=lfs diff=lfs merge=lfs -text
38
+ quality/train.txt filter=lfs diff=lfs merge=lfs -text
39
+ quality/val.txt filter=lfs diff=lfs merge=lfs -text
40
+ research_novelty/train.txt filter=lfs diff=lfs merge=lfs -text
41
+ research_novelty/val.txt filter=lfs diff=lfs merge=lfs -text
42
+ tags/train.txt filter=lfs diff=lfs merge=lfs -text
43
+ tags/val.txt filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PR quality/multilingual/tags fastText classifiers
2
+
3
+ Four fastText classifiers that score or tag a GitHub PR from its diff + touched-file
4
+ content, distilled from Jev (`typesafe/jev-1.13`) annotations on mined GitHub Archive
5
+ PRs. Each subfolder is self-contained: `model.bin` (weights), `train.txt`/`val.txt`
6
+ (the exact fastText training data), `train.py` (the exact training call — rerun in
7
+ place to reproduce `model.bin` from `train.txt`), and `infer.py` (standalone scoring
8
+ script — fetches a live PR via the GitHub API and classifies it, no dependency on the
9
+ original mining pipeline).
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ pip install fasttext==0.9.3 "numpy<2" requests # numpy<2 is required -- see note below
15
+ export GITHUB_PAT=... # any GitHub token (read-only is fine)
16
+ python quality/infer.py https://github.com/owner/repo/pull/123
17
+ python multilingual/infer.py https://github.com/owner/repo/pull/123
18
+ python research_novelty/infer.py https://github.com/owner/repo/pull/123
19
+ python tags/infer.py https://github.com/owner/repo/pull/123 # multi-label
20
+ python tags/infer.py --threshold 0.3 https://github.com/owner/repo/pull/123
21
+ ```
22
+
23
+ To retrain a model from its own `train.txt` in place: `cd <subfolder> && python train.py`.
24
+
25
+ `quality`/`multilingual`/`research_novelty` each print a `soft` score (probability-
26
+ weighted expected value, the better-correlating of the two — use this) and a `hard`
27
+ score (argmax level), both on a 0-10 scale. `tags` instead prints every tag (of 424)
28
+ whose predicted probability clears `--threshold` (default 0.5), sorted by confidence —
29
+ see its own section below for why it's one multi-label model, not 424 separate ones.
30
+
31
+ **Note on fastText + NumPy 2.x:** the prebuilt `fasttext-wheel` can silently corrupt
32
+ batch-`predict()` probabilities under NumPy 2.x, and even a from-source build's Python
33
+ wrapper crashes outright on `np.array(..., copy=False)`. Pin `numpy<2`.
34
+
35
+ ## `quality/` — Jev's strict 0-10 quality rubric
36
+
37
+ Rates the PR's diff on a strict, sensibility-gated 0-10 scale: 0-5 is gated on whether
38
+ the codebase/diff is coherent and credible (a simple-but-clean change tops out at 5); 6+
39
+ requires genuine engineering or conceptual complexity on top of that. See
40
+ `annotate_pr_quality.py`'s `QUALITY_LEVELS` for the full rubric text.
41
+
42
+ - **Hyperparameters**: dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3, bucket=2,000,000
43
+ - **Train**: 254,143 PRs (`train.txt`)
44
+ - **Val**: 28,238 PRs, 1,766 at level≥6 (`val.txt`)
45
+
46
+ **This is the plain dim64/epoch10 baseline** — every variant tried (2-4x capacity, class-
47
+ rebalanced train sets, a ~1.55x-larger combined corpus with a separately-mined
48
+ higher-credibility PR pool) came out flat or slightly worse on a held-out 10,000-PR
49
+ tracking set (685 at quality≥6). More data/capacity does not fix this classifier;
50
+ treat it as a known ceiling, not a bug to chase.
51
+
52
+ **Metrics** (10,000-PR tracking holdout, rule = `quality >= 6`):
53
+
54
+ | metric | value |
55
+ |---|---|
56
+ | rule accuracy | 93.73% |
57
+ | precision | 74.17% |
58
+ | **recall** | **12.99%** |
59
+ | exact-level accuracy | 78.01% |
60
+ | MAE | 0.531 |
61
+ | soft Pearson / Spearman | 0.7252 / 0.6339 |
62
+
63
+ **Read this before using it as a filter**: it misses ~87% of genuinely good PRs
64
+ (quality≥6) to keep false positives to 0.33% of negatives, and its own predictions never
65
+ exceed ~6.8-7 on any PR we've tested — it is a high-precision, very-low-recall filter,
66
+ not a general-purpose quality scorer. It also gave near-identical predictions (4.4-5.3)
67
+ to real, high-quality ML/research PRs (e.g. lucidrains' `x-transformers`, `imagen-pytorch`)
68
+ as to trivial personal-repo PRs — it is not sensitive to genuine technical depth. Lowering
69
+ the decision threshold to `>=5` makes this *worse*, not better (accuracy drops to 37%,
70
+ since ~80% of real PRs are already `>=5` and the model's own predicted mass sits below
71
+ that line).
72
+
73
+ ## `multilingual/` — non-English comment/string content
74
+
75
+ Purely descriptive 0-10 scale: how much non-English natural-language text (comments,
76
+ docstrings, string literals) appears in the diff and touched files, independent of the
77
+ programming language itself. See `annotate_pr_quality.py`'s `MULTILINGUAL_LEVELS`.
78
+
79
+ - **Hyperparameters**: dim=128, epoch=25, lr=0.3, wordNgrams=2, minCount=3, bucket=2,000,000
80
+ (best of a capacity sweep vs dim64/epoch10 baseline and dim256/epoch25 — dim128 won on
81
+ every axis, dim256 added nothing further for 2x the model size)
82
+ - **Train**: 254,143 PRs (`train.txt`)
83
+ - **Val**: 28,238 PRs (`val.txt`)
84
+
85
+ **Metrics** (100-PR live holdout, 79 true positives, rule = `multilingual <= 1`):
86
+
87
+ | metric | value |
88
+ |---|---|
89
+ | rule accuracy | 96.00% |
90
+ | precision | 96.30% |
91
+ | **recall** | **98.73%** |
92
+ | MAE | 0.586 |
93
+ | Pearson / Spearman | 0.906 / 0.721 |
94
+
95
+ This one is genuinely reliable as a pre-filter — high recall *and* precision. If you only
96
+ trust one of these two classifiers for a filtering decision, trust this one.
97
+
98
+ ## `research_novelty/` — research/IP content (⚠️ still recall≈0 at the `>=6` rule)
99
+
100
+ 0-10 scale rating the extent to which the PR implements technical algorithms, ideas from
101
+ research papers, or original design/IP, as opposed to routine engineering — independent
102
+ of how difficult or high-quality the engineering itself is. See `annotate_pr_quality.py`'s
103
+ `RESEARCH_NOVELTY_LEVELS`.
104
+
105
+ - **Hyperparameters**: dim=64, epoch=10 (same baseline as `quality`/`multilingual`)
106
+ - **Train**: 96,634 PRs, 297 at level≥6 (`train.txt`)
107
+ - **Val**: 10,772 PRs, 35 at level≥6 (`val.txt`)
108
+ - **Same train/val PR ids as `tags/`** (both come from the same annotation batch; see
109
+ Provenance) — comparable across the two.
110
+
111
+ **Updated 2026-09-30**: retrained on a new 107,406-PR annotation batch (up from the
112
+ original 39,081-PR/98-positive pass), joined back to `quality`/`multilingual`'s existing
113
+ `text` field so the same 254K+-PR pool's diff/file content is reused rather than
114
+ re-mined. Labels use the field's own fixed 0-10 scale (int truncation), same as
115
+ `quality`/`multilingual` — not sample quantiles.
116
+
117
+ **Metrics** (own val.txt, 10,772 PRs):
118
+
119
+ | metric | value |
120
+ |---|---|
121
+ | exact-level accuracy | 82.45% |
122
+ | within-1-level accuracy | 94.06% |
123
+ | MAE | 0.2745 |
124
+ | hard Pearson / Spearman | 0.5679 / 0.5069 |
125
+ | soft Pearson / Spearman | 0.6662 / 0.5470 |
126
+ | rule (`>=6`) accuracy | 99.68% |
127
+ | rule (`>=6`) precision | undefined (0 predicted positive) |
128
+ | **rule (`>=6`) recall** | **0.00%** (0/35) |
129
+
130
+ **Read this before using it as a filter**: correlation improved modestly over the old
131
+ diagnostic pass (soft Pearson 0.60→0.67) and the accuracy/MAE numbers look good, but that's
132
+ almost entirely the near-100%-easy "correctly predict low novelty" majority class — at the
133
+ actual decision boundary that matters (`>=6`), it now has enough val data to say
134
+ confidently (35 true positives, not 2) that it **misses every single one**, the same
135
+ recall-collapse pattern as `quality`. Its own predictions rarely if ever clear ~6-7 (see
136
+ the Megatron-LM spot-check below). Treat it as a continuous novelty *signal* (the soft
137
+ score correlates reasonably, 0.67) rather than a working `>=6` binary classifier.
138
+
139
+ **Spot-check**: `NVIDIA/Megatron-LM#7762` (an Engram/CUDA synchronization change) scores
140
+ soft=2.46, hard=1 — plausible as "some genuine ML-systems depth but not paper-derived,"
141
+ though still likely underrating it given the classifier's known insensitivity to real
142
+ technical depth (see `quality`'s note above; the same failure mode shows up here).
143
+
144
+ ## `tags/` — multi-label topic tagging (424 tags)
145
+
146
+ Multi-label classification over the topic vocabulary in `/ansh/harbor-tags.txt` (424
147
+ distinct tags after dedup — e.g. `python`, `machine-learning`, `web-security`,
148
+ `gpu`, `formal methods`) — any number of tags can apply to one PR, independently.
149
+ Each tag is an independent Jev "noul" (boolean-probability) claim ("this PR's diff or
150
+ touched files involve code/content related to topic X"), bundled into one Jev call per
151
+ PR alongside `research_novelty` (see `annotate_tags_research.py`).
152
+
153
+ **One joint multi-label model, not 424 separate binary classifiers.** fastText's native
154
+ multi-label mode (`loss="ova"`, one-vs-all) shares a single embedding/vocabulary across
155
+ all 424 tags and trains in one pass — dramatically cheaper than 424x the training
156
+ time/storage for what is fundamentally the same text signal, at the cost of slightly
157
+ less per-tag threshold flexibility (mitigated by tuning `--threshold` in `infer.py`
158
+ per use case; default 0.5).
159
+
160
+ - **Hyperparameters**: dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3,
161
+ bucket=2,000,000, **loss=ova**
162
+ - **Train**: 96,604 PRs (`train.txt`) / **Val**: 10,769 PRs (`val.txt`) — same PR ids as
163
+ `research_novelty/`'s updated split (both from the same annotation batch)
164
+ - 33 PRs with zero tags above threshold were dropped (fastText needs ≥1 label/line)
165
+
166
+ **Metrics** (10,769-PR val set, pooled across all 10,769×424 PR-tag pairs, threshold=0.5):
167
+
168
+ | metric | value |
169
+ |---|---|
170
+ | **precision** | **81.95%** |
171
+ | **recall** | **57.17%** |
172
+ | f1 | 67.35% |
173
+ | pooled Pearson r (vs. Jev's raw `tag_probs`) | **0.8055** |
174
+ | pooled Spearman ρ | **0.7145** |
175
+
176
+ Precision stays high across common tags (0.79-0.99) with recall degrading gracefully as
177
+ per-tag support drops — normal multi-label behavior, not a red flag. This is the
178
+ best-behaved of the four classifiers relative to its task difficulty (a 424-way
179
+ multi-label problem vs. a single 0-10 scale).
180
+
181
+ **Known data-hygiene issue**: `harbor-tags.txt` has a couple of near-duplicate tag pairs
182
+ that survived as distinct labels (`data processing`/`data-processing`, `data
183
+ engineering`/`data_engineering` — the latter pair also collided post-slugification and
184
+ one was suffixed `_dup` in `tag_vocab.json` to disambiguate). Worth deduping the source
185
+ file; not fixed here since it would invalidate the current label vocabulary/model.
186
+
187
+ **Spot-check**: `vercel/next.js#99499` (a tag-revalidation fix) → 30 tags incl.
188
+ `typescript`, `react`, `nextjs`, `frontend`, `routing`, `concurrency`, `web-server`.
189
+ `NVIDIA/Megatron-LM#7762` → 36 tags incl. `machine-learning`, `pytorch`, `cuda`, `gpu`,
190
+ `distributed-systems`, `performance-optimization`, `deeply-technical-ip`. Both match
191
+ what a human skim of the PRs would tag.
192
+
193
+ ## Provenance
194
+
195
+ - Distilled from `typesafe/jev-1.13` (via OpenRouter) on GitHub Archive-mined PRs.
196
+ - Full annotation/training pipeline: `/ansh/prism-datagen/annotate_pr_quality.py`
197
+ (rubric + Jev calls, incl. `HARBOR_TAGS`/`TAG_CLAIMS`), `/ansh/fasttext-stuff/run.py`/
198
+ `train_sweep.py` (quality/multilingual training — each subfolder's `train.py` is the
199
+ relevant one of these, trimmed to a single model), `/ansh/fasttext-stuff/evaluate.py`/
200
+ `evaluate_sweep.py`/`compare_pre_post.py` (eval).
201
+ - `quality`'s known ceiling was diagnosed, not guessed: a dim/epoch capacity sweep
202
+ (64→128→256), a class-rebalancing ablation (10%/15% minority oversampling), and a
203
+ ~1.55x larger combined training corpus (adding a separately GitHub-credibility-filtered
204
+ PR pool skewed toward higher quality) were all tried and none moved recall past ~14%.
205
+ - `research_novelty`/`tags` come from a separate, lighter-weight annotation pass:
206
+ `/ansh/prism-datagen/annotate_tags_research.py` re-streams the same S3 source
207
+ `quality`/`multilingual` were mined from, filtered to PR ids already in that
208
+ 254K+-PR corpus, and asks Jev only for `research_novelty` + the 424 tag claims
209
+ (bundled into one call per PR, not 424 separate calls) — reusing that corpus's
210
+ existing `text` field rather than re-annotating everything from scratch. Dataset
211
+ build: `/ansh/fasttext-stuff/build_research_novelty_v2_dataset.py` and
212
+ `build_tags_dataset.py` (both replicate `quality`/`multilingual`'s exact train/val
213
+ split — same seed, same 10%, same PR ids — for cross-classifier comparability).
214
+ Training/eval: each subfolder's own `train.py`/`evaluate.py`.
multilingual/infer.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Standalone inference: fetch a live GitHub PR and score it with this multilingual
2
+ classifier (0-10: how much non-English comment/doc/string text appears in the diff and
3
+ touched files -- purely descriptive, not a quality judgment). Self-contained -- only
4
+ needs `requests` and `fasttext` installed (see /ansh/fasttext-stuff/requirements.txt:
5
+ fasttext==0.9.3, numpy<2, scipy<1.14 if you also want the correlation-metric scripts;
6
+ numpy<2 alone is required for fasttext's batch predict() to work at all).
7
+
8
+ Usage:
9
+ export GITHUB_PAT=... # a GitHub personal access token (read-only is fine)
10
+ python infer.py https://github.com/owner/repo/pull/123 [more PR urls...]
11
+
12
+ The text fed to the model is built EXACTLY the way training data was: the same
13
+ render_document() + fasttext_preprocess() pipeline as annotate_pr_quality.py, so a
14
+ score here is directly comparable to what's in train.txt/val.txt.
15
+ """
16
+ import base64
17
+ import os
18
+ import re
19
+ import sys
20
+
21
+ import fasttext
22
+ import requests
23
+
24
+ HERE = os.path.dirname(os.path.abspath(__file__))
25
+ MODEL_PATH = os.path.join(HERE, "model.bin")
26
+
27
+ MAX_FILES = 10
28
+ MAX_FILE_CHARS = 4_000
29
+ MAX_DIFF_CHARS = 12_000
30
+ MAX_FASTTEXT_TOKENS = 4_096
31
+ NON_CODE_EXTS = {"json", "xml"}
32
+ TOKEN_PATTERN = re.compile(r"\w+(?:['’]\w+)*|[^\w\s]", re.UNICODE)
33
+
34
+ TOKEN = os.environ["GITHUB_PAT"]
35
+ HEADERS = {"Authorization": f"token {TOKEN}", "Accept": "application/vnd.github+json"}
36
+
37
+
38
+ def truncate(text, limit):
39
+ if text is None:
40
+ return None
41
+ if len(text) <= limit:
42
+ return text
43
+ return text[:limit] + f"\n...[truncated, {len(text) - limit} more chars]"
44
+
45
+
46
+ def fasttext_preprocess(text):
47
+ tokens = []
48
+ for match in TOKEN_PATTERN.finditer(text.casefold()):
49
+ if len(tokens) == MAX_FASTTEXT_TOKENS:
50
+ break
51
+ token = match.group()
52
+ if token.startswith("__label__"):
53
+ token = "literal_" + token
54
+ tokens.append(token)
55
+ return " ".join(tokens)
56
+
57
+
58
+ def render_document(state):
59
+ lines = [f"repo : {state['repo']['full_name']}"]
60
+ if state["repo"]["description"]:
61
+ lines.append(f"description : {state['repo']['description']}")
62
+ lines.append(f"stars : {state['repo']['stars']}")
63
+ if state["repo"]["license"]:
64
+ lines.append(f"license : {state['repo']['license']}")
65
+ if state["pull_request_title"]:
66
+ lines.append(f"title : {state['pull_request_title']}")
67
+ lines.append("touched files : " + ", ".join(state["all_touched_files"]))
68
+ for f in state["files"]:
69
+ lines.append(f"--- {f['path']} (pre-change) ---")
70
+ lines.append(f["pre_change_content"] or "[new file]")
71
+ lines.append("--- diff ---")
72
+ lines.append(state["diff"] or "")
73
+ return "\n".join(lines)
74
+
75
+
76
+ def get(url, headers=None, **kw):
77
+ resp = requests.get(url, headers=headers or HEADERS, timeout=20, **kw)
78
+ resp.raise_for_status()
79
+ return resp
80
+
81
+
82
+ def parse_pr_url(url):
83
+ m = re.search(r"github\.com/([^/]+)/([^/]+)/pull/(\d+)", url)
84
+ return m.group(1), m.group(2), int(m.group(3))
85
+
86
+
87
+ def fetch_state(pr_url):
88
+ owner, repo, number = parse_pr_url(pr_url)
89
+ repo_full = f"{owner}/{repo}"
90
+
91
+ repo_info = get(f"https://api.github.com/repos/{repo_full}").json()
92
+ pr_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}").json()
93
+ files_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}/files",
94
+ params={"per_page": 100}).json()
95
+ diff = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}",
96
+ headers={**HEADERS, "Accept": "application/vnd.github.v3.diff"}).text
97
+
98
+ base_sha = pr_info["base"]["sha"]
99
+ all_touched_files = [f["filename"] for f in files_info]
100
+
101
+ picked = []
102
+ for f in files_info:
103
+ path = f["filename"]
104
+ ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
105
+ if ext in NON_CODE_EXTS:
106
+ continue
107
+ content = None
108
+ if f["status"] != "added":
109
+ try:
110
+ c = get(f"https://api.github.com/repos/{repo_full}/contents/{path}",
111
+ params={"ref": base_sha}).json()
112
+ if c.get("encoding") == "base64":
113
+ content = base64.b64decode(c["content"]).decode("utf-8", errors="replace")
114
+ except requests.HTTPError:
115
+ content = None
116
+ picked.append({"path": path, "pre_change_content": truncate(content, MAX_FILE_CHARS)})
117
+ if len(picked) == MAX_FILES:
118
+ break
119
+
120
+ license_ = repo_info.get("license") or {}
121
+ return {
122
+ "repo": {
123
+ "full_name": repo_full,
124
+ "description": repo_info.get("description"),
125
+ "stars": repo_info.get("stargazers_count"),
126
+ "license": license_.get("spdx_id") or license_.get("name"),
127
+ },
128
+ "pull_request_title": pr_info.get("title"),
129
+ "all_touched_files": all_touched_files,
130
+ "files": picked,
131
+ "diff": truncate(diff, MAX_DIFF_CHARS),
132
+ }
133
+
134
+
135
+ def predict(model, text):
136
+ n_labels = len(model.get_labels())
137
+ labels, probs = model.predict(text, k=n_labels)
138
+ soft = sum(p * int(l.rsplit("_", 1)[1]) for l, p in zip(labels, probs))
139
+ hard = int(labels[0].rsplit("_", 1)[1])
140
+ return soft, hard
141
+
142
+
143
+ if __name__ == "__main__":
144
+ if len(sys.argv) < 2:
145
+ raise SystemExit("usage: python infer.py <github PR url> [more PR urls...]")
146
+
147
+ model = fasttext.load_model(MODEL_PATH)
148
+ for pr_url in sys.argv[1:]:
149
+ state = fetch_state(pr_url)
150
+ text = fasttext_preprocess(render_document(state))
151
+ soft, hard = predict(model, text)
152
+ print(f"{pr_url}")
153
+ print(f" repo: {state['repo']['full_name']} stars: {state['repo']['stars']}")
154
+ print(f" predicted multilingual score: soft={soft:.2f} hard_level={hard}\n")
multilingual/model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:13e27ed9bfe7f186579f95c6e58345e1a7f9061fc207b60f8e1b95617d6bd3bf
3
+ size 2545727308
multilingual/train.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exact training call used for this model. Run from inside this directory:
2
+ python train.py
3
+ Retrains model.bin from train.txt in place (dim=128/epoch=25 -- the winner of a capacity
4
+ sweep against dim=64/epoch=10 and dim=256/epoch=25; dim256 added nothing further, see
5
+ README.md)."""
6
+ import fasttext
7
+
8
+ HPARAMS = dict(dim=128, epoch=25, lr=0.3, wordNgrams=2, minCount=3, bucket=2_000_000)
9
+
10
+ model = fasttext.train_supervised(input="train.txt", **HPARAMS)
11
+ model.save_model("model.bin")
12
+ print("saved model.bin")
multilingual/train.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ec67e1e15a3844f1f914f55882fbc97024c51ed9a39a555d6901da13c069768
3
+ size 2802807438
multilingual/val.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b26041cdb01d61d1d3fc91bbbbecc8b43e3ecfb987055da93b8c4cda1f660061
3
+ size 309577373
quality/__pycache__/infer.cpython-312.pyc ADDED
Binary file (8.79 kB). View file
 
quality/infer.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Standalone inference: fetch a live GitHub PR and score it with this quality
2
+ classifier. Self-contained -- only needs `requests` and `fasttext` installed
3
+ (see /ansh/fasttext-stuff/requirements.txt: fasttext==0.9.3, numpy<2, scipy<1.14
4
+ if you also want the correlation-metric scripts; numpy<2 alone is required for
5
+ fasttext's batch predict() to work at all).
6
+
7
+ Usage:
8
+ export GITHUB_PAT=... # a GitHub personal access token (read-only is fine)
9
+ python infer.py https://github.com/owner/repo/pull/123 [more PR urls...]
10
+
11
+ The text fed to the model is built EXACTLY the way training data was: the same
12
+ render_document() + fasttext_preprocess() pipeline as annotate_pr_quality.py, so a
13
+ score here is directly comparable to what's in train.txt/val.txt.
14
+ """
15
+ import base64
16
+ import os
17
+ import re
18
+ import sys
19
+
20
+ import fasttext
21
+ import requests
22
+
23
+ HERE = os.path.dirname(os.path.abspath(__file__))
24
+ MODEL_PATH = os.path.join(HERE, "model.bin")
25
+
26
+ MAX_FILES = 10
27
+ MAX_FILE_CHARS = 4_000
28
+ MAX_DIFF_CHARS = 12_000
29
+ MAX_FASTTEXT_TOKENS = 4_096
30
+ NON_CODE_EXTS = {"json", "xml"}
31
+ TOKEN_PATTERN = re.compile(r"\w+(?:['’]\w+)*|[^\w\s]", re.UNICODE)
32
+
33
+ TOKEN = os.environ["GITHUB_PAT"]
34
+ HEADERS = {"Authorization": f"token {TOKEN}", "Accept": "application/vnd.github+json"}
35
+
36
+
37
+ def truncate(text, limit):
38
+ if text is None:
39
+ return None
40
+ if len(text) <= limit:
41
+ return text
42
+ return text[:limit] + f"\n...[truncated, {len(text) - limit} more chars]"
43
+
44
+
45
+ def fasttext_preprocess(text):
46
+ tokens = []
47
+ for match in TOKEN_PATTERN.finditer(text.casefold()):
48
+ if len(tokens) == MAX_FASTTEXT_TOKENS:
49
+ break
50
+ token = match.group()
51
+ if token.startswith("__label__"):
52
+ token = "literal_" + token
53
+ tokens.append(token)
54
+ return " ".join(tokens)
55
+
56
+
57
+ def render_document(state):
58
+ lines = [f"repo : {state['repo']['full_name']}"]
59
+ if state["repo"]["description"]:
60
+ lines.append(f"description : {state['repo']['description']}")
61
+ lines.append(f"stars : {state['repo']['stars']}")
62
+ if state["repo"]["license"]:
63
+ lines.append(f"license : {state['repo']['license']}")
64
+ if state["pull_request_title"]:
65
+ lines.append(f"title : {state['pull_request_title']}")
66
+ lines.append("touched files : " + ", ".join(state["all_touched_files"]))
67
+ for f in state["files"]:
68
+ lines.append(f"--- {f['path']} (pre-change) ---")
69
+ lines.append(f["pre_change_content"] or "[new file]")
70
+ lines.append("--- diff ---")
71
+ lines.append(state["diff"] or "")
72
+ return "\n".join(lines)
73
+
74
+
75
+ def get(url, headers=None, **kw):
76
+ resp = requests.get(url, headers=headers or HEADERS, timeout=20, **kw)
77
+ resp.raise_for_status()
78
+ return resp
79
+
80
+
81
+ def parse_pr_url(url):
82
+ m = re.search(r"github\.com/([^/]+)/([^/]+)/pull/(\d+)", url)
83
+ return m.group(1), m.group(2), int(m.group(3))
84
+
85
+
86
+ def fetch_state(pr_url):
87
+ owner, repo, number = parse_pr_url(pr_url)
88
+ repo_full = f"{owner}/{repo}"
89
+
90
+ repo_info = get(f"https://api.github.com/repos/{repo_full}").json()
91
+ pr_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}").json()
92
+ files_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}/files",
93
+ params={"per_page": 100}).json()
94
+ diff = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}",
95
+ headers={**HEADERS, "Accept": "application/vnd.github.v3.diff"}).text
96
+
97
+ base_sha = pr_info["base"]["sha"]
98
+ all_touched_files = [f["filename"] for f in files_info]
99
+
100
+ picked = []
101
+ for f in files_info:
102
+ path = f["filename"]
103
+ ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
104
+ if ext in NON_CODE_EXTS:
105
+ continue
106
+ content = None
107
+ if f["status"] != "added":
108
+ try:
109
+ c = get(f"https://api.github.com/repos/{repo_full}/contents/{path}",
110
+ params={"ref": base_sha}).json()
111
+ if c.get("encoding") == "base64":
112
+ content = base64.b64decode(c["content"]).decode("utf-8", errors="replace")
113
+ except requests.HTTPError:
114
+ content = None
115
+ picked.append({"path": path, "pre_change_content": truncate(content, MAX_FILE_CHARS)})
116
+ if len(picked) == MAX_FILES:
117
+ break
118
+
119
+ license_ = repo_info.get("license") or {}
120
+ return {
121
+ "repo": {
122
+ "full_name": repo_full,
123
+ "description": repo_info.get("description"),
124
+ "stars": repo_info.get("stargazers_count"),
125
+ "license": license_.get("spdx_id") or license_.get("name"),
126
+ },
127
+ "pull_request_title": pr_info.get("title"),
128
+ "all_touched_files": all_touched_files,
129
+ "files": picked,
130
+ "diff": truncate(diff, MAX_DIFF_CHARS),
131
+ }
132
+
133
+
134
+ def predict(model, text):
135
+ n_labels = len(model.get_labels())
136
+ labels, probs = model.predict(text, k=n_labels)
137
+ soft = sum(p * int(l.rsplit("_", 1)[1]) for l, p in zip(labels, probs))
138
+ hard = int(labels[0].rsplit("_", 1)[1])
139
+ return soft, hard
140
+
141
+
142
+ if __name__ == "__main__":
143
+ if len(sys.argv) < 2:
144
+ raise SystemExit("usage: python infer.py <github PR url> [more PR urls...]")
145
+
146
+ model = fasttext.load_model(MODEL_PATH)
147
+ for pr_url in sys.argv[1:]:
148
+ state = fetch_state(pr_url)
149
+ text = fasttext_preprocess(render_document(state))
150
+ soft, hard = predict(model, text)
151
+ print(f"{pr_url}")
152
+ print(f" repo: {state['repo']['full_name']} stars: {state['repo']['stars']}")
153
+ print(f" predicted score: soft={soft:.2f} hard_level={hard}\n")
quality/model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2037f469a40e1baada22d185d549a22f36ac8fce8f6a752e51e357b43e1279d0
3
+ size 1306555670
quality/train.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exact training call used for this model. Run from inside this directory:
2
+ python train.py
3
+ Retrains model.bin from train.txt in place (dim=64/epoch=10 baseline -- see README.md
4
+ for why higher capacity/rebalancing/more data were all tried and didn't help recall)."""
5
+ import fasttext
6
+
7
+ HPARAMS = dict(dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3, bucket=2_000_000)
8
+
9
+ model = fasttext.train_supervised(input="train.txt", **HPARAMS)
10
+ model.save_model("model.bin")
11
+ print("saved model.bin")
quality/train.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:190483bf69835756e28e282a2e3de1152046199c00ee6821efb0eb649781b90a
3
+ size 2802807438
quality/val.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f244d446eff36e234c5c8a3ccfc3d2d3f22defdd52f86f7a84b0bc2861faf61c
3
+ size 309577373
research_novelty/bins.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "counts": [
3
+ 87150,
4
+ 11209,
5
+ 4607,
6
+ 2167,
7
+ 1298,
8
+ 645,
9
+ 271,
10
+ 57,
11
+ 2,
12
+ 0,
13
+ 0
14
+ ],
15
+ "bin_means": [
16
+ 0.23606701090074586,
17
+ 1.4044954946917656,
18
+ 2.425053179943564,
19
+ 3.457143516382095,
20
+ 4.4452234206471495,
21
+ 5.416713178294574,
22
+ 6.39959409594096,
23
+ 7.317368421052632,
24
+ 8.32,
25
+ null,
26
+ null
27
+ ]
28
+ }
research_novelty/infer.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Standalone inference: fetch a live GitHub PR and score it with this research/IP-
2
+ novelty classifier (0-10: the extent to which the PR implements technical algorithms,
3
+ paper-derived ideas, or original design, vs routine engineering -- see README.md for
4
+ the SEVERE data-scarcity caveat, this model should not be trusted much yet).
5
+ Self-contained -- only needs `requests` and `fasttext` installed (see
6
+ /ansh/fasttext-stuff/requirements.txt: fasttext==0.9.3, numpy<2, scipy<1.14 if you also
7
+ want the correlation-metric scripts; numpy<2 alone is required for fasttext's batch
8
+ predict() to work at all).
9
+
10
+ Usage:
11
+ export GITHUB_PAT=... # a GitHub personal access token (read-only is fine)
12
+ python infer.py https://github.com/owner/repo/pull/123 [more PR urls...]
13
+
14
+ The text fed to the model is built EXACTLY the way training data was: the same
15
+ render_document() + fasttext_preprocess() pipeline as annotate_pr_quality.py, so a
16
+ score here is directly comparable to what's in train.txt/val.txt.
17
+ """
18
+ import base64
19
+ import os
20
+ import re
21
+ import sys
22
+
23
+ import fasttext
24
+ import requests
25
+
26
+ HERE = os.path.dirname(os.path.abspath(__file__))
27
+ MODEL_PATH = os.path.join(HERE, "model.bin")
28
+
29
+ MAX_FILES = 10
30
+ MAX_FILE_CHARS = 4_000
31
+ MAX_DIFF_CHARS = 12_000
32
+ MAX_FASTTEXT_TOKENS = 4_096
33
+ NON_CODE_EXTS = {"json", "xml"}
34
+ TOKEN_PATTERN = re.compile(r"\w+(?:['’]\w+)*|[^\w\s]", re.UNICODE)
35
+
36
+ TOKEN = os.environ["GITHUB_PAT"]
37
+ HEADERS = {"Authorization": f"token {TOKEN}", "Accept": "application/vnd.github+json"}
38
+
39
+
40
+ def truncate(text, limit):
41
+ if text is None:
42
+ return None
43
+ if len(text) <= limit:
44
+ return text
45
+ return text[:limit] + f"\n...[truncated, {len(text) - limit} more chars]"
46
+
47
+
48
+ def fasttext_preprocess(text):
49
+ tokens = []
50
+ for match in TOKEN_PATTERN.finditer(text.casefold()):
51
+ if len(tokens) == MAX_FASTTEXT_TOKENS:
52
+ break
53
+ token = match.group()
54
+ if token.startswith("__label__"):
55
+ token = "literal_" + token
56
+ tokens.append(token)
57
+ return " ".join(tokens)
58
+
59
+
60
+ def render_document(state):
61
+ lines = [f"repo : {state['repo']['full_name']}"]
62
+ if state["repo"]["description"]:
63
+ lines.append(f"description : {state['repo']['description']}")
64
+ lines.append(f"stars : {state['repo']['stars']}")
65
+ if state["repo"]["license"]:
66
+ lines.append(f"license : {state['repo']['license']}")
67
+ if state["pull_request_title"]:
68
+ lines.append(f"title : {state['pull_request_title']}")
69
+ lines.append("touched files : " + ", ".join(state["all_touched_files"]))
70
+ for f in state["files"]:
71
+ lines.append(f"--- {f['path']} (pre-change) ---")
72
+ lines.append(f["pre_change_content"] or "[new file]")
73
+ lines.append("--- diff ---")
74
+ lines.append(state["diff"] or "")
75
+ return "\n".join(lines)
76
+
77
+
78
+ def get(url, headers=None, **kw):
79
+ resp = requests.get(url, headers=headers or HEADERS, timeout=20, **kw)
80
+ resp.raise_for_status()
81
+ return resp
82
+
83
+
84
+ def parse_pr_url(url):
85
+ m = re.search(r"github\.com/([^/]+)/([^/]+)/pull/(\d+)", url)
86
+ return m.group(1), m.group(2), int(m.group(3))
87
+
88
+
89
+ def fetch_state(pr_url):
90
+ owner, repo, number = parse_pr_url(pr_url)
91
+ repo_full = f"{owner}/{repo}"
92
+
93
+ repo_info = get(f"https://api.github.com/repos/{repo_full}").json()
94
+ pr_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}").json()
95
+ files_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}/files",
96
+ params={"per_page": 100}).json()
97
+ diff = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}",
98
+ headers={**HEADERS, "Accept": "application/vnd.github.v3.diff"}).text
99
+
100
+ base_sha = pr_info["base"]["sha"]
101
+ all_touched_files = [f["filename"] for f in files_info]
102
+
103
+ picked = []
104
+ for f in files_info:
105
+ path = f["filename"]
106
+ ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
107
+ if ext in NON_CODE_EXTS:
108
+ continue
109
+ content = None
110
+ if f["status"] != "added":
111
+ try:
112
+ c = get(f"https://api.github.com/repos/{repo_full}/contents/{path}",
113
+ params={"ref": base_sha}).json()
114
+ if c.get("encoding") == "base64":
115
+ content = base64.b64decode(c["content"]).decode("utf-8", errors="replace")
116
+ except requests.HTTPError:
117
+ content = None
118
+ picked.append({"path": path, "pre_change_content": truncate(content, MAX_FILE_CHARS)})
119
+ if len(picked) == MAX_FILES:
120
+ break
121
+
122
+ license_ = repo_info.get("license") or {}
123
+ return {
124
+ "repo": {
125
+ "full_name": repo_full,
126
+ "description": repo_info.get("description"),
127
+ "stars": repo_info.get("stargazers_count"),
128
+ "license": license_.get("spdx_id") or license_.get("name"),
129
+ },
130
+ "pull_request_title": pr_info.get("title"),
131
+ "all_touched_files": all_touched_files,
132
+ "files": picked,
133
+ "diff": truncate(diff, MAX_DIFF_CHARS),
134
+ }
135
+
136
+
137
+ def predict(model, text):
138
+ n_labels = len(model.get_labels())
139
+ labels, probs = model.predict(text, k=n_labels)
140
+ soft = sum(p * int(l.rsplit("_", 1)[1]) for l, p in zip(labels, probs))
141
+ hard = int(labels[0].rsplit("_", 1)[1])
142
+ return soft, hard
143
+
144
+
145
+ if __name__ == "__main__":
146
+ if len(sys.argv) < 2:
147
+ raise SystemExit("usage: python infer.py <github PR url> [more PR urls...]")
148
+
149
+ model = fasttext.load_model(MODEL_PATH)
150
+ for pr_url in sys.argv[1:]:
151
+ state = fetch_state(pr_url)
152
+ text = fasttext_preprocess(render_document(state))
153
+ soft, hard = predict(model, text)
154
+ print(f"{pr_url}")
155
+ print(f" repo: {state['repo']['full_name']} stars: {state['repo']['stars']}")
156
+ print(f" predicted research_novelty score: soft={soft:.2f} hard_level={hard}\n")
research_novelty/model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee241bd0d406b50af9dd3a2b51570e41a17a173065fd5fff3d5b04ddb9d238f5
3
+ size 881045797
research_novelty/train.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exact training call used for this model. Run from inside this directory:
2
+ python train.py
3
+ Same dim64/epoch10 baseline hyperparams as quality/. Trained on the expanded
4
+ 107,406-PR research_novelty annotation batch (330 at level>=6, up from the original
5
+ 39,081-PR/98-positive pass) -- see README.md for current stats and caveats."""
6
+ import fasttext
7
+
8
+ HPARAMS = dict(dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3, bucket=2_000_000)
9
+
10
+ model = fasttext.train_supervised(input="train.txt", **HPARAMS)
11
+ model.save_model("model.bin")
12
+ print("saved model.bin")
research_novelty/train.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec4190a66eb1dcf212ed1f015f0cac0464bca7667daa74655cb52a9fc0e58d24
3
+ size 1065555987
research_novelty/val.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a821e6e2daba9bc2575e2ccc984e159a08f858675864ae5bf64e73fdb077cb9f
3
+ size 119036497
tags/infer.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Standalone inference: fetch a live GitHub PR and tag it with this multi-label
2
+ classifier (424 topics from harbor-tags.txt -- any number can apply to one PR).
3
+ Self-contained -- only needs `requests` and `fasttext` installed (see
4
+ /ansh/fasttext-stuff/requirements.txt: fasttext==0.9.3, numpy<2, scipy<1.14 if you also
5
+ want the correlation-metric scripts; numpy<2 alone is required for fasttext's batch
6
+ predict() to work at all).
7
+
8
+ Usage:
9
+ export GITHUB_PAT=... # a GitHub personal access token (read-only is fine)
10
+ python infer.py https://github.com/owner/repo/pull/123 [more PR urls...]
11
+ python infer.py --threshold 0.3 https://github.com/owner/repo/pull/123
12
+
13
+ The text fed to the model is built EXACTLY the way training data was: the same
14
+ render_document() + fasttext_preprocess() pipeline as annotate_pr_quality.py, so a
15
+ prediction here is directly comparable to what's in train.txt/val.txt.
16
+ """
17
+ import base64
18
+ import json
19
+ import os
20
+ import re
21
+ import sys
22
+
23
+ import fasttext
24
+ import requests
25
+
26
+ HERE = os.path.dirname(os.path.abspath(__file__))
27
+ MODEL_PATH = os.path.join(HERE, "model.bin")
28
+ VOCAB_PATH = os.path.join(HERE, "tag_vocab.json")
29
+
30
+ MAX_FILES = 10
31
+ MAX_FILE_CHARS = 4_000
32
+ MAX_DIFF_CHARS = 12_000
33
+ MAX_FASTTEXT_TOKENS = 4_096
34
+ NON_CODE_EXTS = {"json", "xml"}
35
+ TOKEN_PATTERN = re.compile(r"\w+(?:['’]\w+)*|[^\w\s]", re.UNICODE)
36
+ DEFAULT_THRESHOLD = 0.5
37
+
38
+ TOKEN = os.environ["GITHUB_PAT"]
39
+ HEADERS = {"Authorization": f"token {TOKEN}", "Accept": "application/vnd.github+json"}
40
+
41
+
42
+ def truncate(text, limit):
43
+ if text is None:
44
+ return None
45
+ if len(text) <= limit:
46
+ return text
47
+ return text[:limit] + f"\n...[truncated, {len(text) - limit} more chars]"
48
+
49
+
50
+ def fasttext_preprocess(text):
51
+ tokens = []
52
+ for match in TOKEN_PATTERN.finditer(text.casefold()):
53
+ if len(tokens) == MAX_FASTTEXT_TOKENS:
54
+ break
55
+ token = match.group()
56
+ if token.startswith("__label__"):
57
+ token = "literal_" + token
58
+ tokens.append(token)
59
+ return " ".join(tokens)
60
+
61
+
62
+ def render_document(state):
63
+ lines = [f"repo : {state['repo']['full_name']}"]
64
+ if state["repo"]["description"]:
65
+ lines.append(f"description : {state['repo']['description']}")
66
+ lines.append(f"stars : {state['repo']['stars']}")
67
+ if state["repo"]["license"]:
68
+ lines.append(f"license : {state['repo']['license']}")
69
+ if state["pull_request_title"]:
70
+ lines.append(f"title : {state['pull_request_title']}")
71
+ lines.append("touched files : " + ", ".join(state["all_touched_files"]))
72
+ for f in state["files"]:
73
+ lines.append(f"--- {f['path']} (pre-change) ---")
74
+ lines.append(f["pre_change_content"] or "[new file]")
75
+ lines.append("--- diff ---")
76
+ lines.append(state["diff"] or "")
77
+ return "\n".join(lines)
78
+
79
+
80
+ def get(url, headers=None, **kw):
81
+ resp = requests.get(url, headers=headers or HEADERS, timeout=20, **kw)
82
+ resp.raise_for_status()
83
+ return resp
84
+
85
+
86
+ def parse_pr_url(url):
87
+ m = re.search(r"github\.com/([^/]+)/([^/]+)/pull/(\d+)", url)
88
+ return m.group(1), m.group(2), int(m.group(3))
89
+
90
+
91
+ def fetch_state(pr_url):
92
+ owner, repo, number = parse_pr_url(pr_url)
93
+ repo_full = f"{owner}/{repo}"
94
+
95
+ repo_info = get(f"https://api.github.com/repos/{repo_full}").json()
96
+ pr_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}").json()
97
+ files_info = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}/files",
98
+ params={"per_page": 100}).json()
99
+ diff = get(f"https://api.github.com/repos/{repo_full}/pulls/{number}",
100
+ headers={**HEADERS, "Accept": "application/vnd.github.v3.diff"}).text
101
+
102
+ base_sha = pr_info["base"]["sha"]
103
+ all_touched_files = [f["filename"] for f in files_info]
104
+
105
+ picked = []
106
+ for f in files_info:
107
+ path = f["filename"]
108
+ ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
109
+ if ext in NON_CODE_EXTS:
110
+ continue
111
+ content = None
112
+ if f["status"] != "added":
113
+ try:
114
+ c = get(f"https://api.github.com/repos/{repo_full}/contents/{path}",
115
+ params={"ref": base_sha}).json()
116
+ if c.get("encoding") == "base64":
117
+ content = base64.b64decode(c["content"]).decode("utf-8", errors="replace")
118
+ except requests.HTTPError:
119
+ content = None
120
+ picked.append({"path": path, "pre_change_content": truncate(content, MAX_FILE_CHARS)})
121
+ if len(picked) == MAX_FILES:
122
+ break
123
+
124
+ license_ = repo_info.get("license") or {}
125
+ return {
126
+ "repo": {
127
+ "full_name": repo_full,
128
+ "description": repo_info.get("description"),
129
+ "stars": repo_info.get("stargazers_count"),
130
+ "license": license_.get("spdx_id") or license_.get("name"),
131
+ },
132
+ "pull_request_title": pr_info.get("title"),
133
+ "all_touched_files": all_touched_files,
134
+ "files": picked,
135
+ "diff": truncate(diff, MAX_DIFF_CHARS),
136
+ }
137
+
138
+
139
+ def predict(model, tag_of_slug, text, threshold):
140
+ n_labels = len(model.get_labels())
141
+ labels, probs = model.predict(text, k=n_labels)
142
+ tagged = []
143
+ for label, prob in zip(labels, probs):
144
+ slug = label[len("__label__"):]
145
+ tag = tag_of_slug.get(slug, slug)
146
+ if prob >= threshold:
147
+ tagged.append((tag, float(prob)))
148
+ tagged.sort(key=lambda x: -x[1])
149
+ return tagged
150
+
151
+
152
+ if __name__ == "__main__":
153
+ args = sys.argv[1:]
154
+ threshold = DEFAULT_THRESHOLD
155
+ if args and args[0] == "--threshold":
156
+ threshold = float(args[1])
157
+ args = args[2:]
158
+ if not args:
159
+ raise SystemExit("usage: python infer.py [--threshold 0.5] <github PR url> [more PR urls...]")
160
+
161
+ vocab = json.loads(open(VOCAB_PATH).read())
162
+ tag_of_slug = {v: k for k, v in vocab["slug_of"].items()}
163
+
164
+ model = fasttext.load_model(MODEL_PATH)
165
+ for pr_url in args:
166
+ state = fetch_state(pr_url)
167
+ text = fasttext_preprocess(render_document(state))
168
+ tagged = predict(model, tag_of_slug, text, threshold)
169
+ print(f"{pr_url}")
170
+ print(f" repo: {state['repo']['full_name']} stars: {state['repo']['stars']}")
171
+ print(f" {len(tagged)} tags (threshold={threshold}):")
172
+ for tag, prob in tagged:
173
+ print(f" {tag}: {prob:.3f}")
174
+ print()
tags/model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9b67d07e2442cc73f3303fe182738500821fe30afe41019a1b430176f69e98b9
3
+ size 881160000
tags/tag_vocab.json ADDED
@@ -0,0 +1,855 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tags": [
3
+ "3d-modeling",
4
+ "AMT",
5
+ "Bayesian-inference",
6
+ "C",
7
+ "ERP",
8
+ "GSEA",
9
+ "MES",
10
+ "MusicXML",
11
+ "R",
12
+ "SATB",
13
+ "TMT",
14
+ "WMS",
15
+ "adaptive-rejection-sampling",
16
+ "administrative-data",
17
+ "advertising",
18
+ "after-sales",
19
+ "aggregation",
20
+ "algorithms",
21
+ "analysis",
22
+ "anonymization",
23
+ "api",
24
+ "applied-microeconomics",
25
+ "applied-statistics",
26
+ "asset-lineage",
27
+ "async",
28
+ "audio-transcription",
29
+ "audit-provenance",
30
+ "autokey-cipher",
31
+ "aviation",
32
+ "bash",
33
+ "batching",
34
+ "bayesian-network",
35
+ "bayesian-statistics",
36
+ "bcbs-279",
37
+ "benchmark-construction",
38
+ "bessel-functions",
39
+ "binlog",
40
+ "bioinformatics",
41
+ "biology",
42
+ "black-box-parity",
43
+ "bootkit",
44
+ "browser",
45
+ "build-tools",
46
+ "bun",
47
+ "c",
48
+ "c-programming",
49
+ "cad",
50
+ "caffe",
51
+ "calendar",
52
+ "cdc",
53
+ "checkpoint-conversion",
54
+ "cheminformatics",
55
+ "chemistry",
56
+ "chorale",
57
+ "cli",
58
+ "climate-science",
59
+ "cloning",
60
+ "cnn",
61
+ "code-vulnerability",
62
+ "coding",
63
+ "cold-start",
64
+ "combinatorial-optimization",
65
+ "combinatorics",
66
+ "common-weakness-enumeration",
67
+ "compaction",
68
+ "compilation",
69
+ "compiled-extensions",
70
+ "compilers",
71
+ "compliance",
72
+ "compression",
73
+ "computer-use",
74
+ "computer-vision",
75
+ "concurrency",
76
+ "config-generation",
77
+ "config-management",
78
+ "conflict-resolution",
79
+ "connected_components",
80
+ "constraint",
81
+ "constraint-satisfaction",
82
+ "contact-dynamics",
83
+ "coq",
84
+ "corewars",
85
+ "counterparty-credit-risk",
86
+ "cpp",
87
+ "cpu",
88
+ "crm",
89
+ "cross-referencing",
90
+ "cryptanalysis",
91
+ "cryptography",
92
+ "crystallography",
93
+ "csv",
94
+ "cuda",
95
+ "custom-protocol",
96
+ "cybersecurity",
97
+ "cybersecurity-vulnerability-resolution",
98
+ "dangerous-goods",
99
+ "data",
100
+ "data engineering",
101
+ "data processing",
102
+ "data-analysis",
103
+ "data-integration",
104
+ "data-linkage",
105
+ "data-pipeline",
106
+ "data-processing",
107
+ "data-science",
108
+ "data_engineering",
109
+ "database",
110
+ "databases",
111
+ "datasets",
112
+ "debian",
113
+ "debugging",
114
+ "decrypt",
115
+ "deduplication",
116
+ "deeply-technical-ip",
117
+ "deepseek-r1",
118
+ "dependency",
119
+ "devops",
120
+ "differential-expression",
121
+ "digital-design",
122
+ "dispatch",
123
+ "distributed-systems",
124
+ "distributed-training",
125
+ "distributed_systems",
126
+ "document-processing",
127
+ "document-verification",
128
+ "domain-reasoning",
129
+ "drawing",
130
+ "drift-detection",
131
+ "driver-hours",
132
+ "editable-layout",
133
+ "email-server",
134
+ "embedded-systems",
135
+ "encryption",
136
+ "ensembl-vep",
137
+ "entity-resolution",
138
+ "erp",
139
+ "etl",
140
+ "evaluation",
141
+ "event-time",
142
+ "excel",
143
+ "exploration-exploitation",
144
+ "fastapi",
145
+ "fdtd",
146
+ "feature-interpretation",
147
+ "file-operations",
148
+ "financial",
149
+ "firmware",
150
+ "fitting",
151
+ "flight-planning",
152
+ "forensics",
153
+ "formal methods",
154
+ "formal-mathematics",
155
+ "formal-verification",
156
+ "forms",
157
+ "fp8",
158
+ "fraud-detection",
159
+ "freecad",
160
+ "frontend",
161
+ "fuel-planning",
162
+ "gaming",
163
+ "gaussian-process",
164
+ "genomics",
165
+ "geometry",
166
+ "german-compliance",
167
+ "git",
168
+ "glycomics",
169
+ "gpu",
170
+ "gpu-parallelisms",
171
+ "grader",
172
+ "graph-algorithms",
173
+ "graphic-design",
174
+ "graphics",
175
+ "h100",
176
+ "hardware",
177
+ "hardware-kernels",
178
+ "hardware-modeling",
179
+ "harmony",
180
+ "heat-pumps",
181
+ "hgvs",
182
+ "histopathology",
183
+ "historical-linguistics",
184
+ "household-consumption",
185
+ "hpc",
186
+ "html-sanitization",
187
+ "huggingface",
188
+ "hydrogen-bonding",
189
+ "ics-parsing",
190
+ "image-processing",
191
+ "images",
192
+ "imo",
193
+ "impeller",
194
+ "incident-response",
195
+ "inference",
196
+ "information-retrieval",
197
+ "infrastructure-configuration",
198
+ "interpenetration",
199
+ "intrastat",
200
+ "inverse-design",
201
+ "inverse-problem",
202
+ "jax",
203
+ "kafka",
204
+ "kernel-optimization",
205
+ "knowledge-graph",
206
+ "knowledge-graphs",
207
+ "kuramoto-sivashinsky",
208
+ "large-scale-text-manipulation",
209
+ "latency",
210
+ "latex",
211
+ "layout-analysis",
212
+ "layout-reconstruction",
213
+ "layout-shift",
214
+ "lean4",
215
+ "legacy-gui",
216
+ "legacy-modernization",
217
+ "legacy-software",
218
+ "linux",
219
+ "liquid_scintillation_counting",
220
+ "list-decoding",
221
+ "litdata",
222
+ "live-surgery",
223
+ "live-system",
224
+ "llm",
225
+ "llm-eval",
226
+ "log-analysis",
227
+ "logistics",
228
+ "lsh",
229
+ "lsm",
230
+ "machine-learning",
231
+ "mailing-list",
232
+ "manufacturing",
233
+ "mass-spectrometry",
234
+ "mathematics",
235
+ "mcmc",
236
+ "mechanical-engineering",
237
+ "medical-billing",
238
+ "meep",
239
+ "megatron",
240
+ "memory-management",
241
+ "meter-data",
242
+ "migration",
243
+ "minhash",
244
+ "mixture-of-experts",
245
+ "ml-monitoring",
246
+ "ml-platform",
247
+ "mlops",
248
+ "model-risk",
249
+ "model-training",
250
+ "mteb",
251
+ "mujoco",
252
+ "multi-container",
253
+ "multi-modal",
254
+ "multi-service",
255
+ "music",
256
+ "mvcc",
257
+ "mysql",
258
+ "network",
259
+ "network-analysis",
260
+ "networking",
261
+ "next.js",
262
+ "nextjs",
263
+ "no-verified-solution",
264
+ "numerical-analysis",
265
+ "numerical-approximation",
266
+ "numerical-optimization",
267
+ "numpy",
268
+ "ocr",
269
+ "offline-evaluation",
270
+ "operating-systems",
271
+ "optimization",
272
+ "owl",
273
+ "pandas",
274
+ "panel-construction",
275
+ "parametric",
276
+ "parquet",
277
+ "pathfinding",
278
+ "pde",
279
+ "penetration-testing",
280
+ "performance",
281
+ "performance-engineering",
282
+ "performance-optimization",
283
+ "phonology",
284
+ "photonics",
285
+ "physical-chemistry",
286
+ "physics",
287
+ "pipeline-parallelism",
288
+ "pixel-matching",
289
+ "planning",
290
+ "playwright",
291
+ "pmars",
292
+ "postgresql",
293
+ "pretraining",
294
+ "privacy",
295
+ "process-modeling",
296
+ "production planning",
297
+ "proofs",
298
+ "protein-structure",
299
+ "proteins",
300
+ "proteomics",
301
+ "ptrace",
302
+ "pydrake",
303
+ "pystan",
304
+ "python",
305
+ "python-extension",
306
+ "python-migration",
307
+ "pytorch",
308
+ "qemu",
309
+ "query-optimization",
310
+ "qwen3-coder",
311
+ "radiochemistry",
312
+ "ray-tracing",
313
+ "rdf",
314
+ "react",
315
+ "reasoning",
316
+ "reasoning-model",
317
+ "recovery",
318
+ "reed-solomon",
319
+ "regex",
320
+ "reimplementation",
321
+ "release-security",
322
+ "report-generation",
323
+ "research",
324
+ "retrieval",
325
+ "retro-computing",
326
+ "retro-console",
327
+ "reverse-engineering",
328
+ "rocksdb",
329
+ "routing",
330
+ "rstan",
331
+ "rtl",
332
+ "rule-induction",
333
+ "sa-ccr",
334
+ "satb",
335
+ "scala",
336
+ "scheduling",
337
+ "schema-mapping",
338
+ "scientific-computing",
339
+ "scipy",
340
+ "security",
341
+ "session-windows",
342
+ "sglang",
343
+ "simulation",
344
+ "software-engineering",
345
+ "software-installation",
346
+ "source-maps",
347
+ "spark",
348
+ "sparql",
349
+ "sparql-query",
350
+ "spatial-reasoning",
351
+ "spcas9",
352
+ "spectral-methods",
353
+ "spectroscopy",
354
+ "speculative-decoding",
355
+ "spring_clip",
356
+ "sql",
357
+ "sql-query",
358
+ "sqlite",
359
+ "stan",
360
+ "state",
361
+ "stateful-cli",
362
+ "statistical-filing",
363
+ "statistics",
364
+ "stats",
365
+ "storage-engine",
366
+ "stream-processing",
367
+ "streaming",
368
+ "string-parsing",
369
+ "string-rewriting",
370
+ "structural-elucidation",
371
+ "supramolecular-chemistry",
372
+ "symbolic-math",
373
+ "sympy",
374
+ "sys-admin",
375
+ "system",
376
+ "system-on-chip",
377
+ "tandem-ms",
378
+ "tariffs",
379
+ "template-matching",
380
+ "temporal-reasoning",
381
+ "tensor-parallelism",
382
+ "tensorflow",
383
+ "terminal",
384
+ "text-editing",
385
+ "theorem-proving",
386
+ "time-series",
387
+ "tokenization",
388
+ "tool-calling",
389
+ "topology",
390
+ "topology-optimization",
391
+ "trajectory-optimization",
392
+ "transformer",
393
+ "troubleshooting",
394
+ "typescript",
395
+ "typography",
396
+ "uefi",
397
+ "userform",
398
+ "utility-billing",
399
+ "validation",
400
+ "vba",
401
+ "vector-graphics",
402
+ "verilog",
403
+ "version-control",
404
+ "video-processing",
405
+ "vigenere",
406
+ "vim",
407
+ "vim-macros",
408
+ "virtual-machine",
409
+ "virtualization",
410
+ "vision",
411
+ "vllm",
412
+ "vnc",
413
+ "voice-leading",
414
+ "wal",
415
+ "warranty",
416
+ "watermarks",
417
+ "wdm",
418
+ "web",
419
+ "web-security",
420
+ "web-server",
421
+ "weight-balance",
422
+ "windows-3.11",
423
+ "workflow",
424
+ "x86_64",
425
+ "xss",
426
+ "zero-downtime"
427
+ ],
428
+ "slug_of": {
429
+ "3d-modeling": "3d-modeling",
430
+ "AMT": "AMT",
431
+ "Bayesian-inference": "Bayesian-inference",
432
+ "C": "C",
433
+ "ERP": "ERP",
434
+ "GSEA": "GSEA",
435
+ "MES": "MES",
436
+ "MusicXML": "MusicXML",
437
+ "R": "R",
438
+ "SATB": "SATB",
439
+ "TMT": "TMT",
440
+ "WMS": "WMS",
441
+ "adaptive-rejection-sampling": "adaptive-rejection-sampling",
442
+ "administrative-data": "administrative-data",
443
+ "advertising": "advertising",
444
+ "after-sales": "after-sales",
445
+ "aggregation": "aggregation",
446
+ "algorithms": "algorithms",
447
+ "analysis": "analysis",
448
+ "anonymization": "anonymization",
449
+ "api": "api",
450
+ "applied-microeconomics": "applied-microeconomics",
451
+ "applied-statistics": "applied-statistics",
452
+ "asset-lineage": "asset-lineage",
453
+ "async": "async",
454
+ "audio-transcription": "audio-transcription",
455
+ "audit-provenance": "audit-provenance",
456
+ "autokey-cipher": "autokey-cipher",
457
+ "aviation": "aviation",
458
+ "bash": "bash",
459
+ "batching": "batching",
460
+ "bayesian-network": "bayesian-network",
461
+ "bayesian-statistics": "bayesian-statistics",
462
+ "bcbs-279": "bcbs-279",
463
+ "benchmark-construction": "benchmark-construction",
464
+ "bessel-functions": "bessel-functions",
465
+ "binlog": "binlog",
466
+ "bioinformatics": "bioinformatics",
467
+ "biology": "biology",
468
+ "black-box-parity": "black-box-parity",
469
+ "bootkit": "bootkit",
470
+ "browser": "browser",
471
+ "build-tools": "build-tools",
472
+ "bun": "bun",
473
+ "c": "c",
474
+ "c-programming": "c-programming",
475
+ "cad": "cad",
476
+ "caffe": "caffe",
477
+ "calendar": "calendar",
478
+ "cdc": "cdc",
479
+ "checkpoint-conversion": "checkpoint-conversion",
480
+ "cheminformatics": "cheminformatics",
481
+ "chemistry": "chemistry",
482
+ "chorale": "chorale",
483
+ "cli": "cli",
484
+ "climate-science": "climate-science",
485
+ "cloning": "cloning",
486
+ "cnn": "cnn",
487
+ "code-vulnerability": "code-vulnerability",
488
+ "coding": "coding",
489
+ "cold-start": "cold-start",
490
+ "combinatorial-optimization": "combinatorial-optimization",
491
+ "combinatorics": "combinatorics",
492
+ "common-weakness-enumeration": "common-weakness-enumeration",
493
+ "compaction": "compaction",
494
+ "compilation": "compilation",
495
+ "compiled-extensions": "compiled-extensions",
496
+ "compilers": "compilers",
497
+ "compliance": "compliance",
498
+ "compression": "compression",
499
+ "computer-use": "computer-use",
500
+ "computer-vision": "computer-vision",
501
+ "concurrency": "concurrency",
502
+ "config-generation": "config-generation",
503
+ "config-management": "config-management",
504
+ "conflict-resolution": "conflict-resolution",
505
+ "connected_components": "connected_components",
506
+ "constraint": "constraint",
507
+ "constraint-satisfaction": "constraint-satisfaction",
508
+ "contact-dynamics": "contact-dynamics",
509
+ "coq": "coq",
510
+ "corewars": "corewars",
511
+ "counterparty-credit-risk": "counterparty-credit-risk",
512
+ "cpp": "cpp",
513
+ "cpu": "cpu",
514
+ "crm": "crm",
515
+ "cross-referencing": "cross-referencing",
516
+ "cryptanalysis": "cryptanalysis",
517
+ "cryptography": "cryptography",
518
+ "crystallography": "crystallography",
519
+ "csv": "csv",
520
+ "cuda": "cuda",
521
+ "custom-protocol": "custom-protocol",
522
+ "cybersecurity": "cybersecurity",
523
+ "cybersecurity-vulnerability-resolution": "cybersecurity-vulnerability-resolution",
524
+ "dangerous-goods": "dangerous-goods",
525
+ "data": "data",
526
+ "data engineering": "data_engineering",
527
+ "data processing": "data_processing",
528
+ "data-analysis": "data-analysis",
529
+ "data-integration": "data-integration",
530
+ "data-linkage": "data-linkage",
531
+ "data-pipeline": "data-pipeline",
532
+ "data-processing": "data-processing",
533
+ "data-science": "data-science",
534
+ "data_engineering": "data_engineering_dup",
535
+ "database": "database",
536
+ "databases": "databases",
537
+ "datasets": "datasets",
538
+ "debian": "debian",
539
+ "debugging": "debugging",
540
+ "decrypt": "decrypt",
541
+ "deduplication": "deduplication",
542
+ "deeply-technical-ip": "deeply-technical-ip",
543
+ "deepseek-r1": "deepseek-r1",
544
+ "dependency": "dependency",
545
+ "devops": "devops",
546
+ "differential-expression": "differential-expression",
547
+ "digital-design": "digital-design",
548
+ "dispatch": "dispatch",
549
+ "distributed-systems": "distributed-systems",
550
+ "distributed-training": "distributed-training",
551
+ "distributed_systems": "distributed_systems",
552
+ "document-processing": "document-processing",
553
+ "document-verification": "document-verification",
554
+ "domain-reasoning": "domain-reasoning",
555
+ "drawing": "drawing",
556
+ "drift-detection": "drift-detection",
557
+ "driver-hours": "driver-hours",
558
+ "editable-layout": "editable-layout",
559
+ "email-server": "email-server",
560
+ "embedded-systems": "embedded-systems",
561
+ "encryption": "encryption",
562
+ "ensembl-vep": "ensembl-vep",
563
+ "entity-resolution": "entity-resolution",
564
+ "erp": "erp",
565
+ "etl": "etl",
566
+ "evaluation": "evaluation",
567
+ "event-time": "event-time",
568
+ "excel": "excel",
569
+ "exploration-exploitation": "exploration-exploitation",
570
+ "fastapi": "fastapi",
571
+ "fdtd": "fdtd",
572
+ "feature-interpretation": "feature-interpretation",
573
+ "file-operations": "file-operations",
574
+ "financial": "financial",
575
+ "firmware": "firmware",
576
+ "fitting": "fitting",
577
+ "flight-planning": "flight-planning",
578
+ "forensics": "forensics",
579
+ "formal methods": "formal_methods",
580
+ "formal-mathematics": "formal-mathematics",
581
+ "formal-verification": "formal-verification",
582
+ "forms": "forms",
583
+ "fp8": "fp8",
584
+ "fraud-detection": "fraud-detection",
585
+ "freecad": "freecad",
586
+ "frontend": "frontend",
587
+ "fuel-planning": "fuel-planning",
588
+ "gaming": "gaming",
589
+ "gaussian-process": "gaussian-process",
590
+ "genomics": "genomics",
591
+ "geometry": "geometry",
592
+ "german-compliance": "german-compliance",
593
+ "git": "git",
594
+ "glycomics": "glycomics",
595
+ "gpu": "gpu",
596
+ "gpu-parallelisms": "gpu-parallelisms",
597
+ "grader": "grader",
598
+ "graph-algorithms": "graph-algorithms",
599
+ "graphic-design": "graphic-design",
600
+ "graphics": "graphics",
601
+ "h100": "h100",
602
+ "hardware": "hardware",
603
+ "hardware-kernels": "hardware-kernels",
604
+ "hardware-modeling": "hardware-modeling",
605
+ "harmony": "harmony",
606
+ "heat-pumps": "heat-pumps",
607
+ "hgvs": "hgvs",
608
+ "histopathology": "histopathology",
609
+ "historical-linguistics": "historical-linguistics",
610
+ "household-consumption": "household-consumption",
611
+ "hpc": "hpc",
612
+ "html-sanitization": "html-sanitization",
613
+ "huggingface": "huggingface",
614
+ "hydrogen-bonding": "hydrogen-bonding",
615
+ "ics-parsing": "ics-parsing",
616
+ "image-processing": "image-processing",
617
+ "images": "images",
618
+ "imo": "imo",
619
+ "impeller": "impeller",
620
+ "incident-response": "incident-response",
621
+ "inference": "inference",
622
+ "information-retrieval": "information-retrieval",
623
+ "infrastructure-configuration": "infrastructure-configuration",
624
+ "interpenetration": "interpenetration",
625
+ "intrastat": "intrastat",
626
+ "inverse-design": "inverse-design",
627
+ "inverse-problem": "inverse-problem",
628
+ "jax": "jax",
629
+ "kafka": "kafka",
630
+ "kernel-optimization": "kernel-optimization",
631
+ "knowledge-graph": "knowledge-graph",
632
+ "knowledge-graphs": "knowledge-graphs",
633
+ "kuramoto-sivashinsky": "kuramoto-sivashinsky",
634
+ "large-scale-text-manipulation": "large-scale-text-manipulation",
635
+ "latency": "latency",
636
+ "latex": "latex",
637
+ "layout-analysis": "layout-analysis",
638
+ "layout-reconstruction": "layout-reconstruction",
639
+ "layout-shift": "layout-shift",
640
+ "lean4": "lean4",
641
+ "legacy-gui": "legacy-gui",
642
+ "legacy-modernization": "legacy-modernization",
643
+ "legacy-software": "legacy-software",
644
+ "linux": "linux",
645
+ "liquid_scintillation_counting": "liquid_scintillation_counting",
646
+ "list-decoding": "list-decoding",
647
+ "litdata": "litdata",
648
+ "live-surgery": "live-surgery",
649
+ "live-system": "live-system",
650
+ "llm": "llm",
651
+ "llm-eval": "llm-eval",
652
+ "log-analysis": "log-analysis",
653
+ "logistics": "logistics",
654
+ "lsh": "lsh",
655
+ "lsm": "lsm",
656
+ "machine-learning": "machine-learning",
657
+ "mailing-list": "mailing-list",
658
+ "manufacturing": "manufacturing",
659
+ "mass-spectrometry": "mass-spectrometry",
660
+ "mathematics": "mathematics",
661
+ "mcmc": "mcmc",
662
+ "mechanical-engineering": "mechanical-engineering",
663
+ "medical-billing": "medical-billing",
664
+ "meep": "meep",
665
+ "megatron": "megatron",
666
+ "memory-management": "memory-management",
667
+ "meter-data": "meter-data",
668
+ "migration": "migration",
669
+ "minhash": "minhash",
670
+ "mixture-of-experts": "mixture-of-experts",
671
+ "ml-monitoring": "ml-monitoring",
672
+ "ml-platform": "ml-platform",
673
+ "mlops": "mlops",
674
+ "model-risk": "model-risk",
675
+ "model-training": "model-training",
676
+ "mteb": "mteb",
677
+ "mujoco": "mujoco",
678
+ "multi-container": "multi-container",
679
+ "multi-modal": "multi-modal",
680
+ "multi-service": "multi-service",
681
+ "music": "music",
682
+ "mvcc": "mvcc",
683
+ "mysql": "mysql",
684
+ "network": "network",
685
+ "network-analysis": "network-analysis",
686
+ "networking": "networking",
687
+ "next.js": "next.js",
688
+ "nextjs": "nextjs",
689
+ "no-verified-solution": "no-verified-solution",
690
+ "numerical-analysis": "numerical-analysis",
691
+ "numerical-approximation": "numerical-approximation",
692
+ "numerical-optimization": "numerical-optimization",
693
+ "numpy": "numpy",
694
+ "ocr": "ocr",
695
+ "offline-evaluation": "offline-evaluation",
696
+ "operating-systems": "operating-systems",
697
+ "optimization": "optimization",
698
+ "owl": "owl",
699
+ "pandas": "pandas",
700
+ "panel-construction": "panel-construction",
701
+ "parametric": "parametric",
702
+ "parquet": "parquet",
703
+ "pathfinding": "pathfinding",
704
+ "pde": "pde",
705
+ "penetration-testing": "penetration-testing",
706
+ "performance": "performance",
707
+ "performance-engineering": "performance-engineering",
708
+ "performance-optimization": "performance-optimization",
709
+ "phonology": "phonology",
710
+ "photonics": "photonics",
711
+ "physical-chemistry": "physical-chemistry",
712
+ "physics": "physics",
713
+ "pipeline-parallelism": "pipeline-parallelism",
714
+ "pixel-matching": "pixel-matching",
715
+ "planning": "planning",
716
+ "playwright": "playwright",
717
+ "pmars": "pmars",
718
+ "postgresql": "postgresql",
719
+ "pretraining": "pretraining",
720
+ "privacy": "privacy",
721
+ "process-modeling": "process-modeling",
722
+ "production planning": "production_planning",
723
+ "proofs": "proofs",
724
+ "protein-structure": "protein-structure",
725
+ "proteins": "proteins",
726
+ "proteomics": "proteomics",
727
+ "ptrace": "ptrace",
728
+ "pydrake": "pydrake",
729
+ "pystan": "pystan",
730
+ "python": "python",
731
+ "python-extension": "python-extension",
732
+ "python-migration": "python-migration",
733
+ "pytorch": "pytorch",
734
+ "qemu": "qemu",
735
+ "query-optimization": "query-optimization",
736
+ "qwen3-coder": "qwen3-coder",
737
+ "radiochemistry": "radiochemistry",
738
+ "ray-tracing": "ray-tracing",
739
+ "rdf": "rdf",
740
+ "react": "react",
741
+ "reasoning": "reasoning",
742
+ "reasoning-model": "reasoning-model",
743
+ "recovery": "recovery",
744
+ "reed-solomon": "reed-solomon",
745
+ "regex": "regex",
746
+ "reimplementation": "reimplementation",
747
+ "release-security": "release-security",
748
+ "report-generation": "report-generation",
749
+ "research": "research",
750
+ "retrieval": "retrieval",
751
+ "retro-computing": "retro-computing",
752
+ "retro-console": "retro-console",
753
+ "reverse-engineering": "reverse-engineering",
754
+ "rocksdb": "rocksdb",
755
+ "routing": "routing",
756
+ "rstan": "rstan",
757
+ "rtl": "rtl",
758
+ "rule-induction": "rule-induction",
759
+ "sa-ccr": "sa-ccr",
760
+ "satb": "satb",
761
+ "scala": "scala",
762
+ "scheduling": "scheduling",
763
+ "schema-mapping": "schema-mapping",
764
+ "scientific-computing": "scientific-computing",
765
+ "scipy": "scipy",
766
+ "security": "security",
767
+ "session-windows": "session-windows",
768
+ "sglang": "sglang",
769
+ "simulation": "simulation",
770
+ "software-engineering": "software-engineering",
771
+ "software-installation": "software-installation",
772
+ "source-maps": "source-maps",
773
+ "spark": "spark",
774
+ "sparql": "sparql",
775
+ "sparql-query": "sparql-query",
776
+ "spatial-reasoning": "spatial-reasoning",
777
+ "spcas9": "spcas9",
778
+ "spectral-methods": "spectral-methods",
779
+ "spectroscopy": "spectroscopy",
780
+ "speculative-decoding": "speculative-decoding",
781
+ "spring_clip": "spring_clip",
782
+ "sql": "sql",
783
+ "sql-query": "sql-query",
784
+ "sqlite": "sqlite",
785
+ "stan": "stan",
786
+ "state": "state",
787
+ "stateful-cli": "stateful-cli",
788
+ "statistical-filing": "statistical-filing",
789
+ "statistics": "statistics",
790
+ "stats": "stats",
791
+ "storage-engine": "storage-engine",
792
+ "stream-processing": "stream-processing",
793
+ "streaming": "streaming",
794
+ "string-parsing": "string-parsing",
795
+ "string-rewriting": "string-rewriting",
796
+ "structural-elucidation": "structural-elucidation",
797
+ "supramolecular-chemistry": "supramolecular-chemistry",
798
+ "symbolic-math": "symbolic-math",
799
+ "sympy": "sympy",
800
+ "sys-admin": "sys-admin",
801
+ "system": "system",
802
+ "system-on-chip": "system-on-chip",
803
+ "tandem-ms": "tandem-ms",
804
+ "tariffs": "tariffs",
805
+ "template-matching": "template-matching",
806
+ "temporal-reasoning": "temporal-reasoning",
807
+ "tensor-parallelism": "tensor-parallelism",
808
+ "tensorflow": "tensorflow",
809
+ "terminal": "terminal",
810
+ "text-editing": "text-editing",
811
+ "theorem-proving": "theorem-proving",
812
+ "time-series": "time-series",
813
+ "tokenization": "tokenization",
814
+ "tool-calling": "tool-calling",
815
+ "topology": "topology",
816
+ "topology-optimization": "topology-optimization",
817
+ "trajectory-optimization": "trajectory-optimization",
818
+ "transformer": "transformer",
819
+ "troubleshooting": "troubleshooting",
820
+ "typescript": "typescript",
821
+ "typography": "typography",
822
+ "uefi": "uefi",
823
+ "userform": "userform",
824
+ "utility-billing": "utility-billing",
825
+ "validation": "validation",
826
+ "vba": "vba",
827
+ "vector-graphics": "vector-graphics",
828
+ "verilog": "verilog",
829
+ "version-control": "version-control",
830
+ "video-processing": "video-processing",
831
+ "vigenere": "vigenere",
832
+ "vim": "vim",
833
+ "vim-macros": "vim-macros",
834
+ "virtual-machine": "virtual-machine",
835
+ "virtualization": "virtualization",
836
+ "vision": "vision",
837
+ "vllm": "vllm",
838
+ "vnc": "vnc",
839
+ "voice-leading": "voice-leading",
840
+ "wal": "wal",
841
+ "warranty": "warranty",
842
+ "watermarks": "watermarks",
843
+ "wdm": "wdm",
844
+ "web": "web",
845
+ "web-security": "web-security",
846
+ "web-server": "web-server",
847
+ "weight-balance": "weight-balance",
848
+ "windows-3.11": "windows-3.11",
849
+ "workflow": "workflow",
850
+ "x86_64": "x86_64",
851
+ "xss": "xss",
852
+ "zero-downtime": "zero-downtime"
853
+ },
854
+ "n_tags": 424
855
+ }
tags/train.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exact training call used for this model. Run from inside this directory:
2
+ python train.py
3
+ Same dim64/epoch10 baseline hyperparams as quality/multilingual, plus loss="ova"
4
+ (fastText's native multi-label mode): one shared model/vocabulary across all 424 tags,
5
+ trained jointly, rather than 424 separate binary classifiers -- see README.md."""
6
+ import fasttext
7
+
8
+ HPARAMS = dict(dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3, bucket=2_000_000, loss="ova")
9
+
10
+ model = fasttext.train_supervised(input="train.txt", **HPARAMS)
11
+ model.save_model("model.bin")
12
+ print("saved model.bin")
tags/train.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:deafd9750d4d82b7209ee453381cc1bacf5dcae49bd125cdbf702f3136ec19af
3
+ size 1114536579
tags/val.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:19ae51807b1c1d42981ec8d41907bddfa095358e301e59a7e689d6e01348f666
3
+ size 124512325