lean_ctx/proxy/
usage_parity.rs1use crate::core::context_kernel::provider_parity;
4use crate::core::context_kernel::token_envelope::{ProviderKind, TokenEnvelope};
5
6pub fn provider_kind_from_label(label: &str) -> ProviderKind {
8 match label {
9 "Anthropic" => ProviderKind::Anthropic,
10 "OpenAI" | "ChatGPT" => ProviderKind::OpenAi,
11 "Gemini" => ProviderKind::Gemini,
12 "Bedrock" => ProviderKind::Bedrock,
13 "Azure" => ProviderKind::Azure,
14 _ if label.to_ascii_lowercase().contains("openrouter") => ProviderKind::OpenRouter,
15 _ => ProviderKind::Unknown,
16 }
17}
18
19pub fn real_usage_to_envelope(usage: &super::usage::RealUsage, label: &str) -> TokenEnvelope {
21 TokenEnvelope {
22 model: usage.model.clone(),
23 provider: provider_kind_from_label(label),
24 input_tokens: usage.input_tokens as usize,
25 output_tokens: usage.output_tokens as usize,
26 cache_read_tokens: usage.cache_read_tokens as usize,
27 cache_write_tokens: usage.cache_write_tokens as usize,
28 reasoning_tokens: usage.reasoning_tokens as usize,
29 cost_usd: usage.provider_cost_usd,
30 tokens_saved: 0,
31 is_retry: false,
32 }
33}
34
35pub fn label_from_base_url(base_url: &str) -> &'static str {
37 provider_parity::provider_display_name(provider_parity::detect_provider(base_url))
38}
39
40#[cfg(test)]
41mod tests {
42 use super::{label_from_base_url, provider_kind_from_label, real_usage_to_envelope};
43 use crate::core::context_kernel::token_envelope::ProviderKind;
44 use crate::proxy::usage::RealUsage;
45
46 #[test]
47 fn kind_from_anthropic_label() {
48 assert_eq!(
49 provider_kind_from_label("Anthropic"),
50 ProviderKind::Anthropic
51 );
52 }
53
54 #[test]
55 fn kind_from_openai_label() {
56 assert_eq!(provider_kind_from_label("OpenAI"), ProviderKind::OpenAi);
57 }
58
59 #[test]
60 fn kind_from_chatgpt_label() {
61 assert_eq!(provider_kind_from_label("ChatGPT"), ProviderKind::OpenAi);
62 }
63
64 #[test]
65 fn kind_from_bedrock_label() {
66 assert_eq!(provider_kind_from_label("Bedrock"), ProviderKind::Bedrock);
67 }
68
69 #[test]
70 fn kind_from_azure_label() {
71 assert_eq!(provider_kind_from_label("Azure"), ProviderKind::Azure);
72 }
73
74 #[test]
75 fn kind_from_gemini_label() {
76 assert_eq!(provider_kind_from_label("Gemini"), ProviderKind::Gemini);
77 }
78
79 #[test]
80 fn kind_from_unknown() {
81 assert_eq!(provider_kind_from_label("Other"), ProviderKind::Unknown);
82 }
83
84 #[test]
85 fn real_usage_full_convert() {
86 let usage = RealUsage {
87 model: "model-1".to_owned(),
88 input_tokens: 11,
89 output_tokens: 12,
90 cache_read_tokens: 13,
91 cache_write_tokens: 14,
92 reasoning_tokens: 15,
93 provider_cost_usd: Some(0.25),
94 ..RealUsage::default()
95 };
96
97 let envelope = real_usage_to_envelope(&usage, "Anthropic");
98
99 assert_eq!(envelope.model, "model-1");
100 assert_eq!(envelope.provider, ProviderKind::Anthropic);
101 assert_eq!(envelope.input_tokens, 11);
102 assert_eq!(envelope.output_tokens, 12);
103 assert_eq!(envelope.cache_read_tokens, 13);
104 assert_eq!(envelope.cache_write_tokens, 14);
105 assert_eq!(envelope.reasoning_tokens, 15);
106 assert_eq!(envelope.cost_usd, Some(0.25));
107 }
108
109 #[test]
110 fn label_from_url_openai() {
111 assert_eq!(label_from_base_url("https://api.openai.com/v1"), "OpenAI");
112 }
113}