Spaces:
Sleeping
Sleeping
| // Byte-level BPE, read straight from a repo's tokenizer.json. | |
| // | |
| // The three families this build supports — GPT-2, Llama-style (SmolLM, | |
| // TinyLlama), and Qwen2 — all use byte-level BPE, so one implementation covers | |
| // them. A repo using anything else (SentencePiece/Unigram, WordPiece) is | |
| // refused by name rather than approximated: a tokenizer that is merely close | |
| // produces text that looks right and is subtly not, which is the worst | |
| // possible failure mode to ship quietly. | |
| (function (root) { | |
| "use strict"; | |
| // GPT-2's byte<->unicode table. Byte-level BPE maps raw bytes into printable | |
| // codepoints so the merge table can be plain text; every byte round-trips. | |
| function byteTables() { | |
| const bs = []; | |
| for (let i = 33; i <= 126; i++) bs.push(i); | |
| for (let i = 161; i <= 172; i++) bs.push(i); | |
| for (let i = 174; i <= 255; i++) bs.push(i); | |
| const cs = bs.slice(); | |
| let n = 0; | |
| for (let b = 0; b < 256; b++) | |
| if (!bs.includes(b)) { bs.push(b); cs.push(256 + n); n++; } | |
| const b2u = new Map(), u2b = new Map(); | |
| for (let i = 0; i < bs.length; i++) { | |
| const ch = String.fromCodePoint(cs[i]); | |
| b2u.set(bs[i], ch); u2b.set(ch, bs[i]); | |
| } | |
| return { b2u, u2b }; | |
| } | |
| const { b2u, u2b } = byteTables(); | |
| // GPT-2 / Llama pretokenizer split. Keeps leading spaces attached to the | |
| // following word, which is what the merge table was built against. | |
| const SPLIT = /'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?[\p{N}]+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu; | |
| function build(json) { | |
| const model = json.model || {}; | |
| if (String(model.type || "").toUpperCase() !== "BPE") | |
| throw new Error(`tokenizer type "${model.type}" is not supported — this build implements byte-level BPE ` + | |
| `(GPT-2, Llama-style and Qwen2 repos). A different tokenizer would decode to subtly wrong text.`); | |
| const vocab = model.vocab || {}; | |
| const ids = []; | |
| for (const [tokStr, id] of Object.entries(vocab)) ids[id] = tokStr; | |
| // merges may be "a b" strings or ["a","b"] pairs depending on the version | |
| const ranks = new Map(); | |
| (model.merges || []).forEach((m, i) => { | |
| const pair = Array.isArray(m) ? m : String(m).split(" "); | |
| if (pair.length === 2) ranks.set(pair[0] + " |