Skip to main content

cc_switch_lib/services/provider/
models.rs

1use reqwest::Client;
2use serde_json::Value;
3use std::collections::HashSet;
4use std::time::Duration;
5
6use crate::error::AppError;
7
8use super::ProviderService;
9
10impl ProviderService {
11    /// 尝试从远端拉取模型列表
12    pub async fn fetch_provider_models(
13        base_url: &str,
14        api_key: Option<&str>,
15    ) -> Result<Vec<String>, AppError> {
16        let base_url = base_url.trim().trim_end_matches('/');
17        if base_url.is_empty() {
18            return Err(AppError::localized(
19                "fetch.invalid_url",
20                "URL 不能为空",
21                "URL cannot be empty",
22            ));
23        }
24
25        let mut candidate_urls = Vec::new();
26
27        // 如果用户直接填了 /v1/models 或者 /models,我们就直接用
28        if base_url.ends_with("/models") {
29            candidate_urls.push(base_url.to_string());
30        } else {
31            // 智能适配:如果没带 /models,尝试追加
32            candidate_urls.push(format!("{}/models", base_url));
33            if !base_url.ends_with("/v1") && !base_url.ends_with("/v1beta") {
34                candidate_urls.push(format!("{}/v1/models", base_url));
35            }
36        }
37
38        let client = Client::builder()
39            .timeout(Duration::from_secs(5))
40            .build()
41            .map_err(|e| AppError::Message(e.to_string()))?;
42
43        let mut last_err_zh = None;
44        let mut last_err_en = None;
45
46        for url in candidate_urls {
47            let mut req = client.get(&url);
48            if let Some(key) = api_key {
49                let key = key.trim();
50                // 同时添加 OpenAI 的 Bearer 和 Anthropic 的 x-api-key 格式,代理服务通常会接受其中之一
51                req = req
52                    .header("Authorization", format!("Bearer {}", key))
53                    .header("x-api-key", key);
54            }
55
56            match req.send().await {
57                Ok(resp) => {
58                    if resp.status().is_success() {
59                        if let Ok(json) = resp.json::<Value>().await {
60                            let mut models = Vec::new();
61
62                            // 测试格式 1: OpenAI 兼容格式 {"data": [{"id": "gpt-4o"}]}
63                            if let Some(data) = json.get("data").and_then(|d| d.as_array()) {
64                                for item in data {
65                                    if let Some(id) = item.get("id").and_then(|i| i.as_str()) {
66                                        models.push(id.to_string());
67                                    }
68                                }
69                            }
70
71                            // 测试格式 2: Gemini 格式 {"models": [{"name": "models/gemini-pro"}]}
72                            if models.is_empty() {
73                                if let Some(data) = json.get("models").and_then(|d| d.as_array()) {
74                                    for item in data {
75                                        if let Some(name) =
76                                            item.get("name").and_then(|i| i.as_str())
77                                        {
78                                            let id = name.strip_prefix("models/").unwrap_or(name);
79                                            models.push(id.to_string());
80                                        }
81                                    }
82                                }
83                            }
84
85                            // 测试格式 3: 直接的数组格式 [{"id": "llama-3"}]
86                            if models.is_empty() {
87                                if let Some(arr) = json.as_array() {
88                                    for item in arr {
89                                        if let Some(id) = item.get("id").and_then(|i| i.as_str()) {
90                                            models.push(id.to_string());
91                                        }
92                                    }
93                                }
94                            }
95
96                            if !models.is_empty() {
97                                // 保序去重,避免非相邻重复项残留。
98                                let mut seen = HashSet::new();
99                                models.retain(|model| seen.insert(model.clone()));
100                                return Ok(models);
101                            } else {
102                                last_err_zh =
103                                    Some(format!("未能在响应中找到模型列表 (URL: {})", url));
104                                last_err_en =
105                                    Some(format!("No model list found in response (URL: {})", url));
106                            }
107                        } else {
108                            last_err_zh = Some(format!("无法解析 JSON 响应 (URL: {})", url));
109                            last_err_en =
110                                Some(format!("Failed to parse JSON response (URL: {})", url));
111                        }
112                    } else {
113                        let err = format!("HTTP {} (URL: {})", resp.status(), url);
114                        last_err_zh = Some(err.clone());
115                        last_err_en = Some(err);
116                    }
117                }
118                Err(e) => {
119                    let err = e.to_string();
120                    last_err_zh = Some(err.clone());
121                    last_err_en = Some(err);
122                }
123            }
124        }
125
126        let err_zh = last_err_zh.unwrap_or_else(|| "未知错误".to_string());
127        let err_en = last_err_en.unwrap_or_else(|| "Unknown error".to_string());
128        Err(AppError::localized(
129            "fetch.failed",
130            format!("拉取失败: {}", err_zh),
131            format!("Fetch failed: {}", err_en),
132        ))
133    }
134}