File size: 3,267 Bytes
a2ec7b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
Pricing utilities for computing per-run cost from token usage.

All prices are specified per 1,000,000 tokens (M tokens) in USD.
"""

from __future__ import annotations

from typing import Dict, Optional


# Price map keyed by canonical model name (lowercased)
# Values are dicts with per-M token prices for input and output tokens
MODEL_PRICES_PER_M: Dict[str, Dict[str, float]] = {
    # Use exact actual_model_name keys (lowercased) provided by the user
    # Anthropic
    "claude-opus-4-1-20250805": {"input": 15.0, "output": 75.0},
    "claude-opus-4-5-20251101": {"input": 5.0, "output": 25.0},
    "claude-sonnet-4-20250514": {"input": 3.0, "output": 15.0},
    "claude-sonnet-4-5-20250929": {"input": 3.0, "output": 15.0},

    # DeepSeek
    "deepseek-v3.1-non-think": {"input": 0.56, "output": 1.68},
    "deepseek-v3.2-chat": {"input": 0.27, "output": 0.40},
    "deepseek-v3.2-reasoner": {"input": 0.27, "output": 0.40},
    "deepseek-v3.1-terminus-thinking": {"input": 0.21, "output": 0.79},
    "deepseek-v3.1-terminus": {"input": 0.21, "output": 0.79},

    # Google Gemini
    "gemini-2.5-pro": {"input": 2.5, "output": 15.0},
    "gemini-2.5-flash": {"input": 0.3, "output": 2.5},
    "gemini-3-pro": {"input": 2.0, "output": 12.0},

    # Z.AI
    "glm-4.5": {"input": 0.33, "output": 1.32},

    # OpenAI
    "gpt-5-2025-08-07": {"input": 1.25, "output": 10.0},
    "gpt-5.2-2025-12-11": {"input": 1.75, "output": 14.0},
    "gpt-5-mini-2025-08-07": {"input": 0.25, "output": 2.0},
    "gpt-5-nano-2025-08-07": {"input": 0.05, "output": 0.4},
    "gpt-4.1-2025-04-14": {"input": 2.0, "output": 8.0},
    "gpt-4.1-mini-2025-04-14": {"input": 0.4, "output": 1.6},
    "gpt-4.1-nano-2025-04-14": {"input": 0.1, "output": 0.4},
    "o3-2025-04-16": {"input": 2.0, "output": 8.0},
    "o4-mini-2025-04-16": {"input": 1.1, "output": 4.4},
    "gpt-oss-120b": {"input": 0.072, "output": 0.28},

    # Qwen
    "qwen3-coder-480b-a35b-instruct": {"input": 0.2, "output": 0.8},
    "qwen3-max-preview": {"input": 1.2, "output": 6},
    
    # Xai
    "grok-4-0709": {"input": 3.0, "output": 15.0},
    "grok-code-fast-1": {"input": 0.2, "output": 1.5},
    "grok-4-fast": {"input": 0.2, "output": 0.5},

    # Moonshot
    "kimi-k2-0711-preview": {"input": 0.6, "output": 2.5},
    "kimi-k2-0905-preview": {"input": 0.6, "output": 2.5},
}


def normalize_model_name(model_name: str) -> str:
    """Normalize model name for pricing lookup.

    Lowercases only.
    """
    return (model_name or "").strip().lower()


def get_price_per_m(model_name: str) -> Optional[Dict[str, float]]:
    """Return per-M token prices for given model, or None if unknown."""
    key = normalize_model_name(model_name)
    return MODEL_PRICES_PER_M.get(key)


def compute_cost_usd(model_name: str, input_tokens: float, output_tokens: float) -> Optional[float]:
    """Compute cost in USD given token usage and model pricing.

    Prices are per 1,000,000 tokens. If pricing unknown, returns None.
    """
    prices = get_price_per_m(model_name)
    if not prices:
        return None
    input_cost = (input_tokens / 1_000_000.0) * prices["input"]
    output_cost = (output_tokens / 1_000_000.0) * prices["output"]
    return float(round(input_cost + output_cost, 6))