mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 16:26:16 +08:00
9196d07925
Add FailoverSwitchManager to handle provider switching after successful failover. This ensures the UI reflects the actual provider in use: - Create failover_switch.rs with deduplication and async switching logic - Pass AppHandle through ProxyService -> ProxyServer -> RequestForwarder - Update is_current in database when failover succeeds - Emit provider-switched event for frontend refresh - Update tray menu and live backup synchronously The switching runs asynchronously via tokio::spawn to avoid blocking API responses while still providing immediate UI feedback.
152 lines
4.6 KiB
Rust
152 lines
4.6 KiB
Rust
//! 请求上下文模块
|
||
//!
|
||
//! 提供请求生命周期的上下文管理,封装通用初始化逻辑
|
||
|
||
use crate::app_config::AppType;
|
||
use crate::provider::Provider;
|
||
use crate::proxy::{
|
||
forwarder::RequestForwarder, server::ProxyState, types::ProxyConfig, ProxyError,
|
||
};
|
||
use std::time::Instant;
|
||
|
||
/// 请求上下文
|
||
///
|
||
/// 贯穿整个请求生命周期,包含:
|
||
/// - 计时信息
|
||
/// - 代理配置
|
||
/// - 选中的 Provider 列表(用于故障转移)
|
||
/// - 请求模型名称
|
||
/// - 日志标签
|
||
pub struct RequestContext {
|
||
/// 请求开始时间
|
||
pub start_time: Instant,
|
||
/// 代理配置快照
|
||
pub config: ProxyConfig,
|
||
/// 选中的 Provider(故障转移链的第一个)
|
||
pub provider: Provider,
|
||
/// 完整的 Provider 列表(用于故障转移)
|
||
providers: Vec<Provider>,
|
||
/// 请求中的模型名称
|
||
pub request_model: String,
|
||
/// 日志标签(如 "Claude"、"Codex"、"Gemini")
|
||
pub tag: &'static str,
|
||
/// 应用类型字符串(如 "claude"、"codex"、"gemini")
|
||
pub app_type_str: &'static str,
|
||
/// 应用类型(预留,目前通过 app_type_str 使用)
|
||
#[allow(dead_code)]
|
||
pub app_type: AppType,
|
||
}
|
||
|
||
impl RequestContext {
|
||
/// 创建请求上下文
|
||
///
|
||
/// # Arguments
|
||
/// * `state` - 代理服务器状态
|
||
/// * `body` - 请求体 JSON
|
||
/// * `app_type` - 应用类型
|
||
/// * `tag` - 日志标签
|
||
/// * `app_type_str` - 应用类型字符串
|
||
///
|
||
/// # Errors
|
||
/// 返回 `ProxyError` 如果 Provider 选择失败
|
||
pub async fn new(
|
||
state: &ProxyState,
|
||
body: &serde_json::Value,
|
||
app_type: AppType,
|
||
tag: &'static str,
|
||
app_type_str: &'static str,
|
||
) -> Result<Self, ProxyError> {
|
||
let start_time = Instant::now();
|
||
let config = state.config.read().await.clone();
|
||
|
||
// 从请求体提取模型名称
|
||
let request_model = body
|
||
.get("model")
|
||
.and_then(|m| m.as_str())
|
||
.unwrap_or("unknown")
|
||
.to_string();
|
||
|
||
// 使用共享的 ProviderRouter 选择 Provider(熔断器状态跨请求保持)
|
||
// 注意:只在这里调用一次,结果传递给 forwarder,避免重复消耗 HalfOpen 名额
|
||
let providers = state
|
||
.provider_router
|
||
.select_providers(app_type_str)
|
||
.await
|
||
.map_err(|e| ProxyError::DatabaseError(e.to_string()))?;
|
||
|
||
let provider = providers
|
||
.first()
|
||
.cloned()
|
||
.ok_or(ProxyError::NoAvailableProvider)?;
|
||
|
||
log::info!(
|
||
"[{}] Provider: {}, model: {}, failover chain: {} providers",
|
||
tag,
|
||
provider.name,
|
||
request_model,
|
||
providers.len()
|
||
);
|
||
|
||
Ok(Self {
|
||
start_time,
|
||
config,
|
||
provider,
|
||
providers,
|
||
request_model,
|
||
tag,
|
||
app_type_str,
|
||
app_type,
|
||
})
|
||
}
|
||
|
||
/// 从 URI 提取模型名称(Gemini 专用)
|
||
///
|
||
/// Gemini API 的模型名称在 URI 中,格式如:
|
||
/// `/v1beta/models/gemini-pro:generateContent`
|
||
pub fn with_model_from_uri(mut self, uri: &axum::http::Uri) -> Self {
|
||
let endpoint = uri
|
||
.path_and_query()
|
||
.map(|pq| pq.as_str())
|
||
.unwrap_or(uri.path());
|
||
|
||
self.request_model = endpoint
|
||
.split('/')
|
||
.find(|s| s.starts_with("models/"))
|
||
.and_then(|s| s.strip_prefix("models/"))
|
||
.map(|s| s.split(':').next().unwrap_or(s))
|
||
.unwrap_or("unknown")
|
||
.to_string();
|
||
|
||
log::info!("[{}] 从 URI 提取模型: {}", self.tag, self.request_model);
|
||
self
|
||
}
|
||
|
||
/// 创建 RequestForwarder
|
||
///
|
||
/// 使用共享的 ProviderRouter,确保熔断器状态跨请求保持
|
||
pub fn create_forwarder(&self, state: &ProxyState) -> RequestForwarder {
|
||
RequestForwarder::new(
|
||
state.provider_router.clone(),
|
||
self.config.request_timeout,
|
||
self.config.max_retries,
|
||
state.status.clone(),
|
||
state.current_providers.clone(),
|
||
state.failover_manager.clone(),
|
||
state.app_handle.clone(),
|
||
)
|
||
}
|
||
|
||
/// 获取 Provider 列表(用于故障转移)
|
||
///
|
||
/// 返回在创建上下文时已选择的 providers,避免重复调用 select_providers()
|
||
pub fn get_providers(&self) -> Vec<Provider> {
|
||
self.providers.clone()
|
||
}
|
||
|
||
/// 计算请求延迟(毫秒)
|
||
#[inline]
|
||
pub fn latency_ms(&self) -> u64 {
|
||
self.start_time.elapsed().as_millis() as u64
|
||
}
|
||
}
|