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(
43 counts: TurnUsageCounts,
44 model: nanocodex_oai_api::Model,
45 fast_mode: bool,
46 ) -> Self {
47 let (estimated_cost, cost_status) = if !counts.reported {
48 (None, CostStatus::UsageNotReported)
49 } else {
50 let usage = Usage {
51 input_tokens: counts.input_tokens,
52 input_tokens_details: Some(InputTokenDetails {
53 cached_tokens: counts.cached_input_tokens,
54 cache_write_tokens: counts.cache_write_input_tokens,
55 }),
56 output_tokens: counts.output_tokens,
57 output_tokens_details: None,
58 total_tokens: counts.total_tokens,
59 };
60 (
61 Some(Box::new(pricing::estimate_for_model(
62 &usage,
63 model,
64 if fast_mode {
65 ServiceTier::Priority
66 } else {
67 ServiceTier::Standard
68 },
69 ))),
70 CostStatus::EstimatedFromUsage,
71 )
72 };
73 Self {
74 input_tokens: counts.input_tokens,
75 cached_input_tokens: counts.cached_input_tokens,
76 cache_write_input_tokens: counts.cache_write_input_tokens,
77 output_tokens: counts.output_tokens,
78 reasoning_output_tokens: counts.reasoning_output_tokens,
79 total_tokens: counts.total_tokens,
80 estimated_cost,
81 cost_status,
82 }
83 }
84
85 #[must_use]
87 pub const fn input_tokens(&self) -> u64 {
88 self.input_tokens
89 }
90
91 #[must_use]
93 pub const fn cached_input_tokens(&self) -> u64 {
94 self.cached_input_tokens
95 }
96
97 #[must_use]
99 pub const fn cache_write_input_tokens(&self) -> u64 {
100 self.cache_write_input_tokens
101 }
102
103 #[must_use]
105 pub const fn output_tokens(&self) -> u64 {
106 self.output_tokens
107 }
108
109 #[must_use]
111 pub const fn reasoning_output_tokens(&self) -> u64 {
112 self.reasoning_output_tokens
113 }
114
115 #[must_use]
117 pub const fn total_tokens(&self) -> u64 {
118 self.total_tokens
119 }
120
121 #[must_use]
127 pub fn estimated_cost(&self) -> Option<&EstimatedUsdCost> {
128 self.estimated_cost.as_deref()
129 }
130
131 #[must_use]
133 pub const fn cost_status(&self) -> CostStatus {
134 self.cost_status
135 }
136}