Skip to main content

cairn_runtime/
pricing.rs

1use async_trait::async_trait;
2
3use crate::model::{Model, ModelError, ModelRequest, ModelResponse, ModelStream, Usage};
4
5/// USD per million tokens. Prices change often, so Cairn never hardcodes
6/// them; set them from your provider's current price sheet. A model that is
7/// genuinely free should say so explicitly with `Pricing::new(0.0, 0.0)`.
8#[derive(Debug, Clone, Copy, Default, PartialEq)]
9pub struct Pricing {
10    pub input: f64,
11    pub output: f64,
12    pub cache_read: f64,
13    pub cache_write: f64,
14}
15
16impl Pricing {
17    pub fn new(input: f64, output: f64) -> Self {
18        Self { input, output, cache_read: input * 0.1, cache_write: input * 1.25 }
19    }
20
21    pub fn cost(&self, u: &Usage) -> f64 {
22        (u.input_tokens as f64 * self.input
23            + u.output_tokens as f64 * self.output
24            + u.cache_read_tokens as f64 * self.cache_read
25            + u.cache_write_tokens as f64 * self.cache_write)
26            / 1_000_000.0
27    }
28
29    /// Field-wise maximum: a conservative price when the serving model of
30    /// several is not known.
31    pub fn max(self, other: Pricing) -> Pricing {
32        Pricing {
33            input: self.input.max(other.input),
34            output: self.output.max(other.output),
35            cache_read: self.cache_read.max(other.cache_read),
36            cache_write: self.cache_write.max(other.cache_write),
37        }
38    }
39}
40
41/// Attaches pricing to any model, e.g. one built from a `provider:model` spec.
42pub struct Priced<M> {
43    inner: M,
44    pricing: Pricing,
45}
46
47impl<M: Model> Priced<M> {
48    pub fn new(inner: M, pricing: Pricing) -> Self {
49        Self { inner, pricing }
50    }
51}
52
53#[async_trait]
54impl<M: Model> Model for Priced<M> {
55    fn id(&self) -> String {
56        self.inner.id()
57    }
58
59    async fn complete(&self, request: &ModelRequest) -> Result<ModelResponse, ModelError> {
60        self.inner.complete(request).await
61    }
62
63    async fn stream(&self, request: &ModelRequest) -> Result<ModelStream, ModelError> {
64        self.inner.stream(request).await
65    }
66
67    fn pricing(&self) -> Option<Pricing> {
68        Some(self.pricing)
69    }
70
71    fn unpriced_models(&self) -> Vec<String> {
72        Vec::new()
73    }
74}