Spaces:
Sleeping
Sleeping
| // 上游客户端实现 | |
| // 基于高性能通讯接口封装 | |
| use reqwest::{header, Client, Response}; | |
| use serde_json::Value; | |
| use tokio::time::Duration; | |
| // 生产环境端点 | |
| const V1_INTERNAL_BASE_URL: &str = "https://cloudcode-pa.googleapis.com/v1internal"; | |
| pub struct UpstreamClient { | |
| http_client: Client, | |
| } | |
| impl UpstreamClient { | |
| pub fn new(proxy_config: Option<crate::proxy::config::UpstreamProxyConfig>) -> Self { | |
| let mut builder = Client::builder() | |
| .timeout(Duration::from_secs(600)) | |
| .user_agent("antigravity/1.11.9 windows/amd64"); | |
| if let Some(config) = proxy_config { | |
| if config.enabled && !config.url.is_empty() { | |
| if let Ok(proxy) = reqwest::Proxy::all(&config.url) { | |
| builder = builder.proxy(proxy); | |
| tracing::info!("UpstreamClient enabled proxy: {}", config.url); | |
| } | |
| } | |
| } | |
| let http_client = builder.build().expect("Failed to create HTTP client"); | |
| Self { http_client } | |
| } | |
| /// 构建 v1internal URL | |
| /// | |
| /// 构建 API 请求地址 | |
| fn build_url(method: &str, query_string: Option<&str>) -> String { | |
| if let Some(qs) = query_string { | |
| format!("{}:{}?{}", V1_INTERNAL_BASE_URL, method, qs) | |
| } else { | |
| format!("{}:{}", V1_INTERNAL_BASE_URL, method) | |
| } | |
| } | |
| /// 调用 v1internal API(基础方法) | |
| /// | |
| /// 发起基础网络请求 | |
| pub async fn call_v1_internal( | |
| &self, | |
| method: &str, | |
| access_token: &str, | |
| body: Value, | |
| query_string: Option<&str>, | |
| ) -> Result<Response, String> { | |
| let url = Self::build_url(method, query_string); | |
| // 构建 Headers | |
| let mut headers = header::HeaderMap::new(); | |
| headers.insert(header::CONTENT_TYPE, header::HeaderValue::from_static("application/json")); | |
| headers.insert(header::AUTHORIZATION, header::HeaderValue::from_str(&format!("Bearer {}", access_token)).map_err(|e| e.to_string())?); | |
| // 设置自定义 User-Agent | |
| headers.insert(header::USER_AGENT, header::HeaderValue::from_static("antigravity/1.11.9 windows/amd64")); | |
| // 记录请求详情以便调试 404 | |
| let response = self | |
| .http_client | |
| .post(&url) | |
| .headers(headers) // Apply all headers at once | |
| .json(&body) | |
| .send() | |
| .await | |
| .map_err(|e| format!("HTTP request failed: {}", e))?; | |
| Ok(response) | |
| } | |
| /// 调用 v1internal API(带 429 重试,支持闭包) | |
| /// | |
| /// 带容错和重试的核心请求逻辑 | |
| /// | |
| /// # Arguments | |
| /// * `method` - API method (e.g., "generateContent") | |
| /// * `query_string` - Optional query string (e.g., "?alt=sse") | |
| /// * `get_credentials` - 闭包,获取凭证(支持账号轮换) | |
| /// * `build_body` - 闭包,接收 project_id 构建请求体 | |
| /// * `max_attempts` - 最大重试次数 | |
| /// | |
| /// # Returns | |
| /// HTTP Response | |
| // 已移除弃用的重试方法 (call_v1_internal_with_retry) | |
| // 已移除弃用的辅助方法 (parse_retry_delay) | |
| // 已移除弃用的辅助方法 (parse_duration_ms) | |
| /// 获取可用模型列表 | |
| /// | |
| /// 获取远端模型列表 | |
| pub async fn fetch_available_models(&self, access_token: &str) -> Result<Value, String> { | |
| let url = Self::build_url("fetchAvailableModels", None); | |
| let mut headers = header::HeaderMap::new(); | |
| headers.insert(header::CONTENT_TYPE, header::HeaderValue::from_static("application/json")); | |
| headers.insert(header::AUTHORIZATION, header::HeaderValue::from_str(&format!("Bearer {}", access_token)).map_err(|e| e.to_string())?); | |
| headers.insert(header::USER_AGENT, header::HeaderValue::from_static("antigravity/1.11.9 windows/amd64")); | |
| let response = self.http_client | |
| .post(&url) | |
| .headers(headers) | |
| .json(&serde_json::json!({})) | |
| .send() | |
| .await | |
| .map_err(|e| format!("Request failed: {}", e))?; | |
| if !response.status().is_success() { | |
| return Err(format!("Upstream error: {}", response.status())); | |
| } | |
| let json: Value = response.json().await.map_err(|e| format!("Parse json failed: {}", e))?; | |
| Ok(json) | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| fn test_build_url() { | |
| let url1 = UpstreamClient::build_url("generateContent", None); | |
| assert_eq!( | |
| url1, | |
| "https://cloudcode-pa.googleapis.com/v1internal:generateContent" | |
| ); | |
| let url2 = UpstreamClient::build_url("streamGenerateContent", Some("alt=sse")); | |
| assert_eq!( | |
| url2, | |
| "https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse" | |
| ); | |
| } | |
| } | |