Skip to main content

orch/store/
pricing.rs

1/// Token usage for an agent run.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3pub struct TokenUsage {
4    pub input_tokens: u64,
5    pub output_tokens: u64,
6}
7
8impl TokenUsage {
9    pub fn total_tokens(self) -> u64 {
10        self.input_tokens.saturating_add(self.output_tokens)
11    }
12}
13
14/// Per-1M token pricing in USD.
15#[derive(Debug, Clone, Copy, PartialEq)]
16pub struct ModelPricing {
17    pub input_per_million_usd: f64,
18    pub output_per_million_usd: f64,
19}
20
21impl ModelPricing {
22    pub fn estimate_cost_usd(self, usage: TokenUsage) -> CostEstimate {
23        let input_cost = (usage.input_tokens as f64 / 1_000_000.0) * self.input_per_million_usd;
24        let output_cost = (usage.output_tokens as f64 / 1_000_000.0) * self.output_per_million_usd;
25        CostEstimate {
26            input_cost_usd: input_cost,
27            output_cost_usd: output_cost,
28            total_cost_usd: input_cost + output_cost,
29        }
30    }
31}
32
33/// Cost estimate in USD.
34#[derive(Debug, Clone, Copy, PartialEq, Default)]
35pub struct CostEstimate {
36    pub input_cost_usd: f64,
37    pub output_cost_usd: f64,
38    pub total_cost_usd: f64,
39}
40/// Zero-cost pricing (subscription-based or free-tier models).
41const FREE: ModelPricing = ModelPricing {
42    input_per_million_usd: 0.0,
43    output_per_million_usd: 0.0,
44};
45
46/// Resolve model pricing using a built-in table and normalized model aliases.
47pub fn pricing_for_model(model: &str) -> ModelPricing {
48    let normalized = model.trim().to_lowercase();
49
50    // GitHub Copilot subscription models — billed to Copilot account, not per-token.
51    if normalized.starts_with("github-copilot/") {
52        return FREE;
53    }
54
55    // Free-tier models (e.g. minimax-m2.5-free, nemotron-3-super-free).
56    if normalized.contains("-free") || normalized.ends_with("free") {
57        return FREE;
58    }
59
60    // DeepSeek models (e.g. deepseek-r1, deepseek-v3).
61    if normalized.starts_with("deepseek") {
62        return ModelPricing {
63            input_per_million_usd: 0.55,
64            output_per_million_usd: 2.19,
65        };
66    }
67
68    // OpenAI models
69    if normalized == "o3" {
70        return ModelPricing {
71            input_per_million_usd: 2.0,
72            output_per_million_usd: 8.0,
73        };
74    }
75    if normalized == "o4-mini" || normalized.contains("gpt-4.1-mini") {
76        return ModelPricing {
77            input_per_million_usd: 0.15,
78            output_per_million_usd: 0.6,
79        };
80    }
81    if normalized.contains("gpt-4.1") && !normalized.contains("mini") {
82        return ModelPricing {
83            input_per_million_usd: 2.0,
84            output_per_million_usd: 8.0,
85        };
86    }
87    // Codex v5 mini variants (gpt-5.1-codex-mini, gpt-5.4-mini, etc.).
88    if normalized.starts_with("gpt-5") && normalized.contains("mini") {
89        return ModelPricing {
90            input_per_million_usd: 0.15,
91            output_per_million_usd: 0.6,
92        };
93    }
94    // Codex v5 full variants (gpt-5.2-codex, gpt-5.3-codex, etc.).
95    if normalized.starts_with("gpt-5") {
96        return ModelPricing {
97            input_per_million_usd: 2.0,
98            output_per_million_usd: 8.0,
99        };
100    }
101
102    // Claude models matched by family substring.
103    if normalized.contains("opus") {
104        return ModelPricing {
105            input_per_million_usd: 15.0,
106            output_per_million_usd: 75.0,
107        };
108    }
109    if normalized.contains("sonnet") {
110        return ModelPricing {
111            input_per_million_usd: 3.0,
112            output_per_million_usd: 15.0,
113        };
114    }
115    if normalized.contains("haiku") {
116        return ModelPricing {
117            input_per_million_usd: 0.8,
118            output_per_million_usd: 4.0,
119        };
120    }
121
122    // Fallback baseline when model is unknown.
123    ModelPricing {
124        input_per_million_usd: 1.0,
125        output_per_million_usd: 4.0,
126    }
127}