Intermediate

Cost Tracking

Track per-request costs, enforce department budgets, and alert on spending anomalies.

Cost Tracker

# src/costs.py
from datetime import datetime, timedelta
from typing import Dict, Optional
import json

# Token pricing per 1M tokens (input/output)
PRICING = {
    "gpt-4o": {"input": 2.50, "output": 10.00},
    "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    "claude-sonnet-4-20250514": {"input": 3.00, "output": 15.00},
    "claude-3-5-haiku-20241022": {"input": 0.80, "output": 4.00},
}

class CostTracker:
    def __init__(self, redis_client):
        self.redis = redis_client
        self.budgets = {}

    def calculate_cost(self, model, prompt_tokens, completion_tokens):
        pricing = PRICING.get(model, {"input": 5.0, "output": 15.0})
        input_cost = (prompt_tokens / 1_000_000) * pricing["input"]
        output_cost = (completion_tokens / 1_000_000) * pricing["output"]
        return round(input_cost + output_cost, 6)

    async def record(self, user_id, team_id, model,
                     prompt_tokens, completion_tokens):
        cost = self.calculate_cost(model, prompt_tokens, completion_tokens)
        now = datetime.now()
        record = {
            "user_id": user_id, "team_id": team_id, "model": model,
            "prompt_tokens": prompt_tokens,
            "completion_tokens": completion_tokens,
            "cost": cost, "timestamp": now.isoformat(),
        }

        # Store in Redis sorted set by timestamp
        day_key = f"costs:{now.strftime('%Y-%m-%d')}"
        await self.redis.zadd(day_key, {json.dumps(record): now.timestamp()})
        await self.redis.expire(day_key, 86400 * 90)

        # Update running totals
        await self.redis.incrbyfloat(f"cost:user:{user_id}:daily", cost)
        await self.redis.incrbyfloat(f"cost:team:{team_id}:monthly", cost)

        return {"cost": cost, "record": record}

    def set_budget(self, team_id, monthly_budget):
        self.budgets[team_id] = monthly_budget

    async def check_budget(self, team_id):
        spent = await self.redis.get(f"cost:team:{team_id}:monthly")
        spent = float(spent) if spent else 0
        budget = self.budgets.get(team_id, float('inf'))
        return {
            "team_id": team_id,
            "spent": round(spent, 2),
            "budget": budget,
            "remaining": round(budget - spent, 2),
            "utilization": round(spent / budget * 100, 1) if budget else 0,
            "over_budget": spent > budget,
        }

    async def get_daily_summary(self, date=None):
        if date is None:
            date = datetime.now().strftime('%Y-%m-%d')
        day_key = f"costs:{date}"
        records = await self.redis.zrange(day_key, 0, -1)
        total = 0
        by_model = {}
        by_team = {}
        for r in records:
            rec = json.loads(r)
            total += rec["cost"]
            by_model[rec["model"]] = by_model.get(rec["model"], 0) + rec["cost"]
            by_team[rec["team_id"]] = by_team.get(rec["team_id"], 0) + rec["cost"]
        return {"date": date, "total_cost": round(total, 4),
                "by_model": by_model, "by_team": by_team,
                "total_requests": len(records)}
💡
Budget enforcement: Check budgets before routing requests. Return a 429 status when a team exceeds their monthly budget to prevent runaway costs.