1pub use nanocodex_oai_api::pricing::{CostStatus, EstimatedUsdCost, ServiceTier, UsdAmount};
2use nanocodex_oai_api::{
3 pricing,
4 responses::{InputTokenDetails, Usage},
5};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
17#[allow(clippy::struct_field_names)]
18pub struct TurnUsage {
19 input_tokens: u64,
20 cached_input_tokens: u64,
21 cache_write_input_tokens: u64,
22 output_tokens: u64,
23 reasoning_output_tokens: u64,
24 total_tokens: u64,
25 estimated_cost: Option<Box<EstimatedUsdCost>>,
26 cost_status: CostStatus,
27}
28
29#[allow(clippy::struct_field_names)]
30#[derive(Clone, Copy)]
31pub(crate) struct TurnUsageCounts {
32 pub(crate) input_tokens: u64,
33 pub(crate) cached_input_tokens: u64,
34 pub(crate) cache_write_input_tokens: u64,
35 pub(crate) output_tokens: u64,
36 pub(crate) reasoning_output_tokens: u64,
37 pub(crate) total_tokens: u64,
38 pub(crate) reported: bool,
39}
40
41impl TurnUsage {
42 pub(crate) fn from_counts(counts: TurnUsageCounts, fast_mode: bool) -> Self {
43 let (estimated_cost, cost_status) = if counts.reported {
44 let usage = Usage {
45 input_tokens: counts.input_tokens,
46 input_tokens_details: Some(InputTokenDetails {
47 cached_tokens: counts.cached_input_tokens,
48 cache_write_tokens: counts.cache_write_input_tokens,
49 }),
50 output_tokens: counts.output_tokens,
51 output_tokens_details: None,
52 total_tokens: counts.total_tokens,
53 };
54 (
55 Some(Box::new(pricing::estimate(
56 &usage,
57 if fast_mode {
58 ServiceTier::Priority
59 } else {
60 ServiceTier::Standard
61 },
62 ))),
63 CostStatus::EstimatedFromUsage,
64 )
65 } else {
66 (None, CostStatus::UsageNotReported)
67 };
68 Self {
69 input_tokens: counts.input_tokens,
70 cached_input_tokens: counts.cached_input_tokens,
71 cache_write_input_tokens: counts.cache_write_input_tokens,
72 output_tokens: counts.output_tokens,
73 reasoning_output_tokens: counts.reasoning_output_tokens,
74 total_tokens: counts.total_tokens,
75 estimated_cost,
76 cost_status,
77 }
78 }
79
80 #[must_use]
82 pub const fn input_tokens(&self) -> u64 {
83 self.input_tokens
84 }
85
86 #[must_use]
88 pub const fn cached_input_tokens(&self) -> u64 {
89 self.cached_input_tokens
90 }
91
92 #[must_use]
94 pub const fn cache_write_input_tokens(&self) -> u64 {
95 self.cache_write_input_tokens
96 }
97
98 #[must_use]
100 pub const fn output_tokens(&self) -> u64 {
101 self.output_tokens
102 }
103
104 #[must_use]
106 pub const fn reasoning_output_tokens(&self) -> u64 {
107 self.reasoning_output_tokens
108 }
109
110 #[must_use]
112 pub const fn total_tokens(&self) -> u64 {
113 self.total_tokens
114 }
115
116 #[must_use]
122 pub fn estimated_cost(&self) -> Option<&EstimatedUsdCost> {
123 self.estimated_cost.as_deref()
124 }
125
126 #[must_use]
128 pub const fn cost_status(&self) -> CostStatus {
129 self.cost_status
130 }
131}