""" Pure-Python byte-level BPE tokenizer. Supports loading from HuggingFace tokenizer.json (LLaMA-style) and from Mistral tekken JSON blobs. No dependency on the `transformers`, `tokenizers`, or `regex` packages. """ import base64 import json import os import re import unicodedata # This is also the default pattern used by the previous MistralConverter path. _LLAMA_PATTERN = r"""(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+""" _CONTRACTIONS = ("'re", "'ve", "'ll", "'s", "'t", "'m", "'d") def _is_letter(c): return unicodedata.category(c)[0] == "L" def _is_number(c): return unicodedata.category(c)[0] == "N" def _is_whitespace(c): return c in " \t\n\r\v\f\x85\u2028\u2029" or unicodedata.category(c) == "Zs" def _split_llama(text): pieces = [] i = 0 while i < len(text): contraction = None if text[i] == "'": for suffix in _CONTRACTIONS: if text[i:i + len(suffix)].casefold() == suffix: contraction = text[i:i + len(suffix)] break if contraction is not None: pieces.append(contraction) i += len(contraction) continue j = i if text[j] not in "\r\n" and not _is_letter(text[j]) and not _is_number(text[j]): j += 1 if j < len(text) and _is_letter(text[j]): j += 1 while j < len(text) and _is_letter(text[j]): j += 1 pieces.append(text[i:j]) i = j continue if _is_number(text[i]): j = i + 1 while j < len(text) and j - i < 3 and _is_number(text[j]): j += 1 pieces.append(text[i:j]) i = j continue j = i if text[j] == " ": j += 1 punct_start = j while j < len(text) and not _is_whitespace(text[j]) and not _is_letter(text[j]) and not _is_number(text[j]): j += 1 if j > punct_start: while j < len(text) and text[j] in "\r\n": j += 1 pieces.append(text[i:j]) i = j continue if _is_whitespace(text[i]): j = i + 1 while j < len(text) and _is_whitespace(text[j]): j += 1 last_newline = max(text.rfind("\r", i, j), text.rfind("\n", i, j)) if last_newline >= i: j = last_newline + 1 elif j < len(text) and j - i > 1: j -= 1 pieces.append(text[i:j]) i = j continue pieces.append(text[i]) i += 1 return pieces def _make_split_pattern(pattern_str): if pattern_str != _LLAMA_PATTERN: raise ValueError(f"Unsupported tokenizer split pattern: {pattern_str}") return _split_llama def _bytes_to_unicode(): bs = (list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1))) cs = bs[:] n = 0 for b in range(2**8): if b not in bs: bs.append(b) cs.append(2**8 + n) n += 1 cs = [chr(n) for n in cs] return dict(zip(bs, cs)) class BPETokenizer: """Byte-level BPE tokenizer with optional BOS prepending.""" def __init__(self, vocab, merges_by_pair, special_token_ids, pattern_str, byte_encoder, byte_decoder, bos_id=None): self._vocab = vocab # str -> int self._inv_vocab = {v: k for k, v in vocab.items()} self._merges = merges_by_pair # (str, str) -> priority int self._special_token_ids = special_token_ids # str -> int self._special_ids = set(special_token_ids.values()) self._byte_encoder = byte_encoder self._byte_decoder = byte_decoder self._bos_id = bos_id self._split = _make_split_pattern(pattern_str) sorted_specials = sorted(special_token_ids.keys(), key=len, reverse=True) if sorted_specials: self._special_split = re.compile( '(' + '|'.join(re.escape(s) for s in sorted_specials) + ')' ) else: self._special_split = None def _bpe_encode_piece(self, chars): if len(chars) <= 1: return chars while True: min_rank = float('inf') best_pair = None for i in range(len(chars) - 1): r = self._merges.get((chars[i], chars[i + 1]), float('inf')) if r < min_rank: min_rank = r best_pair = (chars[i], chars[i + 1]) if best_pair is None: break merged = best_pair[0] + best_pair[1] new_chars = [] i = 0 while i < len(chars): if i < len(chars) - 1 and chars[i] == best_pair[0] and chars[i + 1] == best_pair[1]: new_chars.append(merged) i += 2 else: new_chars.append(chars[i]) i += 1 chars = new_chars if len(chars) == 1: break return chars def _encode_raw(self, text): ids = [] parts = self._special_split.split(text) if self._special_split else [text] for part in parts: if not part: continue if part in self._special_token_ids: ids.append(self._special_token_ids[part]) else: for piece in self._split(part): byte_chars = [self._byte_encoder[b] for b in piece.encode('utf-8')] for tok in self._bpe_encode_piece(byte_chars): ids.append(self._vocab[tok]) return ids def __call__(self, text): ids = self._encode_raw(text) if self._bos_id is not None: ids = [self._bos_id] + ids return {"input_ids": ids} def get_vocab(self): return dict(self._vocab) def decode(self, token_ids, skip_special_tokens=True): buf = bytearray() for tid in token_ids: s = self._inv_vocab.get(tid, '') if tid in self._special_ids: if not skip_special_tokens: buf.extend(s.encode('utf-8')) else: for c in s: buf.append(self._byte_decoder[c]) return buf.decode('utf-8', errors='replace') def _extract_pattern(pretok): if pretok.get('type') == 'Sequence': for sub in pretok.get('pretokenizers', []): if sub.get('type') == 'Split': pat = sub.get('pattern', {}) if 'Regex' in pat: return pat['Regex'] elif pretok.get('type') == 'Split': pat = pretok.get('pattern', {}) if 'Regex' in pat: return pat['Regex'] return None def _extract_bos_id(post_processor, special_token_ids): if post_processor.get('type') == 'TemplateProcessing': single = post_processor.get('single', []) if single and 'SpecialToken' in single[0]: bos_str = single[0]['SpecialToken']['id'] return special_token_ids.get(bos_str) return None def from_tokenizer_json(path): """Load a BPETokenizer from a directory containing tokenizer.json.""" tok_file = os.path.join(path, 'tokenizer.json') with open(tok_file, encoding='utf-8') as f: data = json.load(f) vocab = dict(data['model']['vocab']) # str -> int merges_by_pair = {} for i, merge_str in enumerate(data['model'].get('merges', [])): a, b = merge_str.split(' ', 1) if (a, b) not in merges_by_pair: merges_by_pair[(a, b)] = i special_token_ids = {} for tok in data.get('added_tokens', []): special_token_ids[tok['content']] = tok['id'] vocab[tok['content']] = tok['id'] # include in vocab for inv_vocab decode pattern = _extract_pattern(data.get('pre_tokenizer', {})) if pattern is None: raise ValueError(f"Could not extract regex pattern from {tok_file}") bos_id = _extract_bos_id(data.get('post_processor', {}), special_token_ids) byte_encoder = _bytes_to_unicode() byte_decoder = {v: k for k, v in byte_encoder.items()} return BPETokenizer(vocab, merges_by_pair, special_token_ids, pattern, byte_encoder, byte_decoder, bos_id=bos_id) def from_tekken_json(data): """Build a BPETokenizer from a Mistral tekken JSON blob (bytes or str).""" mistral_vocab = json.loads(data) config = mistral_vocab["config"] byte_encoder = _bytes_to_unicode() byte_decoder = {v: k for k, v in byte_encoder.items()} def tbts(b): return "".join(byte_encoder[ord(c)] for c in b.decode("latin-1")) special_token_offset = config["default_num_special_tokens"] max_vocab = config["default_vocab_size"] - special_token_offset raw_vocab = {} for w in mistral_vocab["vocab"]: r = w["rank"] if r >= max_vocab: continue raw_vocab[base64.b64decode(w["token_bytes"])] = r + special_token_offset special_tokens_dict = {} for w in mistral_vocab["special_tokens"]: if "token_bytes" in w: special_tokens_dict[base64.b64decode(w["token_bytes"])] = w["rank"] else: special_tokens_dict[w["token_str"]] = w["rank"] all_special = list(special_tokens_dict.keys()) combined = dict(special_tokens_dict) combined.update(raw_vocab) bpe_vocab = {} merge_triples = [] for token, rank in combined.items(): if token not in all_special: bpe_vocab[tbts(token)] = rank if len(token) == 1: continue local = [] for i in range(1, len(token)): pl, pr = token[:i], token[i:] if pl in combined and pr in combined and (pl + pr) in combined: local.append((pl, pr, rank)) local.sort(key=lambda x: (combined[x[0]], combined[x[1]])) merge_triples.extend(local) else: tok_str = token.decode("utf-8", errors="replace") if isinstance(token, bytes) else token bpe_vocab[tok_str] = rank merge_triples.sort(key=lambda v: v[2]) merges_by_pair = {} for i, (pl, pr, _) in enumerate(merge_triples): pair = (tbts(pl), tbts(pr)) if pair not in merges_by_pair: merges_by_pair[pair] = i special_str_ids = {} for tok in all_special: tok_str = tok.decode("utf-8", errors="replace") if isinstance(tok, bytes) else tok if tok_str in bpe_vocab: special_str_ids[tok_str] = bpe_vocab[tok_str] return BPETokenizer(bpe_vocab, merges_by_pair, special_str_ids, _LLAMA_PATTERN, byte_encoder, byte_decoder, bos_id=None) class LlamaTokenizerFast: """Drop-in replacement for transformers.LlamaTokenizerFast (read-only use).""" @staticmethod def from_pretrained(path, **kwargs): return from_tokenizer_json(path)