1use serde::Deserialize;
4
5#[derive(Debug, Clone, Copy, Deserialize)]
7pub struct ModelPricingSpec {
8 pub input_per_million: f64,
9 pub output_per_million: f64,
10 pub cache_hit_input_per_million: Option<f64>,
12}
13
14#[derive(Debug, Clone, Deserialize)]
16pub struct ModelSpec {
17 pub name: String,
18 pub context_window: usize,
20 pub pricing: Option<ModelPricingSpec>,
22}
23
24#[derive(Debug, Clone, Deserialize)]
25pub struct ProviderPreset {
26 pub id: String,
27 pub name: String,
28 pub provider_type: String,
29 pub api_base: String,
30 pub default_model: String,
31 pub models: Vec<ModelSpec>,
32 pub mainland_accessible: bool,
33 pub key_env: String,
34 pub key_url: String,
35}
36
37#[derive(Deserialize)]
38struct PresetsFile {
39 providers: Vec<ProviderPreset>,
40}
41
42static PRESETS_TOML: &str = include_str!("../providers.toml");
43
44pub fn all_presets() -> &'static [ProviderPreset] {
45 use std::sync::OnceLock;
46 static CACHE: OnceLock<Vec<ProviderPreset>> = OnceLock::new();
47 CACHE.get_or_init(|| {
48 toml::from_str::<PresetsFile>(PRESETS_TOML)
49 .expect("providers.toml is bundled at compile time and must be valid")
50 .providers
51 })
52}
53
54pub fn find_preset(id: &str) -> Option<&'static ProviderPreset> {
55 all_presets().iter().find(|p| p.id == id)
56}
57
58pub fn find_preset_by_api_base(url: &str) -> Option<&'static ProviderPreset> {
63 all_presets().iter().find(|p| p.api_base == url)
64}
65
66pub fn find_model_pricing(model: &str) -> Option<&'static ModelPricingSpec> {
69 for preset in all_presets() {
70 for spec in &preset.models {
71 if spec.name == model {
72 return spec.pricing.as_ref();
73 }
74 }
75 }
76 None
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn all_presets_non_empty() {
85 assert!(all_presets().len() > 10);
86 }
87
88 #[test]
89 fn find_preset_anthropic() {
90 let preset = find_preset("anthropic").unwrap();
91 assert_eq!(preset.provider_type, "anthropic");
92 }
93
94 #[test]
95 fn find_preset_unknown_returns_none() {
96 assert!(find_preset("bogus").is_none());
97 }
98
99 #[test]
100 fn all_presets_have_valid_provider_type() {
101 for p in all_presets() {
102 assert!(
103 p.provider_type == "openai" || p.provider_type == "anthropic",
104 "preset {} has invalid provider_type: {}",
105 p.id,
106 p.provider_type
107 );
108 }
109 }
110
111 #[test]
112 fn default_preset_is_anthropic() {
113 assert_eq!(all_presets()[0].id, "anthropic");
114 }
115
116 #[test]
117 fn find_preset_deepseek_api_base() {
118 let preset = find_preset("deepseek").unwrap();
119 assert_eq!(preset.api_base, "https://api.deepseek.com/v1");
120 }
121
122 #[test]
123 fn find_preset_by_api_base_known() {
124 let preset =
125 find_preset_by_api_base("https://api.deepseek.com/v1").expect("deepseek preset");
126 assert_eq!(preset.id, "deepseek");
127 }
128
129 #[test]
130 fn find_preset_by_api_base_unknown() {
131 assert!(find_preset_by_api_base("https://example.com/v1").is_none());
132 }
133}