Universal context limits, token counting and tool callings for OpenAI, Anthropic, and Gemini.
Project description
📦 ContextCounter
ContextCounter is a lightweight Python library for:
- 🔍 Fetching context window limits across LLM providers (OpenAI, Anthropic, Gemini)
- 📊 Counting tokens in text or chat messages, with safe fallbacks
- ✅ Checking if you’re about to exceed context
- 🛠 Detailed analytics: count tool calls, reasoning tokens, and token splits by role
It works out-of-the-box with no dependencies, and can optionally integrate with official SDKs for precise context sizes.
🚀 Installation
pip install contextcounter
Optional extras (for better accuracy):
pip install "contextcounter[openai]" # precise OpenAI context limits
pip install "contextcounter[anthropic]" # precise Anthropic context limits
pip install "contextcounter[tokenizers]" # local tokenizers (tiktoken, transformers)
⚡ Quickstart
from contextcounter import ContextInspector
# create inspector bound to a provider (openai, anthropic, google/vertex)
ctx = ContextInspector(provider="openai")
# fetch context window for a model
limit = ctx.get_limit("gpt-4o")
print("Context window:", limit)
# count tokens in free text
tokens = ctx.count_text("Hello world", model="gpt-4o")
print("Tokens:", tokens)
# count tokens in a chat-style message list
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Summarize this in 5 points."},
]
msg_tokens = ctx.count_messages(messages, model="gpt-4o")
print("Message tokens:", msg_tokens)
# check if it will exceed
print("Will exceed?", ctx.will_exceed(msg_tokens, "gpt-4o", reserve=2048))
📖 API Reference
ContextInspector
from contextcounter import ContextInspector
ctx = ContextInspector(provider="openai")
provider:"openai" | "anthropic" | "google" | "vertex" | None- If given, you don’t need to repeat provider in every function.
gemini_overrides(dict, optional): override documented Gemini limits (default is1_000_000tokens).
ctx.get_limit(model: str) -> int | None
Returns the maximum context window for the given model.
- For OpenAI models: will query live API if
openaipackage is installed & authenticated. - For Anthropic: will query live API if
anthropicpackage is installed & authenticated. - For Gemini: uses documented defaults (
1,000,000) unless overridden. - Fallback: internal static defaults.
ctx = ContextInspector(provider="anthropic")
print(ctx.get_limit("claude-3-7-sonnet")) # 200000
ctx.count_text(text: str, model: str | None = None) -> int
Returns the number of tokens in a plain text string.
- Tries tiktoken if available (for OpenAI models).
- Falls back to GPT-2 tokenizer if
transformersinstalled. - Last resort: heuristic (≈1 token per ~3.7 characters).
print(ctx.count_text("Hello world", model="gpt-4o")) # → 5 (approx.)
ctx.count_messages(messages: list, model: str | None = None) -> int
Returns the number of tokens in a chat-style message list.
- Adds structural overhead tokens per message (default: 3 tokens, 6 for system role).
- Handles content as strings or lists (for multimodal formats).
messages = [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Tell me a joke."}
]
print(ctx.count_messages(messages, model="gpt-4o")) # → ~20
ctx.will_exceed(tokens: int, model: str, reserve: int = 1024) -> bool | None
Checks whether a given token count plus a reserve buffer will exceed the model’s context window.
- Returns
Trueif it will exceed,Falseotherwise,Noneif limit unknown.
tokens = ctx.count_messages(messages, model="gpt-4o")
print(ctx.will_exceed(tokens, "gpt-4o", reserve=2048))
Standalone Functions
If you don’t want to use the ContextInspector class, you can import the core functions:
from contextcounter import get_context_limit, count_text, count_messages
print(get_context_limit("gpt-4o", provider="openai"))
print(count_text("Hello", model="gpt-4o", provider="openai"))
🔎 Detailed Analytics
In addition to simple counts, ContextCounter can provide detailed breakdowns:
- Number of tool calls
- Token usage by role (system/user/assistant/tool)
- Prompt vs. completion split
- Reasoning tokens (when provided by the API usage object)
analyze_messages
from contextcounter import analyze_messages
report = analyze_messages(messages, model="gpt-4o", provider="openai")
print(report)
# {
# "total_tokens": 123,
# "by_role": {"system": 10, "user": 45, "assistant": 60, "tool": 8, "other": 0},
# "details": [
# {"role":"system","content_tokens":4,"overhead":6,"tool_calls":0},
# {"role":"user","content_tokens":42,"overhead":3,"tool_calls":0},
# {"role":"assistant","content_tokens":57,"overhead":3,"tool_calls":2},
# ],
# "num_messages": 3,
# "num_tool_calls": 2
# }
split_prompt_completion
from contextcounter import split_prompt_completion
split = split_prompt_completion(messages, response_text="assistant reply...", model="gpt-4o", provider="openai")
print(split)
# {"prompt_tokens": 88, "completion_tokens": 35, "total_tokens": 123}
summarize_provider_usage
Normalize usage dictionaries from API responses (OpenAI, Anthropic, etc.) into a consistent schema.
from contextcounter import summarize_provider_usage
openai_usage = {
"prompt_tokens": 81,
"completion_tokens": 40,
"total_tokens": 121,
"reasoning_tokens": 12,
}
norm = summarize_provider_usage(openai_usage, provider="openai")
print(norm)
# {"prompt_tokens":81,"completion_tokens":40,"reasoning_tokens":12,"total_tokens":121}
🔧 Supported Models & Defaults
| Provider | Model Example | Context Limit (tokens) |
|---|---|---|
| OpenAI | gpt-4o, gpt-4.1 | 128,000 |
| OpenAI | o3 | 200,000 |
| Anthropic | claude-3-7-sonnet | 200,000 |
| Anthropic | claude-3-5-sonnet/haiku | 200,000 |
| gemini-1.5-pro/flash | 1,000,000 |
Live API calls (OpenAI, Anthropic) will override these defaults if you have SDKs installed + credentials configured.
🧪 Testing
pytest tests/
📌 Roadmap
- ✅ Token counting across providers
- ✅ Context limit retrieval with fallbacks
- ✅ Tool call counting + split by role
- ✅ Prompt vs. completion split
- ✅ Reasoning tokens via provider usage normalization
- 🔜 Native Gemini token counting (when tokenizer becomes public)
- 🔜 More granular overhead estimation per provider
📄 License
MIT © Nemlig ADK
Project details
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 contextcounter-0.1.6.tar.gz.
File metadata
- Download URL: contextcounter-0.1.6.tar.gz
- Upload date:
- Size: 6.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
814cbebdc78a7043e5adcae2df735bfd30febe6b5eb6e9cb8be58680a1395444
|
|
| MD5 |
04e4c69a489714fa176e0737e68d1d62
|
|
| BLAKE2b-256 |
a03763dbf251bab61b7279e7dc79ff997168df975dba4791a180238986b91d39
|
File details
Details for the file contextcounter-0.1.6-py3-none-any.whl.
File metadata
- Download URL: contextcounter-0.1.6-py3-none-any.whl
- Upload date:
- Size: 8.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e07814f4acd12371296c864e571a87dad866861f076406ce27419972cf20c58
|
|
| MD5 |
b810fb87d7d0adbe5dd7b6816c3570ac
|
|
| BLAKE2b-256 |
6216bff2e3a0101adef1f2d2e97b79061c76aef0555013da914e42bb676c1cf2
|