Solving tips
- A chars/4 heuristic is a fast, dependency-free stand-in for a real tokenizer; state that it is an approximation, not exact.
- Chat models spend extra tokens per message on role and formatting, so add a small fixed per-message overhead when you sum a conversation.
- Use ceiling division for the char estimate so any non-empty string costs at least one token.
Before you send a conversation to a model you need to know, roughly, how big it is — a context window is a hard budget, and blowing past it truncates or rejects the request. A real tokenizer (BPE, tiktoken) gives the exact count, but it is model-specific and pulls in a dependency. A cheap, deterministic approximation is enough for a pre-flight budget check: a string is about len(text) / 4 tokens for typical English text.
How the estimate works
Two pieces:
- Per-string estimate.
estimate_tokens(text)returnsceil(len(text) / 4). The ceiling means a 1-3 character string still costs 1 token, and an empty string costs 0. - Per-conversation estimate. Chat models do not just concatenate content; each message also spends a few tokens on its role tag and message delimiters. Model that with a fixed
PER_MESSAGE_OVERHEAD(3 here) added once per message. The conversation total is the sum ofestimate_tokens(content) + PER_MESSAGE_OVERHEADacross all messages.
This is an approximation on purpose. Real tokenizers split on sub-word units, so code, rare words, and non-English text can be well off 4 chars/token. Treat the result as a guardrail, not an exact count.
Task
Implement two functions:
estimate_tokens(text)->ceil(len(text) / 4).fits_budget(messages, budget)->Trueifsum(estimate_tokens(m["content"]) + PER_MESSAGE_OVERHEAD for m in messages) <= budget, elseFalse.
Example
messages = [
{"role": "system", "content": "You are a helpful assistant."}, # 28 chars -> 7 tokens
{"role": "user", "content": "Hi!"}, # 3 chars -> 1 token
]
estimate_tokens("You are a helpful assistant.") # -> 7 (ceil(28/4))
estimate_tokens("Hi!") # -> 1 (ceil(3/4))
# total = (7 + 3) + (1 + 3) = 14
fits_budget(messages, budget=20) # -> True
fits_budget(messages, budget=10) # -> False
Constraints
- Use only the standard library. Do not import a real tokenizer or call any network/model.
- The estimate must be deterministic: the same input always yields the same count.
estimate_tokens("")is0; any non-empty string is>= 1.- The budget check is inclusive: a total exactly equal to
budgetfits. - Assume every message dict has a string
"content"field.