use serde::{Deserialize, Serialize};
use crate::UsageReport;
pub trait CostPolicy: Send + Sync {
fn estimate_cost(&self, usage: &UsageReport) -> CostReport;
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StaticRateTable {
pub currency: String,
pub input_token_micros_per_million: u64,
pub output_token_micros_per_million: u64,
pub tool_call_micros: u64,
}
impl StaticRateTable {
pub fn new(
currency: impl Into<String>,
input_token_micros_per_million: u64,
output_token_micros_per_million: u64,
tool_call_micros: u64,
) -> Self {
Self {
currency: currency.into(),
input_token_micros_per_million,
output_token_micros_per_million,
tool_call_micros,
}
}
}
impl CostPolicy for StaticRateTable {
fn estimate_cost(&self, usage: &UsageReport) -> CostReport {
let input_cost_micros = usage
.provider_input_tokens
.saturating_mul(self.input_token_micros_per_million)
/ 1_000_000;
let output_cost_micros = usage
.provider_output_tokens
.saturating_mul(self.output_token_micros_per_million)
/ 1_000_000;
let tool_cost_micros = usage.tool_call_count.saturating_mul(self.tool_call_micros);
let total_cost_micros = input_cost_micros
.saturating_add(output_cost_micros)
.saturating_add(tool_cost_micros);
let mut limitations = Vec::new();
if usage.provider_call_count == 0 && usage.tool_call_count == 0 {
limitations.push("cost report has no provider or tool usage".to_string());
}
CostReport {
currency: self.currency.clone(),
input_cost_micros,
output_cost_micros,
tool_cost_micros,
total_cost_micros,
limitations,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CostReport {
pub currency: String,
pub input_cost_micros: u64,
pub output_cost_micros: u64,
pub tool_cost_micros: u64,
pub total_cost_micros: u64,
pub limitations: Vec<String>,
}