"""Cost arithmetic for API deployments (pure, deterministic). Prices are USD per 1M tokens; nothing is assumed when a price is missing — the affected component is `null` and a `note` says why. per_request = input_tokens × effective_input / 1e6 + output_tokens × effective_output / 1e6 (+ per_request fee when published) effective_input = (1 − cached_share) × input + cached_share × cached_input (cached_input falls back to input with a note) batch = batch_input / batch_output when published (else the standard prices, with a note) daily = per_request × requests_per_day · monthly = daily × 30 · annual = daily × 365 """ from __future__ import annotations from typing import Any DAYS_PER_MONTH = 30 DAYS_PER_YEAR = 365 def _f(v: Any) -> float | None: if v is None or isinstance(v, bool): return None try: return float(v) except (TypeError, ValueError): return None def compute_cost(prices: dict[str, Any], *, input_tokens: int, output_tokens: int, requests_per_day: float = 1.0, cached_share: float = 0.0, batch: bool = False) -> dict[str, Any]: """`prices` uses the Deployment `prices` keys (input, output, cached_input, batch_input, batch_output, per_request).""" notes: list[str] = [] cached_share = min(1.0, max(0.0, float(cached_share or 0.0))) inp, outp = _f(prices.get("input")), _f(prices.get("output")) if batch: bi, bo = _f(prices.get("batch_input")), _f(prices.get("batch_output")) if bi is None or bo is None: notes.append("batch prices not published for this deployment; standard prices used") inp = bi if bi is not None else inp outp = bo if bo is not None else outp eff_input = inp if cached_share > 0 and inp is not None: cached = _f(prices.get("cached_input")) if cached is None: notes.append("cached input price not published; cached share billed at the standard input price") cached = inp eff_input = (1 - cached_share) * inp + cached_share * cached fee = _f(prices.get("per_request")) or 0.0 if inp is None: notes.append("input price unavailable") if outp is None: notes.append("output price unavailable") if eff_input is None or outp is None: per_request = None else: per_request = input_tokens * eff_input / 1e6 + output_tokens * outp / 1e6 + fee daily = per_request * requests_per_day if per_request is not None else None return { "per_request": _round(per_request, 8), "daily": _round(daily, 6), "monthly": _round(daily * DAYS_PER_MONTH, 4) if daily is not None else None, "annual": _round(daily * DAYS_PER_YEAR, 4) if daily is not None else None, "effective_input_per_mtok": _round(eff_input, 6), "effective_output_per_mtok": _round(outp, 6), "per_request_fee": fee or None, "inputs": {"input_tokens": input_tokens, "output_tokens": output_tokens, "requests_per_day": requests_per_day, "cached_share": cached_share, "batch": batch}, "notes": notes, } def context_fill_cost(input_per_mtok: Any, tokens: int) -> float | None: """Cost of one fully populated context of `tokens` input tokens.""" p = _f(input_per_mtok) return _round(p * tokens / 1e6, 6) if p is not None else None def _round(v: float | None, nd: int) -> float | None: return round(v, nd) if v is not None else None __all__ = ["DAYS_PER_MONTH", "DAYS_PER_YEAR", "compute_cost", "context_fill_cost"]