Skip to main content

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 is 1_000_000 tokens).

ctx.get_limit(model: str) -> int | None

Returns the maximum context window for the given model.

  • For OpenAI models: will query live API if openai package is installed & authenticated.
  • For Anthropic: will query live API if anthropic package 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 transformers installed.
  • 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 True if it will exceed, False otherwise, None if 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
Google 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

contextcounter-0.1.6.tar.gz (6.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

contextcounter-0.1.6-py3-none-any.whl (8.2 kB view details)

Uploaded Python 3

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

Hashes for contextcounter-0.1.6.tar.gz
Algorithm Hash digest
SHA256 814cbebdc78a7043e5adcae2df735bfd30febe6b5eb6e9cb8be58680a1395444
MD5 04e4c69a489714fa176e0737e68d1d62
BLAKE2b-256 a03763dbf251bab61b7279e7dc79ff997168df975dba4791a180238986b91d39

See more details on using hashes here.

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

Hashes for contextcounter-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 1e07814f4acd12371296c864e571a87dad866861f076406ce27419972cf20c58
MD5 b810fb87d7d0adbe5dd7b6816c3570ac
BLAKE2b-256 6216bff2e3a0101adef1f2d2e97b79061c76aef0555013da914e42bb676c1cf2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page