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