/** * Dependency-free token estimator. * * This is NOT a real tokenizer: it is a heuristic calibrated per character * class, built for comparing two versions of the same prompt — but do NOT bill * anyone from it. * * **Its accuracy is not one number, and the years it was written as one were * the mistake.** The branches below treat CJK, digits and punctuation quite * differently from words, and there was never a reason those should land on the * same accuracy. Measured over 47 samples against the official counting * endpoint they do not: the worst error is 3.2% on CJK, 5.6% on Latin prose, * 25.1% on code and quoting, and 32.5% on a digit-dominant table. `band.ts` * holds the bands those measurements earn and `bandFor(text)` is what a caller * should print; the constant below is only the widest of them, for callers that * have no text in hand. * * `test/token-band.test.js` holds every sample against the band `bandFor` gives * it, and a sample edited since it was measured fails rather than passing * quietly. * * For exact numbers use `countTokensAnthropic` (the official token-counting * endpoint, which is free) or pass your own `TokenCounter`. */ /** * The widest band this estimator is published under, as a percentage. * * **A history worth keeping, because each value was believed at the time.** It * was `15` for eight releases as a design target nobody had checked; the first * measurement found two of eight samples outside it and it went to `25`; fixing * the digit divisor and calibrating per language brought it back to `15`, and * splitting kana from han — Japanese had been +11.2% and Chinese −3.2% under one * rule — brought it to `10`. Four values, each an improvement, and all four * wrong in the same way. * * **The corpus they were measured on was the corpus they were fitted to.** * Twenty-one samples held thirteen files of Latin prose and exactly one each of * code, numeric and punctuation, and those single files were where the constants * came from. The worst error it could show was 6.4%, and 10 looked like a * comfortable margin over it. Neither figure was about the estimator. * * **It is no longer one number, and this one is the widest.** A corpus of * thirteen prose files and one each of code, numeric and punctuation put the * band at 10, and those single files were the set the constants had been fitted * to. Measured on forty-seven, the same estimator is 6% out on prose and 33% * out on a CSV ledger. `bandFor` in `band.ts` is what a caller that has the * text should use. * * This stays for the callers that do not have it — a figure covering the whole * catalogue of text, which can only be the worst of them. Widening it from 10 * to 33 makes every claim that reads it true where it had been false, at the * cost of understating the estimator on prose. That trade is deliberate: a * caller with no text cannot know which it is holding, and there is only one * safe direction to guess in. */ export declare const ESTIMATE_ERROR_BAND_PCT = 33; /** * Whose tokenizer that band was measured against. * * The number above is not a property of the estimator; it is a property of the * estimator **against Claude**. Forty-seven samples, one counting endpoint, one * family. Trazum prices seven, and the two that have since been run are far * outside anything published here: DeepSeek 94.5% at worst, Mistral 103.1%. On * a Gemini prompt nobody has measured it at all, and `measuredForeignError` * answers null rather than the nearest number. On a GPT prompt somebody has: * 112.4%, the worst of the four measured, and the figure this file reported as * unmeasured for a release while its own fixture held the answer. * * Exported as a name rather than left implicit in a comment because the band * had already leaked out of its domain in three places: an advisory that told a * GPT-5 user *"the call will fail"* on the strength of a Claude measurement, * the same advisory sending them to a counting endpoint that counts a different * tokenizer, and `--exact-tokens` forwarding their model id to Anthropic. A * fact with no name is a fact nothing can check. */ export declare const BAND_CALIBRATED_PROVIDER = "anthropic"; /** * Whether the published band describes this provider's tokenizer. * * `undefined` is false, deliberately. A model with no provider recorded is not * evidence that the band applies to it — and the flattering reading of missing * information is the one this project does not take. */ export declare function bandGoverns(provider: string | undefined | null): boolean; /** * Estimates how many tokens `text` occupies. * * Rules per character class: * - words: ~4 effective characters per token (minimum 1) * - numbers: ~3 digits per token * - punctuation: ~2 marks per token * - newlines: ~1 token per 2 consecutive newlines * - CJK: 1 token per character * - emoji and symbols outside the BMP: 2 tokens * - spaces: 0 (absorbed into the following token) */ export declare function estimateTokens(text: string): number; /** Asynchronous token counter, for remote sources. */ export type AsyncTokenCounter = (text: string) => Promise; export interface AnthropicCounterOptions { apiKey: string; /** Model to count against. Token counts are model-specific. */ model?: string; baseUrl?: string; fetchImpl?: typeof fetch; /** See `OpenAiCompatibleOptions.allowInsecure`: only when you chose the URL. */ allowInsecure?: boolean; } /** * Exact counter using the official `/v1/messages/count_tokens` endpoint. * The endpoint does not bill tokens, so you can use it freely to measure. * * The third door, and the one nobody had looked at: this takes a `baseUrl` and * sends an `x-api-key` to it. Both providers were hardened at the boundary and * this was left with no check at all, because it is called a counter rather * than a provider. It goes through the same gate now. */ export declare function countTokensAnthropic(options: AnthropicCounterOptions): AsyncTokenCounter; //# sourceMappingURL=tokenizer.d.ts.map