AI Engineer Portal
Your personal operating system for career transition.
Exercise
Implement token counting and cost tracking
Implement Token Counting and Cost Tracking
Token usage is the billing unit for every major LLM API. Without explicit tracking, AI features run with no cost visibility until the monthly invoice arrives. Engineers who track token usage per request can answer "which feature costs the most?", "is this prompt getting more expensive over time?", and "what is the P95 cost per user session?"
What to build
Implement a CostTracker class that records token usage across multiple LLM calls and computes cost summaries:
record(model: str, input_tokens: int, output_tokens: int, feature: str)method — records one LLM call with its token usage and the product feature that made the call.total_cost_usd()method — returns the total USD cost across all recorded calls.cost_by_feature()method — returns a dict mapping feature name to total USD cost.cost_by_model()method — returns a dict mapping model name to total USD cost.summary()method — returns a dict withtotal_calls,total_input_tokens,total_output_tokens,total_cost_usd,cost_by_feature,cost_by_model.
Also implement MODEL_COSTS: dict[str, dict] — a lookup table mapping model name to {"input_per_1k": float, "output_per_1k": float}. Include at minimum:
"gpt-4o": input $0.0025/1k, output $0.01/1k"gpt-4o-mini": input $0.00015/1k, output $0.0006/1k"claude-3-5-sonnet": input $0.003/1k, output $0.015/1k
Why this matters
Token tracking is required for cost attribution in multi-feature products, budget alerts before hitting billing limits, and deciding which model tier to use based on actual usage patterns. A tracker that is easy to integrate (one .record() call per LLM response) gets adopted; a complex one gets bypassed.
Constraints
- Raise
ValueErrorfor unknown model names. - All costs should be computed from the
MODEL_COSTStable, not hardcoded. - The
summary()method should return costs rounded to 6 decimal places.
Python Refresh / medium / Step 11 of 16
Core runtime habits
Normalize the boundary first, keep return shapes stable, and make the middle of the function boring on purpose.
- - Returns one stable shape across branches
- - Makes failure handling obvious
- - Keeps the core logic readable in under a minute
- - Did I make the input and output shapes explicit?
- - Would a teammate know where validation happens?
- - Did I avoid silent mutation or vague branching?
Practice
Generate a variation
Generate a new exercise variation to deepen understanding or practice a related concept.
Attempt history
Recent submissions
Before you submit, decide what a strong answer should make obvious to the reviewer.
No attempts yet.