| """Character-level tokenizer matching the trained vocabulary exactly. |
| |
| Loads the ``tokenizer.json`` artifact produced during training. The |
| token-to-id mapping is preserved verbatim; it is never re-derived from a |
| text file, so token ids stay stable. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| from collections.abc import Iterable |
| from pathlib import Path |
|
|
|
|
| class CharacterTokenizer: |
| def __init__( |
| self, |
| *, |
| token_to_id: dict[str, int], |
| id_to_token: dict[int, str], |
| ) -> None: |
| self.token_to_id = token_to_id |
| self.id_to_token = id_to_token |
|
|
| @classmethod |
| def from_file( |
| cls, |
| path: str | Path, |
| ) -> "CharacterTokenizer": |
| data = json.loads( |
| Path(path).read_text( |
| encoding="utf-8", |
| ) |
| ) |
|
|
| token_to_id = { |
| token: int(token_id) |
| for token, token_id in data["token_to_id"].items() |
| } |
|
|
| id_to_token = { |
| int(token_id): token |
| for token_id, token in data["id_to_token"].items() |
| } |
|
|
| return cls( |
| token_to_id=token_to_id, |
| id_to_token=id_to_token, |
| ) |
|
|
| @property |
| def vocab_size( |
| self, |
| ) -> int: |
| return len(self.token_to_id) |
|
|
| def encode( |
| self, |
| text: str, |
| ) -> list[int]: |
| return [self.token_to_id[token] for token in text] |
|
|
| def decode( |
| self, |
| token_ids: Iterable[int], |
| ) -> str: |
| return "".join(self.id_to_token[token_id] for token_id in token_ids) |