Skip to main content

codetether_agent/telemetry/
cost.rs

1//! USD cost estimation for LLM requests.
2
3use serde::{Deserialize, Serialize};
4
5use super::tokens::TokenCounts;
6
7/// Estimated request cost, split into input/output components.
8///
9/// Prices are in **USD per million tokens** — the standard unit advertised by
10/// Anthropic, OpenAI, and Bedrock.
11///
12/// # Examples
13///
14/// ```rust
15/// use codetether_agent::telemetry::{CostEstimate, TokenCounts};
16///
17/// let cost = CostEstimate::from_tokens(
18///     &TokenCounts::new(2_000_000, 1_000_000),
19///     3.00,
20///     15.00,
21/// );
22/// assert!((cost.total_cost - 21.0).abs() < 1e-6);
23/// assert_eq!(cost.format_smart(), "$21.00");
24/// ```
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct CostEstimate {
27    /// Cost attributable to input tokens.
28    pub input_cost: f64,
29    /// Cost attributable to output tokens.
30    pub output_cost: f64,
31    /// `input_cost + output_cost`.
32    pub total_cost: f64,
33    /// ISO-4217 currency code (always `"USD"` today).
34    pub currency: String,
35}
36
37impl Default for CostEstimate {
38    fn default() -> Self {
39        Self {
40            input_cost: 0.0,
41            output_cost: 0.0,
42            total_cost: 0.0,
43            currency: "USD".to_string(),
44        }
45    }
46}
47
48impl CostEstimate {
49    /// Compute a cost estimate from token counts and per-million prices.
50    pub fn from_tokens(tokens: &TokenCounts, input_price: f64, output_price: f64) -> Self {
51        let input_cost = (tokens.input_tokens as f64 / 1_000_000.0) * input_price;
52        let output_cost = (tokens.output_tokens as f64 / 1_000_000.0) * output_price;
53        Self {
54            input_cost,
55            output_cost,
56            total_cost: input_cost + output_cost,
57            currency: "USD".to_string(),
58        }
59    }
60
61    /// Always 4 decimal places: `"$0.0042"`.
62    pub fn format_currency(&self) -> String {
63        format!("${:.4}", self.total_cost)
64    }
65
66    /// Sub-cent amounts use 4 decimals; `>= $0.01` uses 2.
67    pub fn format_smart(&self) -> String {
68        if self.total_cost < 0.01 {
69            format!("${:.4}", self.total_cost)
70        } else {
71            format!("${:.2}", self.total_cost)
72        }
73    }
74}