tiktoken-context-window
Zero-dependency context-window token counter for tiktoken-powered AI agents. Computes accurate per-message and per-conversation budgets for OpenAI-style message histories. Ships for both Node.js and Python with identical results.
Why
AI coding agents (Claude Code, Cursor, Codex, Gemini CLI) call the
tiktoken encoder to estimate context-window usage. But tiktoken encodes
text — it does not count tokens in already-built message histories. Every
agent framework ends up re-implementing num_tokens_from_messages() and
small errors (off-by-one on role separators, missing the NAME: / ROLE:
prefixes tiktoken adds for tool/function messages) silently corrupt
budget estimates.
This library wraps a user-supplied tiktoken encoding and applies the exact
tiktoken reference rule (3 base + per-message 3 + tokens of role,
content, optional name, function-call envelope, plus 1 trailing newline)
so you get accurate budgets without re-deriving the schema yourself.
Bring your own tiktoken
This package has zero runtime dependencies. You provide the
tiktoken (Node) or
tiktoken (Python) encoder; we
just apply the rule.
# Node
npm install tiktoken-context-window tiktoken
# Python
pip install tiktoken-context-window tiktoken
Both tiktoken 0.7+ (Python) and 1.0+ (Node) are supported.
Quick start
Node.js (ESM)
import { createContextWindow, tokenBudget, DEFAULT_ENCODING } from 'tiktoken-context-window';
import tiktoken from 'tiktoken';
const encoding = tiktoken.get_encoding('cl100k_base');
const ctx = createContextWindow(encoding, [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'Fix the bug in auth.py' },
{ role: 'assistant', content: 'I found the issue in line 42...' },
{ role: 'tool', name: 'read_file', content: 'def authenticate(...):\n ...' },
{ role: 'user', content: 'Thanks, that fixed it.' },
]);
ctx.totalTokens; // e.g. 64
ctx.remainingTokens; // limit - totalTokens (clamped to 0)
ctx.pctUsed; // 0.0–1.0 (may exceed 1.0 when over budget)
ctx.estimatedCost(); // '$0.00016' (gpt-4o prompt pricing)
ctx.canAddMessage({ role: 'user', content: 'Hi' }); // true / false
const budget = tokenBudget(encoding, ctx.messages, 'gpt-4o');
// → { used: 64, limit: 128000, pct: 0.0005 }
Python
from tiktoken_context_window import ContextWindow, token_budget, DEFAULT_ENCODING
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base")
ctx = ContextWindow(encoding, [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Fix the bug in auth.py"},
{"role": "assistant", "content": "I found the issue in line 42..."},
{"role": "tool", "name": "read_file", "content": "def authenticate(...):\n ..."},
{"role": "user", "content": "Thanks, that fixed it."},
])
print(ctx.total_tokens) # e.g. 64
print(ctx.remaining_tokens)
print(ctx.pct_used)
print(ctx.estimated_cost()) # '$0.00016'
print(ctx.can_add_message({"role": "user", "content": "Hi"}))
budget = token_budget(encoding, ctx.messages, "gpt-4o")
# {'used': 64, 'limit': 128000, 'pct': 0.0005}
CLI audit (Python, zero runtime dependencies)
python3 scripts/count_context.py --model gpt-4o --file session_history.json
# Model: gpt-4o
# Context limit: 128,000 tokens
# Tokens used: 12,847
# Utilisation: 10.0%
# Status: SAFE
The CLI uses tiktoken if it's installed (accurate) and falls back to a
coarse 4-chars-per-token approximation otherwise (emits a warning to stderr).
API
Encoding
DEFAULT_ENCODING = 'cl100k_base'. This matches the spec and the
gpt-3.5-turbo / gpt-4-turbo family. For gpt-4o / gpt-4o-mini, current
tiktoken releases actually map those models to o200k_base; this library
still defaults to cl100k_base for reproducibility. Pass a different
encoding object if you need the native per-model encoding:
import tiktoken from 'tiktoken';
const encoding = tiktoken.encoding_for_model('gpt-4o'); // o200k_base
const ctx = createContextWindow(encoding, messages);
Model limits (tokens)
| Model | Limit | Source |
|---|---|---|
gpt-4o |
128,000 | https://platform.openai.com/docs/models/gpt-4o |
gpt-4o-mini |
128,000 | https://platform.openai.com/docs/models/gpt-4o-mini |
gpt-4-turbo |
128,000 | https://platform.openai.com/docs/models/gpt-4-turbo |
gpt-3.5-turbo |
16,385 | https://platform.openai.com/docs/models/gpt-3-5-turbo |
Override per call with createContextWindow(encoding, msgs, { limit: N }) or
tokenBudget(encoding, msgs, 'gpt-4o', { limit: N }).
Pricing (USD per 1k prompt tokens)
| Model | $/1k | Source |
|---|---|---|
gpt-4o |
$0.0025 | https://openai.com/api/pricing/ |
gpt-4o-mini |
$0.00015 | https://openai.com/api/pricing/ |
gpt-4-turbo |
$0.01 | https://openai.com/api/pricing/ |
gpt-3.5-turbo |
$0.0005 | https://openai.com/api/pricing/ |
Pricing is a snapshot from August 2026; refresh the table in
src/index.cjs / tiktoken_context_window/__init__.py if OpenAI changes it.
Operations
canAddMessage(msg)/can_add_message(msg)— boolean: would adding this message keep us withinlimit?trimToBudget(target)/trim_to_budget(target)— removes oldest user/assistant messages FIFO untilpct_used ≤ target. Preservessystem(index 0) and never removestool/functionmessages. RaisesContextOverflowErrorif the target cannot be reached because only protected messages remain.estimatedCost(model?)— formatted$X.XXXXXstring.
Encoding
The token-counting rule is uniform across encodings; the user picks the encoding and we apply the rule:
total = 3 # base overhead per API call
for each message:
total += 3 # tokens_per_message
total += tokens(encoding, role)
total += tokens(encoding, content)
if name and role in {'tool','function'}:
total += tokens(encoding, name) + 1
if function_call:
total += tokens(encoding, function_call.name)
total += tokens(encoding, ' ROLE:')
total += 1
total += 1 # trailing newline
This mirrors the official tiktoken Python reference for cl100k_base
and is verified by a parity test (tests/parity.test.mjs) that runs
identical message arrays through both runtimes and asserts zero
divergence across 50+ messages × 3 seeds.
CLI output
For machine readability, the CLI prints one fact per line:
Model: <model>
Context limit: <limit> tokens
Tokens used: <used>
Utilisation: <pct>%
Status: <SAFE|WARNING|DANGER|OVERFLOW>
Status thresholds: < 70% SAFE, 70–90% WARNING, 90–100% DANGER, > 100% OVERFLOW.
Exit code is 0 only when SAFE; WARNING/DANGER/OVERFLOW all exit 1 so the CLI
can be used as a CI gate.
Limitations
- Spec vs current tiktoken: this library defaults to
cl100k_baseper the v0.1.0 spec; current tiktoken (≥0.7 Python, ≥1.0 Node) mapsgpt-4o/gpt-4o-minitoo200k_base. Passtiktoken.encoding_for_model(...)for native per-model encoding. - Model limits: the per-model context limits use 128k for the GPT-4o
family (OpenAI's docs) rather than the 127216 example value from the
pre-release spec. Override via the
limitoption if you need that exact number for legacy reasons. - No streaming: this library counts tokens in already-built message histories. It does not stream-decode partial messages.
- No tool-use schema validation: function/tool message shapes are accepted as-is. We count tokens for whatever fields you provide.
- Not affiliated with OpenAI: this is an independent library; the pricing and limits tables are best-effort snapshots of OpenAI's public docs and may be stale.
Tests
# Node
npm install
npm test
# Python
pip install -e ".[dev]"
pytest
Both suites assert ≥100 passing tests across acceptance criteria AC-01
through AC-35 (see tests/COVERAGE.md for traceability).
Project layout
src/index.mjs ESM facade
src/index.cjs CommonJS implementation
index.d.ts Type declarations
tiktoken_context_window/ Python package
tests/ Node tests (node:test)
python_tests/ Python tests (pytest)
scripts/count_context.py Zero-dep CLI audit tool
fixtures/sample.json Example session history
tests/COVERAGE.md Acceptance criteria + test mapping
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tiktoken_context_window-0.1.0.tar.gz.
File metadata
- Download URL: tiktoken_context_window-0.1.0.tar.gz
- Upload date:
- Size: 11.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5314e172c4c0f7206107ddc65960245982fbd044426adac87b0e777ddb9056f6
|
|
| MD5 |
a394207330adef1c92ac22b69571281f
|
|
| BLAKE2b-256 |
0abc1e425815ca3012a70643aa48ea5e00fe1cc8a9c4569dd9d6305cb81d3388
|
File details
Details for the file tiktoken_context_window-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tiktoken_context_window-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a45ae2d0300cee0f6c384325d0ceedef5ec7bf4b424c877d840c6e41946ff64a
|
|
| MD5 |
d1a5f6737f7467b8b1a3ffc0f361c25d
|
|
| BLAKE2b-256 |
9404e5bfd092e1f07e29707f49c2a4f14591a3e505df4d2eba1166ecc5c2fdbb
|