Skip to main content

cc_switch_lib/proxy/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// 代理服务器配置
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct ProxyConfig {
6    /// 监听地址
7    pub listen_address: String,
8    /// 监听端口
9    pub listen_port: u16,
10    /// 最大重试次数
11    pub max_retries: u8,
12    /// 请求超时时间(秒)- 已废弃,保留兼容
13    pub request_timeout: u64,
14    /// 是否启用日志
15    pub enable_logging: bool,
16    /// 是否正在接管 Live 配置
17    #[serde(default)]
18    pub live_takeover_active: bool,
19    /// 流式首字超时(秒)- 等待首个数据块的最大时间,范围 1-120 秒,默认 60 秒
20    #[serde(default = "default_streaming_first_byte_timeout")]
21    pub streaming_first_byte_timeout: u64,
22    /// 流式静默超时(秒)- 两个数据块之间的最大间隔,范围 60-600 秒,填 0 禁用(防止中途卡住)
23    #[serde(default = "default_streaming_idle_timeout")]
24    pub streaming_idle_timeout: u64,
25    /// 非流式总超时(秒)- 非流式请求的总超时时间,范围 60-1200 秒,默认 600 秒(10 分钟)
26    #[serde(default = "default_non_streaming_timeout")]
27    pub non_streaming_timeout: u64,
28}
29
30fn default_streaming_first_byte_timeout() -> u64 {
31    60
32}
33
34fn default_streaming_idle_timeout() -> u64 {
35    120
36}
37
38fn default_non_streaming_timeout() -> u64 {
39    600
40}
41
42impl Default for ProxyConfig {
43    fn default() -> Self {
44        Self {
45            listen_address: "127.0.0.1".to_string(),
46            listen_port: 15721, // 使用较少占用的高位端口
47            max_retries: 3,
48            request_timeout: 600,
49            enable_logging: true,
50            live_takeover_active: false,
51            streaming_first_byte_timeout: 60,
52            streaming_idle_timeout: 120,
53            non_streaming_timeout: 600,
54        }
55    }
56}
57
58/// 代理服务器状态
59#[derive(Debug, Clone, Serialize, Deserialize, Default)]
60pub struct ProxyStatus {
61    /// 是否运行中
62    pub running: bool,
63    /// 监听地址
64    pub address: String,
65    /// 监听端口
66    pub port: u16,
67    /// 活跃连接数
68    pub active_connections: usize,
69    /// 总请求数
70    pub total_requests: u64,
71    /// 估算的输入 token 总量
72    #[serde(default)]
73    pub estimated_input_tokens_total: u64,
74    /// 估算的输出 token 总量
75    #[serde(default)]
76    pub estimated_output_tokens_total: u64,
77    /// 成功请求数
78    pub success_requests: u64,
79    /// 失败请求数
80    pub failed_requests: u64,
81    /// 成功率 (0-100)
82    pub success_rate: f32,
83    /// 运行时间(秒)
84    pub uptime_seconds: u64,
85    /// 当前使用的Provider名称
86    pub current_provider: Option<String>,
87    /// 当前Provider的ID
88    pub current_provider_id: Option<String>,
89    /// 最后一次请求时间
90    pub last_request_at: Option<String>,
91    /// 最后一次错误信息
92    pub last_error: Option<String>,
93    /// Provider故障转移次数
94    pub failover_count: u64,
95    /// managed external session 身份令牌
96    #[serde(default)]
97    pub managed_session_token: Option<String>,
98    /// 当前活跃的代理目标列表
99    #[serde(default)]
100    pub active_targets: Vec<ActiveTarget>,
101}
102
103/// 活跃的代理目标信息
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct ActiveTarget {
106    pub app_type: String, // "Claude" | "Codex" | "Gemini"
107    pub provider_name: String,
108    pub provider_id: String,
109}
110
111/// 代理服务器信息
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ProxyServerInfo {
114    pub address: String,
115    pub port: u16,
116    pub started_at: String,
117}
118
119/// 各应用的接管状态(是否改写该应用的 Live 配置指向本地代理)
120#[derive(Debug, Clone, Serialize, Deserialize, Default)]
121pub struct ProxyTakeoverStatus {
122    pub claude: bool,
123    pub codex: bool,
124    pub gemini: bool,
125}
126
127/// API 格式类型(预留,当前不需要格式转换)
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129#[allow(dead_code)]
130pub enum ApiFormat {
131    Claude,
132    OpenAI,
133    Gemini,
134}
135
136/// Provider健康状态
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ProviderHealth {
139    pub provider_id: String,
140    pub app_type: String,
141    pub is_healthy: bool,
142    pub consecutive_failures: u32,
143    pub last_success_at: Option<String>,
144    pub last_failure_at: Option<String>,
145    pub last_error: Option<String>,
146    pub updated_at: String,
147}
148
149/// Live 配置备份记录
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct LiveBackup {
152    /// 应用类型 (claude/codex/gemini)
153    pub app_type: String,
154    /// 原始配置 JSON
155    pub original_config: String,
156    /// 备份时间
157    pub backed_up_at: String,
158}
159
160/// 全局代理配置(统一字段,三行镜像)
161#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct GlobalProxyConfig {
164    /// 代理总开关
165    pub proxy_enabled: bool,
166    /// 监听地址
167    pub listen_address: String,
168    /// 监听端口
169    pub listen_port: u16,
170    /// 是否启用日志
171    pub enable_logging: bool,
172}
173
174/// 应用级代理配置(每个 app 独立)
175#[derive(Debug, Clone, Serialize, Deserialize)]
176#[serde(rename_all = "camelCase")]
177pub struct AppProxyConfig {
178    /// 应用类型 (claude/codex/gemini)
179    pub app_type: String,
180    /// 该 app 代理启用开关
181    pub enabled: bool,
182    /// 该 app 自动故障转移开关
183    pub auto_failover_enabled: bool,
184    /// 最大重试次数
185    pub max_retries: u32,
186    /// 流式首字超时(秒)
187    pub streaming_first_byte_timeout: u32,
188    /// 流式静默超时(秒)
189    pub streaming_idle_timeout: u32,
190    /// 非流式总超时(秒)
191    pub non_streaming_timeout: u32,
192    /// 熔断失败阈值
193    pub circuit_failure_threshold: u32,
194    /// 熔断恢复阈值
195    pub circuit_success_threshold: u32,
196    /// 熔断恢复等待时间(秒)
197    pub circuit_timeout_seconds: u32,
198    /// 错误率阈值
199    pub circuit_error_rate_threshold: f64,
200    /// 计算错误率的最小请求数
201    pub circuit_min_requests: u32,
202}
203
204/// 整流器配置
205///
206/// 存储在 settings 表中
207#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct RectifierConfig {
210    /// 总开关:是否启用整流器(默认开启)
211    #[serde(default = "default_true")]
212    pub enabled: bool,
213    /// 请求整流:启用 thinking 签名整流器(默认开启)
214    #[serde(default = "default_true")]
215    pub request_thinking_signature: bool,
216    /// 请求整流:启用 thinking budget 整流器(默认开启)
217    #[serde(default = "default_true")]
218    pub request_thinking_budget: bool,
219}
220
221fn default_true() -> bool {
222    true
223}
224
225fn default_log_level() -> String {
226    "info".to_string()
227}
228
229impl Default for RectifierConfig {
230    fn default() -> Self {
231        Self {
232            enabled: true,
233            request_thinking_signature: true,
234            request_thinking_budget: true,
235        }
236    }
237}
238
239/// 请求优化器配置
240///
241/// 存储在 settings 表中,key = "optimizer_config"
242/// 仅对 Bedrock Claude provider 生效
243#[derive(Debug, Clone, Serialize, Deserialize)]
244#[serde(rename_all = "camelCase")]
245pub struct OptimizerConfig {
246    #[serde(default)]
247    pub enabled: bool,
248    #[serde(default = "default_true")]
249    pub thinking_optimizer: bool,
250    #[serde(default = "default_true")]
251    pub cache_injection: bool,
252    #[serde(default = "default_cache_ttl")]
253    pub cache_ttl: String,
254}
255
256fn default_cache_ttl() -> String {
257    "1h".to_string()
258}
259
260impl Default for OptimizerConfig {
261    fn default() -> Self {
262        Self {
263            enabled: false,
264            thinking_optimizer: true,
265            cache_injection: true,
266            cache_ttl: "1h".to_string(),
267        }
268    }
269}
270
271/// 日志配置
272///
273/// 存储在 settings 表的 log_config 字段中(JSON 格式)
274#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(rename_all = "camelCase")]
276pub struct LogConfig {
277    /// 总开关:是否启用日志
278    #[serde(default = "default_true")]
279    pub enabled: bool,
280    /// 日志级别: error, warn, info, debug, trace
281    #[serde(default = "default_log_level")]
282    pub level: String,
283}
284
285impl Default for LogConfig {
286    fn default() -> Self {
287        Self {
288            enabled: true,
289            level: "info".to_string(),
290        }
291    }
292}
293
294impl LogConfig {
295    /// 将配置转换为 log::LevelFilter
296    pub fn to_level_filter(&self) -> log::LevelFilter {
297        if !self.enabled {
298            return log::LevelFilter::Off;
299        }
300        match self.level.to_lowercase().as_str() {
301            "error" => log::LevelFilter::Error,
302            "warn" => log::LevelFilter::Warn,
303            "info" => log::LevelFilter::Info,
304            "debug" => log::LevelFilter::Debug,
305            "trace" => log::LevelFilter::Trace,
306            _ => log::LevelFilter::Info,
307        }
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_rectifier_config_default_enabled() {
317        let config = RectifierConfig::default();
318        assert!(config.enabled, "整流器总开关默认应为 true");
319        assert!(
320            config.request_thinking_signature,
321            "thinking 签名整流器默认应为 true"
322        );
323        assert!(
324            config.request_thinking_budget,
325            "thinking budget 整流器默认应为 true"
326        );
327    }
328
329    #[test]
330    fn test_rectifier_config_serde_default() {
331        let json = "{}";
332        let config: RectifierConfig = serde_json::from_str(json).unwrap();
333        assert!(config.enabled);
334        assert!(config.request_thinking_signature);
335        assert!(config.request_thinking_budget);
336    }
337
338    #[test]
339    fn test_rectifier_config_serde_explicit_true() {
340        let json =
341            r#"{"enabled": true, "requestThinkingSignature": true, "requestThinkingBudget": true}"#;
342        let config: RectifierConfig = serde_json::from_str(json).unwrap();
343        assert!(config.enabled);
344        assert!(config.request_thinking_signature);
345        assert!(config.request_thinking_budget);
346    }
347
348    #[test]
349    fn test_rectifier_config_serde_partial_fields() {
350        let json = r#"{"enabled": true, "requestThinkingSignature": false}"#;
351        let config: RectifierConfig = serde_json::from_str(json).unwrap();
352        assert!(config.enabled);
353        assert!(!config.request_thinking_signature);
354        assert!(config.request_thinking_budget);
355    }
356
357    #[test]
358    fn test_rectifier_config_roundtrip_preserves_budget_flag() {
359        let config = RectifierConfig {
360            enabled: false,
361            request_thinking_signature: true,
362            request_thinking_budget: false,
363        };
364
365        let encoded = serde_json::to_string(&config).unwrap();
366        let decoded: RectifierConfig = serde_json::from_str(&encoded).unwrap();
367        assert!(!decoded.enabled);
368        assert!(decoded.request_thinking_signature);
369        assert!(!decoded.request_thinking_budget);
370    }
371
372    #[test]
373    fn test_log_config_default() {
374        let config = LogConfig::default();
375        assert!(config.enabled);
376        assert_eq!(config.level, "info");
377    }
378
379    #[test]
380    fn test_optimizer_config_default() {
381        let config = OptimizerConfig::default();
382        assert!(!config.enabled);
383        assert!(config.thinking_optimizer);
384        assert!(config.cache_injection);
385        assert_eq!(config.cache_ttl, "1h");
386    }
387
388    #[test]
389    fn test_optimizer_config_serde_default() {
390        let json = "{}";
391        let config: OptimizerConfig = serde_json::from_str(json).unwrap();
392        assert!(!config.enabled);
393        assert!(config.thinking_optimizer);
394        assert!(config.cache_injection);
395        assert_eq!(config.cache_ttl, "1h");
396    }
397
398    #[test]
399    fn test_log_config_serde_default() {
400        let json = "{}";
401        let config: LogConfig = serde_json::from_str(json).unwrap();
402        assert!(config.enabled);
403        assert_eq!(config.level, "info");
404    }
405
406    #[test]
407    fn test_log_config_to_level_filter() {
408        let config = LogConfig {
409            level: "error".to_string(),
410            ..Default::default()
411        };
412        assert_eq!(config.to_level_filter(), log::LevelFilter::Error);
413
414        let config = LogConfig {
415            level: "warn".to_string(),
416            ..Default::default()
417        };
418        assert_eq!(config.to_level_filter(), log::LevelFilter::Warn);
419
420        let config = LogConfig {
421            level: "info".to_string(),
422            ..Default::default()
423        };
424        assert_eq!(config.to_level_filter(), log::LevelFilter::Info);
425
426        let config = LogConfig {
427            level: "debug".to_string(),
428            ..Default::default()
429        };
430        assert_eq!(config.to_level_filter(), log::LevelFilter::Debug);
431
432        let config = LogConfig {
433            level: "trace".to_string(),
434            ..Default::default()
435        };
436        assert_eq!(config.to_level_filter(), log::LevelFilter::Trace);
437
438        // 无效级别回退到 info
439        let config = LogConfig {
440            level: "invalid".to_string(),
441            ..Default::default()
442        };
443        assert_eq!(config.to_level_filter(), log::LevelFilter::Info);
444
445        // 禁用时返回 Off
446        let config = LogConfig {
447            enabled: false,
448            level: "debug".to_string(),
449        };
450        assert_eq!(config.to_level_filter(), log::LevelFilter::Off);
451    }
452
453    #[test]
454    fn test_log_config_serde_roundtrip() {
455        let config = LogConfig {
456            enabled: true,
457            level: "debug".to_string(),
458        };
459        let json = serde_json::to_string(&config).unwrap();
460        let parsed: LogConfig = serde_json::from_str(&json).unwrap();
461        assert!(parsed.enabled);
462        assert_eq!(parsed.level, "debug");
463    }
464}