GraphTok

Adding a Tokenizer

Two cases. Pick the one that matches your vocab.

Case A — same pre-tokenizer family as an existing vocab

If your tokenizer.json uses a pre-tokenizer regex identical to GPT-2, Llama-3, Qwen-2.5, or DeepSeek-V3 (most byte-level BPE models do), there is no boundary-kernel change. Its AddedVocabulary entries also work without new code when they are literal, have no single_word/strip flags, use an identity normalizer (or the existing ASCII-stable NFC case), and no literal start byte occurs inside another literal. Other semantics are rejected during vocabulary loading; add a shared matcher capability rather than embedding it in the family regex kernel.

gpu_bpe_tokenize --vocab path/to/tokenizer.json --input text.txt --output ids.bin

The loader detects the family from the pre_tokenizer field (vocab.cc: detect_regex_kind) and picks the merge-table slot layout from the vocab size automatically. Verify bit-exactness against your reference:

scripts/test-all-presets.sh <name>

Case B — a genuinely new pre-tokenizer regex

A new regex means a new boundary kernel (the only per-vocab GPU code — see ARCHITECTURE.md section 2.1). The discipline below is mandatory because a wrong boundary corrupts output silently on inputs the English corpus never exercises.

1. Prove the predicate in Python first — before any CUDA. Write a Python reference module exporting ORACLE_PAT (the regex) and ref_pretokenize_<name>(text) -> [(start,end)] (a parallel boundary predicate: is_boundary[k] from a bounded codepoint window + segmented run-scans, never a serial cursor). Diff it against the regex on a corpus plus an edge set until it matches.

Include long-run edge cases (long digit runs, CJK, CRLF) — these expose window-collapse bugs the corpus hides.

2. Extend the class table if you need new character classes. GPT-2/Llama/Qwen need only L/N/whitespace. DeepSeek added \p{M}\p{P}\p{S} + CJK ranges. If yours needs more, extend tools/gen_class_table.py, regenerate generated/gbpe_class_table.h, and confirm the exhaustive validation reports 0 mismatches vs the reference engine.

3. Translate the predicate to a pretok_k2_<name> kernel. In src/tokenizer.cu, add the kernel guarded by #if GBPE_HAVE_VOCAB_<NAME>, reusing the shared helpers in namespace pretok (src/pretok_boundary.h) (cls_at, b0_at, starts_contraction, the digit-run scan). Add the dispatch arm in launch_pretok and a PretokKind value (tokenizer.cuh), set it in vocab.cc: build_vocab_pack.

4. Wire the build. Add the vocab to CMakeLists.txt (GBPE_KNOWN_VOCABS) and CMakePresets.json. Single-vocab presets must compile only your kernel.

5. Gate it end-to-end (not just the Python predicate):

cmake --preset <name> && cmake --build build/<name> -j
scripts/test-all-presets.sh <name>
compute-sanitizer --tool memcheck build/<name>/gpu_bpe_tokenize --vocab ... --input ... --output ... --runs 1 --warmup 0

Bit-exact on all size rungs + 0 sanitizer errors, or it doesn't land.