Skip to content

Commit d31e9a6

Browse files
feat(client): add the ability to limit token usage
1 parent f14aa0f commit d31e9a6

File tree

4 files changed

+60
-25
lines changed

4 files changed

+60
-25
lines changed

README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,28 @@ console.log(response);
171171
import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api';
172172

173173
const clientOptions = {
174-
// (Optional) Support for a reverse proxy for the completions endpoint (private API server).
175-
// Warning: This will expose your `openaiApiKey` to a third-party. Consider the risks before using this.
176-
// reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
177-
// (Optional) Parameters as described in https://platform.openai.com/docs/api-reference/completions
178-
modelOptions: {
179-
// You can override the model name and any other parameters here.
180-
// model: 'text-chat-davinci-002-20221122',
181-
},
182-
// (Optional) Set custom instructions instead of "You are ChatGPT...".
183-
// promptPrefix: 'You are Bob, a cowboy in Western times...',
184-
// (Optional) Set a custom name for the user
185-
// userLabel: 'User',
186-
// (Optional) Set a custom name for ChatGPT
187-
// chatGptLabel: 'ChatGPT',
188-
// (Optional) Set to true to enable `console.debug()` logging
189-
debug: false,
174+
// (Optional) Support for a reverse proxy for the completions endpoint (private API server).
175+
// Warning: This will expose your `openaiApiKey` to a third-party. Consider the risks before using this.
176+
// reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
177+
// (Optional) Parameters as described in https://platform.openai.com/docs/api-reference/completions
178+
modelOptions: {
179+
// You can override the model name and any other parameters here.
180+
// model: 'text-chat-davinci-002-20221122',
181+
// Set max_tokens here to override the default max_tokens of 1000 for the completion.
182+
// max_tokens: 1000,
183+
},
184+
// (Optional) Davinci models have a max context length of 4097 tokens, but you may need to change this for other models.
185+
// maxContextTokens: 4097,
186+
// (Optional) You might want to lower this to save money if using a paid model like `text-davinci-003`.
187+
// maxPromptTokens: 3097,
188+
// (Optional) Set custom instructions instead of "You are ChatGPT...".
189+
// promptPrefix: 'You are Bob, a cowboy in Western times...',
190+
// (Optional) Set a custom name for the user
191+
// userLabel: 'User',
192+
// (Optional) Set a custom name for ChatGPT
193+
// chatGptLabel: 'ChatGPT',
194+
// (Optional) Set to true to enable `console.debug()` logging
195+
debug: false,
190196
};
191197

192198
const cacheOptions = {
@@ -238,7 +244,13 @@ module.exports = {
238244
modelOptions: {
239245
// You can override the model name and any other parameters here.
240246
// model: 'text-chat-davinci-002-20221122',
247+
// Set max_tokens here to override the default max_tokens of 1000 for the completion.
248+
// max_tokens: 1000,
241249
},
250+
// (Optional) Davinci models have a max context length of 4097 tokens, but you may need to change this for other models.
251+
// maxContextTokens: 4097,
252+
// (Optional) You might want to lower this to save money if using a paid model like `text-davinci-003`.
253+
// maxPromptTokens: 3097,
242254
// (Optional) Set custom instructions instead of "You are ChatGPT...".
243255
// promptPrefix: 'You are Bob, a cowboy in Western times...',
244256
// (Optional) Set a custom name for the user

demos/use-client.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ const clientOptions = {
99
modelOptions: {
1010
// You can override the model name and any other parameters here.
1111
// model: 'text-chat-davinci-002-20221122',
12+
// Set max_tokens here to override the default max_tokens of 1000 for the completion.
13+
// max_tokens: 1000,
1214
},
15+
// (Optional) Davinci models have a max context length of 4097 tokens, but you may need to change this for other models.
16+
// maxContextTokens: 4097,
17+
// (Optional) You might want to lower this to save money if using a paid model like `text-davinci-003`.
18+
// maxPromptTokens: 3097,
1319
// (Optional) Set custom instructions instead of "You are ChatGPT...".
1420
// promptPrefix: 'You are Bob, a cowboy in Western times...',
1521
// (Optional) Set a custom name for the user

settings.example.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ export default {
99
modelOptions: {
1010
// You can override the model name and any other parameters here.
1111
// model: 'text-chat-davinci-002-20221122',
12+
// Set max_tokens here to override the default max_tokens of 1000 for the completion.
13+
// max_tokens: 1000,
1214
},
15+
// (Optional) Davinci models have a max context length of 4097 tokens, but you may need to change this for other models.
16+
// maxContextTokens: 4097,
17+
// (Optional) You might want to lower this to save money if using a paid model like `text-davinci-003`.
18+
// maxPromptTokens: 3097,
1319
// (Optional) Set custom instructions instead of "You are ChatGPT...".
1420
// promptPrefix: 'You are Bob, a cowboy in Western times...',
1521
// (Optional) Set a custom name for the user

src/ChatGPTClient.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ export default class ChatGPTClient {
2626
stop: modelOptions.stop,
2727
};
2828

29+
// Davinci models have a max context length of 4097 tokens.
30+
this.maxContextTokens = this.options.maxContextTokens || 4097;
31+
// I decided to limit conversations to 3097 tokens, leaving 1000 tokens for the response.
32+
this.maxPromptTokens = this.options.maxPromptTokens || 3097;
33+
this.maxResponseTokens = this.modelOptions.max_tokens || 1000;
34+
35+
if (this.maxPromptTokens + this.maxResponseTokens > this.maxContextTokens) {
36+
throw new Error(`maxPromptTokens + max_tokens (${this.maxPromptTokens} + ${this.maxResponseTokens} = ${this.maxPromptTokens + this.maxResponseTokens}) must be less than or equal to maxContextTokens (${this.maxContextTokens})`);
37+
}
38+
2939
this.userLabel = this.options.userLabel || 'User';
3040
this.chatGptLabel = this.options.chatGptLabel || 'ChatGPT';
3141

@@ -258,8 +268,7 @@ export default class ChatGPTClient {
258268

259269
let currentTokenCount = this.getTokenCount(`${promptPrefix}${promptSuffix}`);
260270
let promptBody = '';
261-
// I decided to limit conversations to 3097 tokens, leaving 1000 tokens for the response.
262-
const maxTokenCount = 3097;
271+
const maxTokenCount = this.maxPromptTokens;
263272
// Iterate backwards through the messages, adding them to the prompt until we reach the max token count.
264273
while (currentTokenCount < maxTokenCount && orderedMessages.length > 0) {
265274
const message = orderedMessages.pop();
@@ -280,11 +289,13 @@ export default class ChatGPTClient {
280289
// joined words may combine into a single token. Actually, that isn't really applicable here, but I can't
281290
// resist doing it the "proper" way.
282291
const newTokenCount = this.getTokenCount(`${promptPrefix}${newPromptBody}${promptSuffix}`);
283-
// Always add the first (technically last) message, even if it puts us over the token limit.
284-
// TODO: throw an error if the first message is over 3000 tokens
285-
if (promptBody && newTokenCount > maxTokenCount) {
286-
// This message would put us over the token limit, so don't add it.
287-
break;
292+
if (newTokenCount > maxTokenCount) {
293+
if (promptBody) {
294+
// This message would put us over the token limit, so don't add it.
295+
break;
296+
}
297+
// This is the first message, so we can't add it. Just throw an error.
298+
throw new Error(`Prompt is too long. Max token count is ${maxTokenCount}, but prompt is ${newTokenCount} tokens long.`);
288299
}
289300
promptBody = newPromptBody;
290301
currentTokenCount = newTokenCount;
@@ -293,8 +304,8 @@ export default class ChatGPTClient {
293304
const prompt = `${promptBody}${promptSuffix}`;
294305

295306
const numTokens = this.getTokenCount(prompt);
296-
// Use up to 4097 tokens (prompt + response), but try to leave 1000 tokens for the response.
297-
this.modelOptions.max_tokens = Math.min(4097 - numTokens, 1000);
307+
// Use up to `this.maxContextTokens` tokens (prompt + response), but try to leave `this.maxTokens` tokens for the response.
308+
this.modelOptions.max_tokens = Math.min(this.maxContextTokens - numTokens, this.maxResponseTokens);
298309

299310
return prompt;
300311
}

0 commit comments

Comments
 (0)