gpt-tokenizer 2.3.0 → 2.4.1
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.
- package/README.md +13 -1
- package/cjs/BytePairEncodingCore.d.ts +30 -17
- package/cjs/BytePairEncodingCore.js +202 -134
- package/cjs/BytePairEncodingCore.js.map +1 -1
- package/cjs/GptEncoding.d.ts +6 -4
- package/cjs/GptEncoding.js +48 -14
- package/cjs/GptEncoding.js.map +1 -1
- package/cjs/encoding/cl100k_base.d.ts +1 -1
- package/cjs/encoding/o200k_base.d.ts +1 -1
- package/cjs/encoding/p50k_base.d.ts +1 -1
- package/cjs/encoding/p50k_edit.d.ts +1 -1
- package/cjs/encoding/r50k_base.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-0125.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-0301.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-0613.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-1106.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-16k-0613.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-16k.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-instruct-0914.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo-instruct.d.ts +1 -1
- package/cjs/model/gpt-3.5-turbo.d.ts +1 -1
- package/cjs/model/gpt-4-0125-preview.d.ts +1 -1
- package/cjs/model/gpt-4-0314.d.ts +1 -1
- package/cjs/model/gpt-4-0613.d.ts +1 -1
- package/cjs/model/gpt-4-1106-preview.d.ts +1 -1
- package/cjs/model/gpt-4-32k-0314.d.ts +1 -1
- package/cjs/model/gpt-4-32k-0613.d.ts +1 -1
- package/cjs/model/gpt-4-32k.d.ts +1 -1
- package/cjs/model/gpt-4-turbo-2024-04-09.d.ts +1 -1
- package/cjs/model/gpt-4-turbo-preview.d.ts +1 -1
- package/cjs/model/gpt-4-turbo.d.ts +1 -1
- package/cjs/model/gpt-4-vision-preview.d.ts +1 -1
- package/cjs/model/gpt-4.d.ts +1 -1
- package/cjs/model/gpt-4o-2024-05-13.d.ts +1 -1
- package/cjs/model/gpt-4o-2024-08-06.d.ts +1 -1
- package/cjs/model/gpt-4o-mini-2024-07-18.d.ts +1 -1
- package/cjs/model/gpt-4o-mini.d.ts +1 -1
- package/cjs/model/gpt-4o.d.ts +1 -1
- package/dist/cl100k_base.js +1 -1
- package/dist/cl100k_base.js.map +1 -1
- package/dist/o200k_base.js +1 -1
- package/dist/o200k_base.js.map +1 -1
- package/dist/p50k_base.js +1 -1
- package/dist/p50k_base.js.map +1 -1
- package/dist/p50k_edit.js +1 -1
- package/dist/p50k_edit.js.map +1 -1
- package/dist/r50k_base.js +1 -1
- package/dist/r50k_base.js.map +1 -1
- package/esm/BytePairEncodingCore.d.ts +30 -17
- package/esm/BytePairEncodingCore.js +201 -133
- package/esm/BytePairEncodingCore.js.map +1 -1
- package/esm/GptEncoding.d.ts +6 -4
- package/esm/GptEncoding.js +49 -15
- package/esm/GptEncoding.js.map +1 -1
- package/esm/encoding/cl100k_base.d.ts +1 -1
- package/esm/encoding/o200k_base.d.ts +1 -1
- package/esm/encoding/p50k_base.d.ts +1 -1
- package/esm/encoding/p50k_edit.d.ts +1 -1
- package/esm/encoding/r50k_base.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-0125.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-0301.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-0613.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-1106.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-16k-0613.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-16k.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-instruct-0914.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo-instruct.d.ts +1 -1
- package/esm/model/gpt-3.5-turbo.d.ts +1 -1
- package/esm/model/gpt-4-0125-preview.d.ts +1 -1
- package/esm/model/gpt-4-0314.d.ts +1 -1
- package/esm/model/gpt-4-0613.d.ts +1 -1
- package/esm/model/gpt-4-1106-preview.d.ts +1 -1
- package/esm/model/gpt-4-32k-0314.d.ts +1 -1
- package/esm/model/gpt-4-32k-0613.d.ts +1 -1
- package/esm/model/gpt-4-32k.d.ts +1 -1
- package/esm/model/gpt-4-turbo-2024-04-09.d.ts +1 -1
- package/esm/model/gpt-4-turbo-preview.d.ts +1 -1
- package/esm/model/gpt-4-turbo.d.ts +1 -1
- package/esm/model/gpt-4-vision-preview.d.ts +1 -1
- package/esm/model/gpt-4.d.ts +1 -1
- package/esm/model/gpt-4o-2024-05-13.d.ts +1 -1
- package/esm/model/gpt-4o-2024-08-06.d.ts +1 -1
- package/esm/model/gpt-4o-mini-2024-07-18.d.ts +1 -1
- package/esm/model/gpt-4o-mini.d.ts +1 -1
- package/esm/model/gpt-4o.d.ts +1 -1
- package/package.json +1 -1
- package/src/BytePairEncodingCore.ts +260 -173
- package/src/GptEncoding.ts +82 -20
|
@@ -11,16 +11,30 @@ export interface BytePairEncodingConfig {
|
|
|
11
11
|
tokenSplitRegex: RegExp
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const emptyBuffer = new Uint8Array(0)
|
|
15
|
+
|
|
16
|
+
export const decoder = new TextDecoder('utf8')
|
|
17
|
+
|
|
14
18
|
export class BytePairEncodingCore {
|
|
15
|
-
readonly
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
readonly mergeableBytePairRankCount: number
|
|
20
|
+
/**
|
|
21
|
+
* an array where the index is the BPE rank,
|
|
22
|
+
* and the value is the string or the array of bytes that it decodes to
|
|
23
|
+
* it may contain holes if token is unused
|
|
24
|
+
*/
|
|
25
|
+
private bytePairRankDecoder: RawBytePairRanks
|
|
26
|
+
private bytePairNonUtfRankDecoder = new Map<number, Uint8Array>()
|
|
27
|
+
private bytePairNonUtfSortedEncoder: readonly [Uint8Array, number][]
|
|
28
|
+
/**
|
|
29
|
+
* a reverse map of the bytePairRankDecoder,
|
|
30
|
+
* where the key is the string and the value is the rank
|
|
31
|
+
* values that cannot be represented as a string are present in `bytePairNonUtfSortedEncoder`
|
|
32
|
+
*/
|
|
33
|
+
private bytePairStringRankEncoder: Map<string, number>
|
|
19
34
|
private tokenSplitRegex: RegExp
|
|
20
35
|
private specialTokensEncoder: Map<string, number>
|
|
21
36
|
private specialTokensDecoder: Map<number, string>
|
|
22
37
|
private specialTokenPatternRegex: RegExp
|
|
23
|
-
private stringDecoder: Map<string, number>
|
|
24
38
|
private textEncoder = new TextEncoder()
|
|
25
39
|
|
|
26
40
|
constructor({
|
|
@@ -28,23 +42,23 @@ export class BytePairEncodingCore {
|
|
|
28
42
|
specialTokenMapping: specialTokenEncoder,
|
|
29
43
|
tokenSplitRegex,
|
|
30
44
|
}: BytePairEncodingConfig) {
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
45
|
+
this.bytePairRankDecoder = bytePairEncoder
|
|
46
|
+
this.bytePairStringRankEncoder = new Map<string, number>()
|
|
33
47
|
|
|
34
48
|
// size without array holes (which may be present in the encoder)
|
|
35
|
-
this.
|
|
49
|
+
this.mergeableBytePairRankCount = Object.keys(bytePairEncoder).length
|
|
36
50
|
const binaryLookup: [Uint8Array, number][] = []
|
|
37
51
|
// forEach skips array holes:
|
|
38
52
|
bytePairEncoder.forEach((value, rank) => {
|
|
39
53
|
if (typeof value === 'string') {
|
|
40
|
-
this.
|
|
54
|
+
this.bytePairStringRankEncoder.set(value, rank)
|
|
41
55
|
return
|
|
42
56
|
}
|
|
43
57
|
const byteArray = new Uint8Array(value)
|
|
44
58
|
binaryLookup.push([byteArray, rank])
|
|
45
|
-
this.
|
|
59
|
+
this.bytePairNonUtfRankDecoder.set(rank, byteArray)
|
|
46
60
|
})
|
|
47
|
-
this.
|
|
61
|
+
this.bytePairNonUtfSortedEncoder = binaryLookup.sort((a, b) =>
|
|
48
62
|
compareUint8Arrays(a[0], b[0]),
|
|
49
63
|
)
|
|
50
64
|
this.specialTokensEncoder = specialTokenEncoder ?? new Map<string, number>()
|
|
@@ -53,85 +67,18 @@ export class BytePairEncodingCore {
|
|
|
53
67
|
: new Map<number, string>()
|
|
54
68
|
this.tokenSplitRegex = tokenSplitRegex
|
|
55
69
|
|
|
56
|
-
const
|
|
57
|
-
|
|
70
|
+
const escapedSpecialTokens = [...this.specialTokensEncoder.keys()].map(
|
|
71
|
+
escapeRegExp,
|
|
72
|
+
)
|
|
73
|
+
const allSpecialTokensRegex = escapedSpecialTokens.join('|')
|
|
58
74
|
try {
|
|
59
|
-
this.specialTokenPatternRegex = new RegExp(
|
|
75
|
+
this.specialTokenPatternRegex = new RegExp(allSpecialTokensRegex)
|
|
60
76
|
} catch {
|
|
61
77
|
throw new Error('Invalid regular expression pattern.')
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
return this.stringDecoder.get(key)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getBpeRankFromStringOrThrow(key: string): number {
|
|
70
|
-
const value = this.getBpeRankFromString(key)
|
|
71
|
-
if (value === undefined) {
|
|
72
|
-
throw new Error(
|
|
73
|
-
`The byte-pair encoding does not contain a value for: ${key}`,
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
return value
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getBpeRankFromBytes(key: Uint8Array): number | undefined {
|
|
80
|
-
const keyAsString = tryConvertToString(key)
|
|
81
|
-
|
|
82
|
-
if (keyAsString !== undefined) {
|
|
83
|
-
return this.getBpeRankFromString(keyAsString)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Perform binary search on the binary keys
|
|
87
|
-
const index = this.binarySearch(key)
|
|
88
|
-
if (index !== -1) {
|
|
89
|
-
return this.bytePairEncoderSortedLookup[index]![1]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return undefined
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
getBpeRankFromBytesOrThrow(key: Uint8Array): number {
|
|
96
|
-
const value = this.getBpeRankFromBytes(key)
|
|
97
|
-
if (value === undefined) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`The byte-pair encoding does not contain a value for: ${key.toString()}`,
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
return value
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Binary search on the binary keys
|
|
106
|
-
binarySearch(key: Uint8Array): number {
|
|
107
|
-
let low = 0
|
|
108
|
-
let high = this.bytePairEncoderSortedLookup.length - 1
|
|
109
|
-
|
|
110
|
-
while (low <= high) {
|
|
111
|
-
// eslint-disable-next-line no-bitwise
|
|
112
|
-
const mid = (low + high) >>> 1
|
|
113
|
-
const midKey = this.bytePairEncoderSortedLookup[mid]![0]
|
|
114
|
-
let cmp = 0
|
|
115
|
-
for (let i = 0; i < Math.min(midKey.length, key.length); i++) {
|
|
116
|
-
cmp = midKey[i]! - key[i]!
|
|
117
|
-
if (cmp !== 0) break
|
|
118
|
-
}
|
|
119
|
-
if (cmp === 0) {
|
|
120
|
-
cmp = midKey.length - key.length
|
|
121
|
-
}
|
|
122
|
-
if (cmp === 0) {
|
|
123
|
-
return mid
|
|
124
|
-
}
|
|
125
|
-
if (cmp < 0) {
|
|
126
|
-
low = mid + 1
|
|
127
|
-
} else {
|
|
128
|
-
high = mid - 1
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return -1
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
*encodeNative(
|
|
81
|
+
*encodeNativeGenerator(
|
|
135
82
|
text: string,
|
|
136
83
|
allowedSpecial?: Set<string>,
|
|
137
84
|
): Generator<number[], number, undefined> {
|
|
@@ -143,7 +90,6 @@ export class BytePairEncodingCore {
|
|
|
143
90
|
text,
|
|
144
91
|
allowedSpecial,
|
|
145
92
|
startIndex,
|
|
146
|
-
this.specialTokenPatternRegex,
|
|
147
93
|
)
|
|
148
94
|
|
|
149
95
|
const endIndex =
|
|
@@ -186,35 +132,56 @@ export class BytePairEncodingCore {
|
|
|
186
132
|
return lastTokenLength
|
|
187
133
|
}
|
|
188
134
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
startIndex: number,
|
|
193
|
-
specialRegex: RegExp,
|
|
194
|
-
): number | undefined {
|
|
195
|
-
let searchIndex = startIndex
|
|
135
|
+
encodeNative(text: string, allowedSpecial?: Set<string>): number[] {
|
|
136
|
+
let startIndex = 0
|
|
137
|
+
const tokensArray: number[] = [] // Flat list to collect the tokens
|
|
196
138
|
|
|
197
139
|
// eslint-disable-next-line no-constant-condition
|
|
198
140
|
while (true) {
|
|
199
|
-
const
|
|
200
|
-
text
|
|
141
|
+
const nextSpecialStartIndex = this.findNextSpecialStartIndex(
|
|
142
|
+
text,
|
|
143
|
+
allowedSpecial,
|
|
144
|
+
startIndex,
|
|
201
145
|
)
|
|
202
146
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
147
|
+
const endIndex =
|
|
148
|
+
nextSpecialStartIndex !== undefined
|
|
149
|
+
? nextSpecialStartIndex
|
|
150
|
+
: text.length
|
|
206
151
|
|
|
207
|
-
const
|
|
152
|
+
const textSegment = text.slice(startIndex, endIndex - startIndex)
|
|
208
153
|
|
|
209
|
-
|
|
210
|
-
|
|
154
|
+
for (const [match] of textSegment.matchAll(this.tokenSplitRegex)) {
|
|
155
|
+
const token = this.getBpeRankFromString(match)
|
|
156
|
+
if (token !== undefined) {
|
|
157
|
+
tokensArray.push(token)
|
|
158
|
+
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const tokens = this.bytePairEncode(match)
|
|
163
|
+
tokensArray.push(...tokens)
|
|
211
164
|
}
|
|
212
165
|
|
|
213
|
-
|
|
166
|
+
if (nextSpecialStartIndex !== undefined) {
|
|
167
|
+
const specialToken = text.slice(Math.max(0, nextSpecialStartIndex))
|
|
168
|
+
const specialTokenValue = this.specialTokensEncoder.get(specialToken)
|
|
169
|
+
if (specialTokenValue === undefined) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Special token "${specialToken}" is not in the special token encoder.`,
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
tokensArray.push(specialTokenValue)
|
|
175
|
+
startIndex = nextSpecialStartIndex + specialToken.length
|
|
176
|
+
} else {
|
|
177
|
+
break
|
|
178
|
+
}
|
|
214
179
|
}
|
|
180
|
+
|
|
181
|
+
return tokensArray
|
|
215
182
|
}
|
|
216
183
|
|
|
217
|
-
*
|
|
184
|
+
*decodeNativeGenerator(
|
|
218
185
|
tokens: Iterable<number>,
|
|
219
186
|
): Generator<Uint8Array | string, void, void> {
|
|
220
187
|
for (const token of tokens) {
|
|
@@ -225,7 +192,36 @@ export class BytePairEncodingCore {
|
|
|
225
192
|
}
|
|
226
193
|
}
|
|
227
194
|
|
|
228
|
-
|
|
195
|
+
decodeNative(tokens: Iterable<number>): string {
|
|
196
|
+
let decoded = ''
|
|
197
|
+
let intBuffer = emptyBuffer
|
|
198
|
+
|
|
199
|
+
for (const token of tokens) {
|
|
200
|
+
const tokenBytes = this.tryDecodeToken(token)
|
|
201
|
+
if (tokenBytes === undefined) {
|
|
202
|
+
throw new Error(`Token ${token} is not in the byte pair encoder.`)
|
|
203
|
+
}
|
|
204
|
+
if (typeof tokenBytes === 'string') {
|
|
205
|
+
if (intBuffer !== emptyBuffer) {
|
|
206
|
+
decoded += decoder.decode(intBuffer, { stream: true })
|
|
207
|
+
intBuffer = emptyBuffer
|
|
208
|
+
}
|
|
209
|
+
decoded += tokenBytes
|
|
210
|
+
} else {
|
|
211
|
+
const newBuffer = new Uint8Array(intBuffer.length + tokenBytes.length)
|
|
212
|
+
newBuffer.set(intBuffer)
|
|
213
|
+
newBuffer.set(tokenBytes, intBuffer.length)
|
|
214
|
+
intBuffer = newBuffer
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (intBuffer !== emptyBuffer) {
|
|
219
|
+
decoded += decoder.decode(intBuffer, { stream: true })
|
|
220
|
+
}
|
|
221
|
+
return decoded
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async *decodeNativeAsyncIterable(
|
|
229
225
|
tokens: AsyncIterable<number>,
|
|
230
226
|
): AsyncGenerator<Uint8Array | string> {
|
|
231
227
|
for await (const token of tokens) {
|
|
@@ -236,13 +232,110 @@ export class BytePairEncodingCore {
|
|
|
236
232
|
}
|
|
237
233
|
}
|
|
238
234
|
|
|
239
|
-
|
|
240
|
-
|
|
235
|
+
private getBpeRankFromString(key: string): number | undefined {
|
|
236
|
+
return this.bytePairStringRankEncoder.get(key)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private getBpeRankFromStringOrThrow(key: string): number {
|
|
240
|
+
const value = this.getBpeRankFromString(key)
|
|
241
|
+
if (value === undefined) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`The byte-pair encoding does not contain a value for: ${key}`,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
return value
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private getBpeRankFromBytes(key: Uint8Array): number | undefined {
|
|
250
|
+
const keyAsString = tryConvertToString(key)
|
|
251
|
+
|
|
252
|
+
if (keyAsString !== undefined) {
|
|
253
|
+
return this.getBpeRankFromString(keyAsString)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Perform binary search on the binary keys
|
|
257
|
+
const index = this.binarySearch(key)
|
|
258
|
+
if (index !== -1) {
|
|
259
|
+
return this.bytePairNonUtfSortedEncoder[index]![1]
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return undefined
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private getBpeRankFromBytesOrThrow(key: Uint8Array): number {
|
|
266
|
+
const value = this.getBpeRankFromBytes(key)
|
|
267
|
+
if (value === undefined) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`The byte-pair encoding does not contain a value for: ${key.toString()}`,
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
return value
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Binary search on the binary keys
|
|
276
|
+
private binarySearch(key: Uint8Array): number {
|
|
277
|
+
let low = 0
|
|
278
|
+
let high = this.bytePairNonUtfSortedEncoder.length - 1
|
|
279
|
+
|
|
280
|
+
while (low <= high) {
|
|
281
|
+
// eslint-disable-next-line no-bitwise
|
|
282
|
+
const mid = (low + high) >>> 1
|
|
283
|
+
const midKey = this.bytePairNonUtfSortedEncoder[mid]![0]
|
|
284
|
+
let cmp = 0
|
|
285
|
+
const maxLength = Math.min(midKey.length, key.length)
|
|
286
|
+
for (let i = 0; i < maxLength; i++) {
|
|
287
|
+
cmp = midKey[i]! - key[i]!
|
|
288
|
+
if (cmp !== 0) break
|
|
289
|
+
}
|
|
290
|
+
if (cmp === 0) {
|
|
291
|
+
cmp = midKey.length - key.length
|
|
292
|
+
}
|
|
293
|
+
if (cmp === 0) {
|
|
294
|
+
return mid
|
|
295
|
+
}
|
|
296
|
+
if (cmp < 0) {
|
|
297
|
+
low = mid + 1
|
|
298
|
+
} else {
|
|
299
|
+
high = mid - 1
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return -1
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private findNextSpecialStartIndex(
|
|
306
|
+
text: string,
|
|
307
|
+
allowedSpecial: Set<string> | undefined,
|
|
308
|
+
startIndex: number,
|
|
309
|
+
): number | undefined {
|
|
310
|
+
let searchIndex = startIndex
|
|
311
|
+
|
|
312
|
+
// eslint-disable-next-line no-constant-condition
|
|
313
|
+
while (true) {
|
|
314
|
+
const nextSpecialMatch = this.specialTokenPatternRegex.exec(
|
|
315
|
+
text.slice(Math.max(0, searchIndex)),
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
if (!nextSpecialMatch) {
|
|
319
|
+
return undefined
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const specialToken = nextSpecialMatch[0]
|
|
323
|
+
|
|
324
|
+
if (allowedSpecial?.has(specialToken)) {
|
|
325
|
+
return nextSpecialMatch.index + searchIndex
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
searchIndex = nextSpecialMatch.index + searchIndex + 1
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private tryDecodeToken(tokenRank: number): Uint8Array | string | undefined {
|
|
333
|
+
const value = this.bytePairRankDecoder[tokenRank]
|
|
241
334
|
if (typeof value === 'string') {
|
|
242
335
|
return value
|
|
243
336
|
}
|
|
244
337
|
if (typeof value === 'object') {
|
|
245
|
-
const fromBinary = this.
|
|
338
|
+
const fromBinary = this.bytePairNonUtfRankDecoder.get(tokenRank)
|
|
246
339
|
if (fromBinary) {
|
|
247
340
|
return fromBinary
|
|
248
341
|
}
|
|
@@ -250,108 +343,102 @@ export class BytePairEncodingCore {
|
|
|
250
343
|
return this.specialTokensDecoder.get(tokenRank)
|
|
251
344
|
}
|
|
252
345
|
|
|
253
|
-
bytePairEncode(input: string): number[] {
|
|
346
|
+
private bytePairEncode(input: string): number[] {
|
|
254
347
|
if (input.length === 1 && isAscii(input.codePointAt(0)!)) {
|
|
255
348
|
return [this.getBpeRankFromStringOrThrow(input)]
|
|
256
349
|
}
|
|
257
350
|
|
|
258
351
|
const inputBytes = this.textEncoder.encode(input)
|
|
259
352
|
|
|
260
|
-
return this.bytePairMerge(inputBytes
|
|
261
|
-
const key = inputBytes.subarray(start, end)
|
|
262
|
-
return this.getBpeRankFromBytesOrThrow(key)
|
|
263
|
-
})
|
|
353
|
+
return this.bytePairMerge(inputBytes)
|
|
264
354
|
}
|
|
265
355
|
|
|
266
|
-
bytePairMerge(
|
|
356
|
+
private bytePairMerge(
|
|
267
357
|
// Input array of bytes to process
|
|
268
358
|
piece: Uint8Array,
|
|
269
|
-
// Function to apply to each final segment after merging
|
|
270
|
-
getByteForRange: (start: number, end: number) => number,
|
|
271
359
|
): number[] {
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
360
|
+
// 'starts' holds the start indices of each partition
|
|
361
|
+
const starts: number[] = []
|
|
362
|
+
// 'ranks' holds the BPE ranks of each partition pair
|
|
363
|
+
const ranks: number[] = []
|
|
364
|
+
|
|
365
|
+
// Helper function to get the rank of a byte pair starting at 'startIndex'
|
|
366
|
+
const getRank = (
|
|
367
|
+
startIndex: number,
|
|
368
|
+
pairStart = starts[startIndex],
|
|
369
|
+
pairEnd = starts[startIndex + 2],
|
|
370
|
+
): number => {
|
|
371
|
+
if (pairEnd === undefined) {
|
|
372
|
+
// No valid pair exists
|
|
373
|
+
return Number.POSITIVE_INFINITY
|
|
285
374
|
}
|
|
286
375
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
const key = piece.subarray(
|
|
290
|
-
partitions[startIndex]!.start,
|
|
291
|
-
partitions[startIndex + skip + 2]!.start,
|
|
292
|
-
)
|
|
376
|
+
// Extract the byte pair
|
|
377
|
+
const key = piece.subarray(pairStart, pairEnd)
|
|
293
378
|
|
|
294
|
-
// Retrieve the rank of this byte pair
|
|
295
|
-
|
|
379
|
+
// Retrieve the BPE rank of this byte pair (if it exists)
|
|
380
|
+
const rank = this.getBpeRankFromBytes(key)
|
|
381
|
+
return rank ?? Number.POSITIVE_INFINITY
|
|
296
382
|
}
|
|
297
383
|
|
|
298
|
-
// Initialize the
|
|
299
|
-
for (let i = 0; i
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
384
|
+
// Initialize the 'starts' array with all possible start indices
|
|
385
|
+
for (let i = 0; i <= piece.length; i++) {
|
|
386
|
+
starts.push(i)
|
|
387
|
+
if (i < piece.length - 1) {
|
|
388
|
+
// Initialize the BPE values for all adjacent pairs
|
|
389
|
+
ranks.push(getRank(i, i, i + 2))
|
|
390
|
+
} else {
|
|
391
|
+
// Initialize BPE values to infinity for the last pair
|
|
392
|
+
ranks.push(Number.POSITIVE_INFINITY)
|
|
305
393
|
}
|
|
306
394
|
}
|
|
307
395
|
|
|
308
396
|
// Iteratively merge byte pairs until no more useful merges can be done
|
|
309
|
-
while (
|
|
310
|
-
let
|
|
311
|
-
let
|
|
312
|
-
|
|
313
|
-
// Find the partition with the minimum rank
|
|
314
|
-
let i = 0
|
|
315
|
-
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
397
|
+
while (starts.length > 1) {
|
|
398
|
+
let lowestRank = Number.POSITIVE_INFINITY
|
|
399
|
+
let lowestPartitionIndex = -1
|
|
400
|
+
|
|
401
|
+
// Find the partition with the minimum rank
|
|
402
|
+
for (let i = 0; i < ranks.length - 1; i++) {
|
|
403
|
+
const rank = ranks[i]!
|
|
404
|
+
if (rank < lowestRank) {
|
|
405
|
+
lowestRank = rank
|
|
406
|
+
lowestPartitionIndex = i
|
|
319
407
|
}
|
|
320
|
-
i++
|
|
321
408
|
}
|
|
322
409
|
|
|
323
410
|
// If no valid pair is left to merge, exit the loop
|
|
324
|
-
if (
|
|
411
|
+
if (
|
|
412
|
+
lowestRank === Number.POSITIVE_INFINITY ||
|
|
413
|
+
lowestPartitionIndex === -1
|
|
414
|
+
) {
|
|
325
415
|
break
|
|
326
416
|
}
|
|
327
417
|
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
418
|
+
// Merge the pair at 'lowestPartitionIndex' by removing the next start index
|
|
419
|
+
starts.splice(lowestPartitionIndex + 1, 1)
|
|
420
|
+
// Remove the BPE value of the merged pair
|
|
421
|
+
ranks.splice(lowestPartitionIndex, 1)
|
|
331
422
|
|
|
332
|
-
// Update the
|
|
333
|
-
|
|
334
|
-
partitions[minRankIdx - 1]!.rank =
|
|
335
|
-
getRank(minRankIdx - 1, 1) ?? Number.POSITIVE_INFINITY
|
|
336
|
-
}
|
|
423
|
+
// Update the current merged pair's rank
|
|
424
|
+
ranks[lowestPartitionIndex] = getRank(lowestPartitionIndex)
|
|
337
425
|
|
|
338
|
-
//
|
|
339
|
-
|
|
426
|
+
// Update the rank of the previous pair, if it exists
|
|
427
|
+
if (lowestPartitionIndex > 0) {
|
|
428
|
+
ranks[lowestPartitionIndex - 1] = getRank(lowestPartitionIndex - 1)
|
|
429
|
+
}
|
|
340
430
|
}
|
|
341
431
|
|
|
342
432
|
// Create the final output by applying the transform function to each partitioned range
|
|
343
433
|
const output: number[] = []
|
|
344
|
-
for (let i = 0; i <
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
// end index
|
|
350
|
-
partitions[i + 1]!.start,
|
|
351
|
-
),
|
|
434
|
+
for (let i = 0; i < starts.length - 1; i++) {
|
|
435
|
+
const pairStart = starts[i]
|
|
436
|
+
const pairEnd = starts[i + 1]
|
|
437
|
+
const bpeValue = this.getBpeRankFromBytesOrThrow(
|
|
438
|
+
piece.subarray(pairStart, pairEnd),
|
|
352
439
|
)
|
|
440
|
+
output.push(bpeValue)
|
|
353
441
|
}
|
|
354
|
-
|
|
355
442
|
return output
|
|
356
443
|
}
|
|
357
444
|
}
|