File size: 11,039 Bytes
63f41a6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | """Regenerate tests/data/qwen3_bytelevel_oracle.json.gz -- the frozen HF reference for
tests/test_tokenizer_bytelevel.py.
RUN ONLY in the isolated rollout venv (it has transformers; the align venv must never):
CUDA_VISIBLE_DEVICES= .venv-rollout/bin/python tests/data/make_qwen3_bytelevel_oracle.py
transformers is imported HERE, as a separate process, to DUMP reference ids/decodes.
align itself never imports it (tests/test_dependency_firewall.py scans align/ and
scripts/ only; this file lives under tests/data and is data tooling, not runtime).
Corpus: all 244 minif2f + 360 MOBench formal statements, the P2-B probe completions
(first 2000 chars each), Unicode stress strings, the exact chat-templated Goedel prompts
for the first 50 minif2f problems + correction-round prompts, chat-template cases, and
id sequences for decode. Ids are stored as zlib(uint32 LE) base64 to keep the file small.
"""
import array
import base64
import gzip
import json
import os
import random
import sys
import zlib
REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SNAP = os.environ.get(
"GOEDEL_SNAPSHOT",
"/apps/development/rlhf/hf_cache/hub/models--Goedel-LM--Goedel-Prover-V2-8B/snapshots/"
"dfd02e6271a58375dfbf3ece0175277cf6b6a89a",
)
DATASETS = "/apps/development/rlhf/reference-repos/Goedel-Prover-V2/dataset"
PROBE = os.path.join(REPO, ".tmp/p2/P2-B/probe_16k_seqs256.jsonl")
OUT = os.path.join(REPO, "tests/data/qwen3_bytelevel_oracle.json.gz")
import unicodedata # noqa: E402
from transformers import AutoTokenizer # noqa: E402
sys.path.insert(0, REPO)
from extras.vllm_rollout import lean_prompts # noqa: E402 (stdlib-only at import)
hf = AutoTokenizer.from_pretrained(SNAP)
def pack(ids):
return base64.b64encode(zlib.compress(array.array("I", ids).tobytes(), 9)).decode()
def jl(path):
with open(path) as f:
return [json.loads(x) for x in f if x.strip()]
STRESS = [
"", " ", "\n", "a", "Hello, world!", "hello world", " leading", "trailing ", "a\n\nb\n\n\nc",
"line1\r\nline2\rline3\n", " \n ", "\t\tx\t", "x \t\n y", "a\u00a0b\u2003c\u3000d\u2028e\u2029f\x0bg\x0ch\x85i",
"a\x1cb\x1dc\x1ed\x1fe", "zero\u200bwidth\u200djoiner\ufeffbom",
"2024", "x=1234567890", "3.14159", "١٢٣٤", "²³ ½ ⅷ Ⅷ ①", "1,000,000", "a1b22c333", "0x1F",
"don't can't I'm we're they've she'll he'd it's", "DON'T I'M WE'RE THEY'VE SHE'LL HE'D IT'S", "'s 't 're 've 'm 'll 'd",
"'ſ", "'ſt", "x'ſ", "'S'T'RE", "rock'n'roll", "'quoted'", "o'clock", "''s", "'re's",
"∀ x ∈ ℝ, ∃ y, x < y → ⟨x, y⟩", "theorem foo (f : ℝ → ℝ) (h₀ : ∀ x, f x = 5 * x + 4) : f 1 = 9 := by",
"ℕ ℤ ℚ ℝ ℂ ∑ ∏ ∫ √ ≤ ≥ ≠ ≈ ∈ ∉ ⊂ ⊆ ∪ ∩ ¬ ∧ ∨ ↔ ⇒ λ π", "x₁ x₂ x₃ a⁻¹ x²",
"你好,世界!这是一个测试。", "日本語のテキスト、ひらがな、カタカナ", "한국어 텍스트", "Привет, мир! Ёж", "مرحبا بالعالم", "שלום עולם",
"😀 😃 👨👩👧👦 🏳️🌈 👍🏽 🇮🇳", "\U0001f600\U0001f601", "emoji😀attached",
"e\u0301", "a\u0300\u0301\u0302", "\u1100\u1161\u11a8", "\u0041\u030a vs \u00c5", "\u212b \u2126 \ufb01",
"caf\u00e9 cafe\u0301", "\u0958 \u0915\u093c", "fi fl ㎏ ㍿",
"<|im_start|>user\nhi<|im_end|>\n<|im_start|>assistant\n", "<think>\nreason\n</think>\n\nanswer", "<|endoftext|>",
"<|im_end|>\u0301", "<|im_end|>e\u0301<|im_start|>", "e<|im_end|>\u0301x", "<|im_start|", "<|IM_START|>", "<think", "</think>>",
"<think><think></think></think>", "<|im_start|><|im_end|>", "<tool_call>{}</tool_call>", "<|fim_prefix|>a<|fim_suffix|>b<|fim_middle|>",
"<|vision_pad|><|image_pad|><|video_pad|>", "<|box_start|>x<|box_end|>", "<|file_sep|><|repo_name|>",
"\x00\x01\x02\x7f", "a\x00b", "\ue000 private use", "\ufffe \uffff", "\U0010ffff", "\ufffd",
"\U00001c89 new in Unicode 16", "\U00010d4a garay digit", "\U0001e5d0 ol onal", "\U0000a7ce \U0000a7cf", "\U00031350 CJK ext H",
"\u0378 unassigned", "\U000e0001 tag", "\u180e mongolian vowel sep",
"x" * 5000, " " * 5000, "-" * 3000, "\n" * 2000, "1" * 3000, "ab " * 2000, "=" * 100 + "\n" + "=" * 100,
"a" + " " * 40 + "b", "a" + "\n" * 40 + " b", "\t" * 50 + "x",
"!!!???...", "...", "a,b;c:d", "(a)[b]{c}", "a_b_c", "__init__", "foo.bar(baz)", "$x^2$", "\\frac{1}{3}Bh",
"/-- doc -/\ntheorem t : True := trivial", "```lean4\nexample : 1 + 1 = 2 := by norm_num\n```", " have h : x = 1 := by\n simp\n <;> linarith\n",
"https://example.com/a?b=c&d=e#f", "user@example.com", "C:\\Users\\x", "a\u2028b", "x\u00a0\u00a0y", " \u00a0 ",
"it'sx", "'tis", "I'dnt", "she'lled", "'rest", "'very", "'Sx", "'REx", "we'veto", "they'rent", "i'mnot",
"'The 'This 'Time 'Maybe 'Mean 'Does 'REAL 'Still 'Total", "He said 'The End' and 'THIS IS IT'",
"word\u0301s", "1\u0301", "!\u0301", " \u0301", "\u0301", "\u0301\u0301a",
]
def main():
out = {"snapshot": os.path.basename(SNAP), "hf_transformers": __import__("transformers").__version__,
"hf_tokenizers": __import__("tokenizers").__version__, "texts": [], "id_seqs": [], "chat": []}
texts = [] # (group, text)
for r in jl(os.path.join(DATASETS, "minif2f.jsonl")):
texts.append(("minif2f", r["formal_statement"]))
for r in jl(os.path.join(DATASETS, "MOBench.jsonl")):
texts.append(("mobench", r["formal_statement"]))
for r in jl(PROBE):
texts.append(("completion", r["completion_text"][:2000]))
for s in STRESS:
texts.append(("stress", s))
minif = jl(os.path.join(DATASETS, "minif2f.jsonl"))
for r in minif[:50]:
prompt, msgs = lean_prompts.initial_prompt(r["formal_statement"], hf)
texts.append(("chat_prompt", prompt))
out["chat"].append({"messages": msgs, "kwargs": {"add_generation_prompt": True}, "expected": prompt})
# correction rounds (assistant turn replayed, template rewrites it)
probe = jl(PROBE)
n_corr = 0
for r, p in zip(minif[:20], probe[:20]):
try:
_, m0 = lean_prompts.initial_prompt(r["formal_statement"], hf)
prompt, msgs = lean_prompts.correction_prompt(
r["formal_statement"], m0, "<think>\nplan\n</think>\n\n" + p["completion_text"][:1500],
"import Mathlib\ntheorem x : True := by\n sorry", [], hf, 1)
except Exception as e: # noqa: BLE001
print("correction prompt skipped:", repr(e)[:200])
continue
texts.append(("chat_prompt", prompt))
out["chat"].append({"messages": msgs, "kwargs": {"add_generation_prompt": True}, "expected": prompt})
n_corr += 1
print("correction prompts:", n_corr)
for g, t in texts:
ids = hf.encode(t, add_special_tokens=False)
ids2 = hf(t)["input_ids"]
assert ids == ids2, ("add_special_tokens changes ids", g, t[:40])
dec = hf.decode(ids)
dec_skip = hf.decode(ids, skip_special_tokens=True)
e = {"g": g, "t": t, "ids": pack(ids), "n": len(ids)}
nfc = None
# decode of the ids is the NFC text segment-wise; store explicit decode only when it is not t itself
if dec != t:
e["dec"] = dec
if dec_skip != dec:
e["dec_skip"] = dec_skip
out["texts"].append(e)
# chat-template cases
U = lambda c: {"role": "user", "content": c} # noqa: E731
A = lambda c, **k: {"role": "assistant", "content": c, **k} # noqa: E731
S = lambda c: {"role": "system", "content": c} # noqa: E731
cases = [
[U("hi")], [S("be brief"), U("hi")], [U("hi"), A("hello")], [S("s"), U("q1"), A("a1"), U("q2")],
[U("q1"), A("<think>\nr1\n</think>\n\na1"), U("q2")],
[U("q1"), A("<think>\nr1\n</think>\n\na1"), U("q2"), A("<think>\nr2\n</think>\n\na2")],
[U("q1"), A("<think>\nr1\n</think>\n\na1")],
[U("q1"), A("a1 no think")],
[U("q1"), A("x"), A("<think>\nr\n</think>\n\ny")],
[U("q1"), A("<think>\nr\n</think>\n\ny"), A("<think>\nr2\n</think>\n\nz")],
[U("q1"), A("ans", reasoning_content="RC")],
[U("q1"), A("ans", reasoning_content="\n\nRC\n\n")],
[U("q1"), A("ans", reasoning_content="\n\nRC\n\n"), U("q2"), A("a2", reasoning_content="R2")],
[U("q1"), A("</think>after")], [U("q1"), A("<think>a</think>b</think>c")],
[U("q1"), A("<think>\n\n\nspaced\n\n\n</think>\n\n\n\nbody\n\n")],
[U("q1"), A("\n\nleading newlines")], [U("q1"), A("")], [U(""), A("")],
[S("sys1"), S("sys2"), U("u")], [A("first is assistant"), U("u")],
[U("<tool_response>x</tool_response>"), A("a")], [U("u1"), U("u2")],
[S("only system")], [U("é e\u0301 😀 <|im_end|> literal")],
]
for msgs in cases:
for kw in ({}, {"add_generation_prompt": True}, {"add_generation_prompt": True, "enable_thinking": False},
{"add_generation_prompt": True, "enable_thinking": True}, {"enable_thinking": False}):
exp = hf.apply_chat_template(msgs, tokenize=False, **kw)
out["chat"].append({"messages": msgs, "kwargs": kw, "expected": exp})
texts_ids = hf.encode(exp, add_special_tokens=False)
out["chat"][-1]["ids"] = pack(texts_ids)
# id sequences for decode
rng = random.Random(1234)
V = len(hf)
seqs = [[i] for i in sorted(hf.get_added_vocab().values())]
seqs += [[rng.randrange(V) for _ in range(rng.randrange(1, 48))] for _ in range(150)]
for s in ("😀", "你好", "👨👩👧👦", "é", "\U0001f1ee\U0001f1f3"):
ids = hf.encode(s, add_special_tokens=False)
seqs += [ids, ids[:1], ids[1:], ids[:-1], ids[-1:], ids + ids[:1]]
seqs.append(ids[:1] + [151667] + ids[1:]) # added token splitting a character
seqs += [[151643, 9707, 151645], [9707, 151667, 15, 151668, 220, 151644, 872, 198],
[V - 1, V, V + 5, 151935, 151936, 200000, 9707], [], [9707] * 5]
for s in seqs:
rec = {"ids": s}
for skip in (False, True):
try:
rec["dec_skip" if skip else "dec"] = hf.decode(s, skip_special_tokens=skip)
except Exception as e: # noqa: BLE001
rec["dec_skip" if skip else "dec"] = None
rec["err"] = type(e).__name__
out["id_seqs"].append(rec)
out["added_vocab"] = hf.get_added_vocab()
out["special_ids"] = sorted(hf.all_special_ids)
out["meta"] = {"vocab_size": hf.vocab_size, "len": len(hf), "eos": hf.eos_token_id, "pad": hf.pad_token_id,
"bos": hf.bos_token_id}
with gzip.open(OUT, "wt", encoding="utf-8", compresslevel=9) as f:
json.dump(out, f, ensure_ascii=False, separators=(",", ":"))
print("wrote", OUT, os.path.getsize(OUT), "bytes;", len(out["texts"]), "texts,", len(out["chat"]), "chat,",
len(out["id_seqs"]), "id seqs; tokens:", sum(e["n"] for e in out["texts"]))
if __name__ == "__main__":
main()
|