-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
feat(api): exact offline token counting for count_tokens fallback via tiktoken #4087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { getEncoding, type Tiktoken } from "js-tiktoken"; | ||
|
|
||
| let encoder: Tiktoken | null = null; | ||
|
|
||
| function getEncoder(): Tiktoken { | ||
| if (!encoder) encoder = getEncoding("cl100k_base"); | ||
| return encoder; | ||
| } | ||
|
|
||
| /** | ||
| * Exact token count for a string using cl100k_base (offline, no upstream call). | ||
| * Defensive: never throws in a counting path — falls back to a char heuristic. | ||
| */ | ||
| export function countTextTokens(text: string): number { | ||
| if (!text || typeof text !== "string") return 0; | ||
| try { | ||
| return getEncoder().encode(text).length; | ||
| } catch { | ||
| return Math.ceil(text.length / 4); | ||
| } | ||
| } | ||
|
Comment on lines
+3
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Se a inicialização do Podemos otimizar isso introduzindo uma flag let encoder: Tiktoken | null = null;
let initializationFailed = false;
function getEncoder(): Tiktoken | null {
if (initializationFailed) return null;
if (!encoder) {
try {
encoder = getEncoding("cl100k_base");
} catch {
initializationFailed = true;
return null;
}
}
return encoder;
}
/**
* Exact token count for a string using cl100k_base (offline, no upstream call).
* Defensive: never throws in a counting path — falls back to a char heuristic.
*/
export function countTextTokens(text: string): number {
if (!text || typeof text !== "string") return 0;
try {
const enc = getEncoder();
if (enc) {
return enc.encode(text).length;
}
} catch {
// Fallback to heuristic below
}
return Math.ceil(text.length / 4);
} |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { test } from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import { countTextTokens } from "../../src/shared/utils/tiktokenCounter.ts"; | ||
|
|
||
| test("countTextTokens returns exact tiktoken count for a known string", () => { | ||
| assert.equal(countTextTokens("hello world"), 2); // cl100k_base | ||
| }); | ||
|
|
||
| test("countTextTokens handles empty and non-string safely", () => { | ||
| assert.equal(countTextTokens(""), 0); | ||
| assert.equal(countTextTokens(undefined as unknown as string), 0); | ||
| }); | ||
|
|
||
| test("countTextTokens is additive-ish and monotonic for longer text", () => { | ||
| const short = countTextTokens("the quick brown fox"); | ||
| const long = countTextTokens("the quick brown fox jumps over the lazy dog"); | ||
| assert.ok(long > short); | ||
| assert.ok(short > 0); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Na API de Messages da Anthropic, o parâmetro
systempode ser fornecido tanto como umastringsimples quanto como um array de blocos de conteúdo (por exemplo, blocos de texto do tipo{ type: "text", text: "..." }). Atualmente, o código apenas conta os tokens sesystemfor umastring, ignorando completamente quando ele é enviado como um array.Podemos melhorar a robustez e a precisão da contagem local adicionando suporte para quando
systemfor um array de blocos de texto, de forma análoga ao que já é feito para as mensagens.