gpt-tokenizer 2.0.0 → 2.1.0

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.
Files changed (133) hide show
  1. package/README.md +54 -8
  2. package/cjs/GptEncoding.d.ts +33 -6
  3. package/cjs/GptEncoding.js +79 -34
  4. package/cjs/GptEncoding.js.map +1 -1
  5. package/cjs/GptEncoding.test.js +54 -0
  6. package/cjs/GptEncoding.test.js.map +1 -1
  7. package/cjs/encoding/cl100k_base.d.ts +3 -3
  8. package/cjs/encoding/cl100k_base.js +6 -4
  9. package/cjs/encoding/cl100k_base.js.map +1 -1
  10. package/cjs/encoding/p50k_base.d.ts +2 -2
  11. package/cjs/encoding/p50k_base.js +4 -4
  12. package/cjs/encoding/p50k_base.js.map +1 -1
  13. package/cjs/encoding/p50k_edit.d.ts +2 -2
  14. package/cjs/encoding/p50k_edit.js +4 -4
  15. package/cjs/encoding/p50k_edit.js.map +1 -1
  16. package/cjs/encoding/r50k_base.d.ts +2 -2
  17. package/cjs/encoding/r50k_base.js +4 -4
  18. package/cjs/encoding/r50k_base.js.map +1 -1
  19. package/cjs/mapping.d.ts +37 -0
  20. package/cjs/mapping.js +34 -2
  21. package/cjs/mapping.js.map +1 -1
  22. package/cjs/model/gpt-3.5-turbo-0301.d.ts +6 -0
  23. package/cjs/model/gpt-3.5-turbo-0301.js +38 -0
  24. package/cjs/model/gpt-3.5-turbo-0301.js.map +1 -0
  25. package/cjs/model/gpt-3.5-turbo.d.ts +6 -1
  26. package/cjs/model/gpt-3.5-turbo.js +21 -1
  27. package/cjs/model/gpt-3.5-turbo.js.map +1 -1
  28. package/cjs/model/gpt-4-0314.d.ts +6 -0
  29. package/cjs/model/gpt-4-0314.js +38 -0
  30. package/cjs/model/gpt-4-0314.js.map +1 -0
  31. package/cjs/model/gpt-4-32k-0314.d.ts +6 -0
  32. package/cjs/model/gpt-4-32k-0314.js +38 -0
  33. package/cjs/model/gpt-4-32k-0314.js.map +1 -0
  34. package/cjs/model/gpt-4-32k.d.ts +6 -0
  35. package/cjs/model/gpt-4-32k.js +38 -0
  36. package/cjs/model/gpt-4-32k.js.map +1 -0
  37. package/cjs/model/gpt-4.d.ts +6 -1
  38. package/cjs/model/gpt-4.js +21 -1
  39. package/cjs/model/gpt-4.js.map +1 -1
  40. package/cjs/modelParams.d.ts +5 -4
  41. package/cjs/modelParams.js +7 -4
  42. package/cjs/modelParams.js.map +1 -1
  43. package/cjs/specialTokens.d.ts +3 -0
  44. package/cjs/specialTokens.js +4 -1
  45. package/cjs/specialTokens.js.map +1 -1
  46. package/cjs/util.d.ts +2 -0
  47. package/cjs/util.js +19 -0
  48. package/cjs/util.js.map +1 -0
  49. package/dist/cl100k_base.js +1 -1
  50. package/dist/cl100k_base.js.map +1 -1
  51. package/dist/p50k_base.js +1 -1
  52. package/dist/p50k_base.js.map +1 -1
  53. package/dist/p50k_edit.js +1 -1
  54. package/dist/p50k_edit.js.map +1 -1
  55. package/dist/r50k_base.js +1 -1
  56. package/dist/r50k_base.js.map +1 -1
  57. package/esm/GptEncoding.d.ts +33 -6
  58. package/esm/GptEncoding.js +81 -36
  59. package/esm/GptEncoding.js.map +1 -1
  60. package/esm/GptEncoding.test.js +55 -1
  61. package/esm/GptEncoding.test.js.map +1 -1
  62. package/esm/codegen/generateByModel.js +19 -0
  63. package/esm/codegen/generateByModel.js.map +1 -0
  64. package/esm/{generateJsModels.js → codegen/generateJsEncodings.js} +4 -4
  65. package/esm/codegen/generateJsEncodings.js.map +1 -0
  66. package/esm/encoding/cl100k_base.d.ts +3 -3
  67. package/esm/encoding/cl100k_base.js +3 -3
  68. package/esm/encoding/cl100k_base.js.map +1 -1
  69. package/esm/encoding/p50k_base.d.ts +2 -2
  70. package/esm/encoding/p50k_base.js +1 -1
  71. package/esm/encoding/p50k_base.js.map +1 -1
  72. package/esm/encoding/p50k_edit.d.ts +2 -2
  73. package/esm/encoding/p50k_edit.js +1 -1
  74. package/esm/encoding/p50k_edit.js.map +1 -1
  75. package/esm/encoding/r50k_base.d.ts +2 -2
  76. package/esm/encoding/r50k_base.js +1 -1
  77. package/esm/encoding/r50k_base.js.map +1 -1
  78. package/esm/mapping.d.ts +37 -0
  79. package/esm/mapping.js +32 -0
  80. package/esm/mapping.js.map +1 -1
  81. package/esm/model/gpt-3.5-turbo-0301.d.ts +6 -0
  82. package/esm/model/gpt-3.5-turbo-0301.js +11 -0
  83. package/esm/model/gpt-3.5-turbo-0301.js.map +1 -0
  84. package/esm/model/gpt-3.5-turbo.d.ts +6 -1
  85. package/esm/model/gpt-3.5-turbo.js +10 -1
  86. package/esm/model/gpt-3.5-turbo.js.map +1 -1
  87. package/esm/model/gpt-4-0314.d.ts +6 -0
  88. package/esm/model/gpt-4-0314.js +11 -0
  89. package/esm/model/gpt-4-0314.js.map +1 -0
  90. package/esm/model/gpt-4-32k-0314.d.ts +6 -0
  91. package/esm/model/gpt-4-32k-0314.js +11 -0
  92. package/esm/model/gpt-4-32k-0314.js.map +1 -0
  93. package/esm/model/gpt-4-32k.d.ts +6 -0
  94. package/esm/model/gpt-4-32k.js +11 -0
  95. package/esm/model/gpt-4-32k.js.map +1 -0
  96. package/esm/model/gpt-4.d.ts +6 -1
  97. package/esm/model/gpt-4.js +10 -1
  98. package/esm/model/gpt-4.js.map +1 -1
  99. package/esm/modelParams.d.ts +5 -4
  100. package/esm/modelParams.js +6 -3
  101. package/esm/modelParams.js.map +1 -1
  102. package/esm/specialTokens.d.ts +3 -0
  103. package/esm/specialTokens.js +3 -0
  104. package/esm/specialTokens.js.map +1 -1
  105. package/esm/util.d.ts +2 -0
  106. package/esm/util.js +14 -0
  107. package/esm/util.js.map +1 -0
  108. package/package.json +8 -6
  109. package/src/GptEncoding.test.ts +78 -1
  110. package/src/GptEncoding.ts +125 -44
  111. package/src/__snapshots__/GptEncoding.test.ts.snap +811 -0
  112. package/src/codegen/generateByModel.ts +35 -0
  113. package/src/{generateJsModels.ts → codegen/generateJsEncodings.ts} +3 -3
  114. package/src/encoding/cl100k_base.ts +5 -1
  115. package/src/encoding/p50k_base.ts +1 -1
  116. package/src/encoding/p50k_edit.ts +1 -1
  117. package/src/encoding/r50k_base.ts +1 -1
  118. package/src/mapping.ts +42 -0
  119. package/src/model/gpt-3.5-turbo-0301.ts +32 -0
  120. package/src/model/gpt-3.5-turbo.ts +32 -1
  121. package/src/model/gpt-4-0314.ts +32 -0
  122. package/src/model/gpt-4-32k-0314.ts +32 -0
  123. package/src/model/gpt-4-32k.ts +32 -0
  124. package/src/model/gpt-4.ts +32 -1
  125. package/src/modelParams.ts +17 -10
  126. package/src/specialTokens.ts +3 -0
  127. package/src/util.ts +15 -0
  128. package/esm/generateByModel.js +0 -13
  129. package/esm/generateByModel.js.map +0 -1
  130. package/esm/generateJsModels.js.map +0 -1
  131. package/src/generateByModel.ts +0 -20
  132. /package/esm/{generateByModel.d.ts → codegen/generateByModel.d.ts} +0 -0
  133. /package/esm/{generateJsModels.d.ts → codegen/generateJsEncodings.d.ts} +0 -0
@@ -1,16 +1,16 @@
1
1
  /* eslint-disable no-param-reassign */
2
2
  import { BytePairEncodingCore } from './BytePairEncodingCore.js'
3
- import { escapeRegExp } from './escapeRegExp.js'
4
3
  import {
5
4
  type EncodingName,
6
5
  type ModelName,
6
+ chatModelParams,
7
7
  modelToEncodingMap,
8
8
  } from './mapping.js'
9
9
  import {
10
+ type EncodingParams,
10
11
  type GetMergeableRanksAsyncFn,
11
12
  type GetMergeableRanksFn,
12
- type ModelParams,
13
- getModelParams,
13
+ getEncodingParams,
14
14
  getModelParamsAsync,
15
15
  } from './modelParams.js'
16
16
  import {
@@ -19,10 +19,29 @@ import {
19
19
  FimMiddle,
20
20
  FimPrefix,
21
21
  FimSuffix,
22
+ ImEnd,
23
+ ImSep,
24
+ ImStart,
22
25
  } from './specialTokens.js'
23
26
  import { endsWithIncompleteUtfPairSurrogate } from './utfUtil.js'
27
+ import { getMaxValueFromMap, getSpecialTokenRegex } from './util.js'
24
28
 
25
- export const DISALLOW_ALL_SPECIAL_TOKENS = 'all'
29
+ export const ALL_SPECIAL_TOKENS = 'all'
30
+
31
+ export interface EncodeOptions {
32
+ allowedSpecial?: Set<string>
33
+ disallowedSpecial?: Set<string>
34
+ }
35
+
36
+ export interface ChatMessage {
37
+ role?: 'system' | 'user' | 'assistant'
38
+ name?: string
39
+ content: string
40
+ }
41
+
42
+ export interface EncodeChatOptions {
43
+ primeWithAssistantResponse?: string
44
+ }
26
45
 
27
46
  export class GptEncoding {
28
47
  static EndOfPrompt = EndOfPrompt
@@ -32,6 +51,7 @@ export class GptEncoding {
32
51
  static FimSuffix = FimSuffix
33
52
 
34
53
  decoder = new TextDecoder('utf8')
54
+ modelName?: ModelName
35
55
  private bytePairEncodingCoreProcessor: BytePairEncodingCore
36
56
  private specialTokenMapping: Map<string, number>
37
57
 
@@ -40,10 +60,11 @@ export class GptEncoding {
40
60
  mergeableBytePairRanks,
41
61
  specialTokenMapping,
42
62
  expectedVocabularySize,
43
- }: ModelParams) {
63
+ modelName,
64
+ }: EncodingParams) {
44
65
  const maxTokenValue = Math.max(
45
- GptEncoding.getMaxValueFromMap(mergeableBytePairRanks),
46
- GptEncoding.getMaxValueFromMap(specialTokenMapping),
66
+ getMaxValueFromMap(mergeableBytePairRanks),
67
+ getMaxValueFromMap(specialTokenMapping),
47
68
  )
48
69
  this.specialTokenMapping = specialTokenMapping
49
70
 
@@ -76,13 +97,14 @@ export class GptEncoding {
76
97
  this.decodeGenerator = this.decodeGenerator.bind(this)
77
98
  this.decodeAsyncGenerator = this.decodeAsyncGenerator.bind(this)
78
99
  this.isWithinTokenLimit = this.isWithinTokenLimit.bind(this)
100
+ this.modelName = modelName
79
101
  }
80
102
 
81
103
  static getEncodingApi(
82
104
  encodingName: EncodingName,
83
105
  getMergeableRanks: GetMergeableRanksFn,
84
106
  ): GptEncoding {
85
- const modelParams = getModelParams(encodingName, getMergeableRanks)
107
+ const modelParams = getEncodingParams(encodingName, getMergeableRanks)
86
108
  return new GptEncoding(modelParams)
87
109
  }
88
110
 
@@ -91,7 +113,8 @@ export class GptEncoding {
91
113
  getMergeableRanks: GetMergeableRanksFn,
92
114
  ): GptEncoding {
93
115
  const encodingName = modelToEncodingMap[modelName]
94
- return GptEncoding.getEncodingApi(encodingName, getMergeableRanks)
116
+ const modelParams = getEncodingParams(encodingName, getMergeableRanks)
117
+ return new GptEncoding({ ...modelParams, modelName })
95
118
  }
96
119
 
97
120
  static async getEncodingApiAsync(
@@ -110,43 +133,34 @@ export class GptEncoding {
110
133
  getMergeableRanks: GetMergeableRanksAsyncFn,
111
134
  ): Promise<GptEncoding> {
112
135
  const encodingName = modelToEncodingMap[modelName]
113
- return GptEncoding.getEncodingApiAsync(encodingName, getMergeableRanks)
114
- }
115
-
116
- private static getMaxValueFromMap(map: Map<unknown, number>): number {
117
- let max = 0
118
- map.forEach((val) => {
119
- max = Math.max(max, val)
120
- })
121
- return max
122
- }
123
-
124
- private static specialTokenRegex(tokens: Set<string>): RegExp {
125
- const escapedTokens = [...tokens].map(escapeRegExp)
126
- const inner = escapedTokens.join('|')
127
- return new RegExp(`(${inner})`)
136
+ const modelParams = await getModelParamsAsync(
137
+ encodingName,
138
+ getMergeableRanks,
139
+ )
140
+ return new GptEncoding({ ...modelParams, modelName })
128
141
  }
129
142
 
130
143
  encodeGenerator(
131
144
  lineToEncode: string,
132
- allowedSpecial = new Set<string>(),
133
- disallowedSpecial: Set<string> = new Set<string>([
134
- DISALLOW_ALL_SPECIAL_TOKENS,
135
- ]),
136
- ): Generator<number[], number> {
145
+ {
146
+ allowedSpecial = new Set<string>(),
147
+ disallowedSpecial = new Set<string>([ALL_SPECIAL_TOKENS]),
148
+ }: EncodeOptions = {},
149
+ ): Generator<number[], number, undefined> {
137
150
  const specialTokensSet = new Set<string>(this.specialTokenMapping.keys())
138
151
 
139
- if (disallowedSpecial.has(DISALLOW_ALL_SPECIAL_TOKENS)) {
152
+ if (disallowedSpecial.has(ALL_SPECIAL_TOKENS)) {
140
153
  disallowedSpecial = new Set<string>(specialTokensSet)
154
+ allowedSpecial.forEach((val) => disallowedSpecial.delete(val))
141
155
  disallowedSpecial.forEach((val) => allowedSpecial.delete(val))
142
156
  }
143
157
 
144
- if (allowedSpecial.has(DISALLOW_ALL_SPECIAL_TOKENS)) {
158
+ if (allowedSpecial.has(ALL_SPECIAL_TOKENS)) {
145
159
  allowedSpecial = specialTokensSet
146
160
  }
147
161
 
148
162
  if (disallowedSpecial.size > 0) {
149
- const regexPattern = GptEncoding.specialTokenRegex(disallowedSpecial)
163
+ const regexPattern = getSpecialTokenRegex(disallowedSpecial)
150
164
  const match = lineToEncode.match(regexPattern)
151
165
  if (match !== null) {
152
166
  throw new Error(`Disallowed special token found: ${match[0]}`)
@@ -159,23 +173,90 @@ export class GptEncoding {
159
173
  )
160
174
  }
161
175
 
162
- encode(
163
- lineToEncode: string,
164
- allowedSpecial = new Set<string>(),
165
- disallowedSpecial: Set<string> = new Set<string>([
166
- DISALLOW_ALL_SPECIAL_TOKENS,
167
- ]),
168
- ): number[] {
169
- return [
170
- ...this.encodeGenerator(lineToEncode, allowedSpecial, disallowedSpecial),
171
- ].flat()
176
+ encode(lineToEncode: string, encodeOptions: EncodeOptions = {}): number[] {
177
+ return [...this.encodeGenerator(lineToEncode, encodeOptions)].flat()
178
+ }
179
+
180
+ /**
181
+ * Progressively tokenizes an OpenAI chat.
182
+ * Warning: gpt-3.5-turbo and gpt-4 chat format may change over time.
183
+ * Returns tokens assuming the 'gpt-3.5-turbo-0301' / 'gpt-4-0314' format.
184
+ * Based on OpenAI's guidelines: https://github.com/openai/openai-python/blob/main/chatml.md
185
+ * Also mentioned in section 6 of this document: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
186
+ */
187
+ *encodeChatGenerator(
188
+ chat: Iterable<ChatMessage>,
189
+ model = this.modelName,
190
+ ): Generator<number[], void, undefined> {
191
+ if (!model) {
192
+ throw new Error(
193
+ 'Model name must be provided either during initialization or passed in to the method.',
194
+ )
195
+ }
196
+ const params = chatModelParams[model]
197
+ const chatStartToken = this.specialTokenMapping.get(ImStart)
198
+ const chatEndToken = this.specialTokenMapping.get(ImEnd)
199
+
200
+ if (!params || chatStartToken === undefined || chatEndToken === undefined) {
201
+ throw new Error(`Model '${model}' does not support chat.`)
202
+ }
203
+ const allowedSpecial = new Set([ImSep])
204
+ const { messageSeparator, roleSeparator } = params
205
+ const encodedMessageSeparator =
206
+ messageSeparator.length > 0 ? this.encode(messageSeparator) : []
207
+ const encodedRoleSeparator =
208
+ roleSeparator.length > 0
209
+ ? this.encode(roleSeparator, { allowedSpecial })
210
+ : []
211
+ const nameCache = new Map<string, number[]>()
212
+
213
+ for (const { role = 'system', name = role, content } of chat) {
214
+ if (content === undefined) {
215
+ throw new Error('Content must be defined for all messages.')
216
+ }
217
+
218
+ yield [chatStartToken]
219
+ const encodedName = nameCache.get(name) ?? this.encode(name)
220
+ nameCache.set(name, encodedName)
221
+ yield encodedName
222
+ if (encodedRoleSeparator.length > 0) {
223
+ yield encodedRoleSeparator
224
+ }
225
+ yield* this.encodeGenerator(content)
226
+ yield [chatEndToken]
227
+ yield encodedMessageSeparator
228
+ }
229
+
230
+ // every reply is primed with <|start|>assistant<|message|>
231
+ yield [chatStartToken]
232
+ yield* this.encodeGenerator('assistant')
233
+ if (encodedRoleSeparator.length > 0) {
234
+ yield encodedRoleSeparator
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Encodes a chat into a single array of tokens.
240
+ * Warning: gpt-3.5-turbo and gpt-4 chat format may change over time.
241
+ * Returns tokens assuming the 'gpt-3.5-turbo-0301' / 'gpt-4-0314' format.
242
+ * Based on OpenAI's guidelines: https://github.com/openai/openai-python/blob/main/chatml.md
243
+ * Also mentioned in section 6 of this document: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
244
+ */
245
+ encodeChat(chat: readonly ChatMessage[], model = this.modelName): number[] {
246
+ return [...this.encodeChatGenerator(chat, model)].flat()
172
247
  }
173
248
 
174
249
  /**
175
250
  * @returns {false | number} false if token limit is exceeded, otherwise the number of tokens
176
251
  */
177
- isWithinTokenLimit(text: string, tokenLimit: number): false | number {
178
- const tokenGenerator = this.encodeGenerator(text)
252
+ isWithinTokenLimit(
253
+ input: string | Iterable<ChatMessage>,
254
+ tokenLimit: number,
255
+ ): false | number {
256
+ const tokenGenerator =
257
+ typeof input === 'string'
258
+ ? this.encodeGenerator(input)
259
+ : this.encodeChatGenerator(input)
179
260
  let count = 0
180
261
  for (const tokens of tokenGenerator) {
181
262
  count += tokens.length