1use serde::{Deserialize, Serialize};
4
5use crate::UsageReport;
6
7pub trait CostPolicy: Send + Sync {
9 fn estimate_cost(&self, usage: &UsageReport) -> CostReport;
11}
12
13#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
14pub struct StaticRateTable {
19 pub currency: String,
21 pub input_token_micros_per_million: u64,
23 pub output_token_micros_per_million: u64,
25 pub tool_call_micros: u64,
27}
28
29impl StaticRateTable {
30 pub fn new(
32 currency: impl Into<String>,
33 input_token_micros_per_million: u64,
34 output_token_micros_per_million: u64,
35 tool_call_micros: u64,
36 ) -> Self {
37 Self {
38 currency: currency.into(),
39 input_token_micros_per_million,
40 output_token_micros_per_million,
41 tool_call_micros,
42 }
43 }
44}
45
46impl CostPolicy for StaticRateTable {
47 fn estimate_cost(&self, usage: &UsageReport) -> CostReport {
48 let input_cost_micros = usage
49 .provider_input_tokens
50 .saturating_mul(self.input_token_micros_per_million)
51 / 1_000_000;
52 let output_cost_micros = usage
53 .provider_output_tokens
54 .saturating_mul(self.output_token_micros_per_million)
55 / 1_000_000;
56 let tool_cost_micros = usage.tool_call_count.saturating_mul(self.tool_call_micros);
57 let total_cost_micros = input_cost_micros
58 .saturating_add(output_cost_micros)
59 .saturating_add(tool_cost_micros);
60 let mut limitations = Vec::new();
61 if usage.provider_call_count == 0 && usage.tool_call_count == 0 {
62 limitations.push("cost report has no provider or tool usage".to_string());
63 }
64 CostReport {
65 currency: self.currency.clone(),
66 input_cost_micros,
67 output_cost_micros,
68 tool_cost_micros,
69 total_cost_micros,
70 limitations,
71 }
72 }
73}
74
75#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
76pub struct CostReport {
78 pub currency: String,
80 pub input_cost_micros: u64,
82 pub output_cost_micros: u64,
84 pub tool_cost_micros: u64,
86 pub total_cost_micros: u64,
88 pub limitations: Vec<String>,
90}