fix(proxy): resolve HalfOpen counter underflow and config field inconsistencies

- Fix HalfOpen counter underflow: increment half_open_requests when
  transitioning from Open to HalfOpen to prevent underflow in
  record_success/record_failure

- Fix Gemini config field names: unify to GEMINI_API_KEY and
  GOOGLE_GEMINI_BASE_URL (removed GOOGLE_API_KEY and GEMINI_API_BASE)

- Fix Codex proxy takeover: write base_url to config.toml instead of
  OPENAI_BASE_URL in auth.json (Codex CLI reads from config.toml)
This commit is contained in:
Jason
2025-12-14 20:38:04 +08:00
parent 5a5ca2a989
commit bfe9bb6a0c
4 changed files with 45 additions and 26 deletions

View File

@@ -115,6 +115,8 @@ impl CircuitBreaker {
"Circuit breaker transitioning from Open to HalfOpen (timeout reached)"
);
self.transition_to_half_open().await;
// 增加计数,确保 record_success/record_failure 减计数时不会下溢
self.half_open_requests.fetch_add(1, Ordering::SeqCst);
return true;
}
}

View File

@@ -120,11 +120,7 @@ impl GeminiAdapter {
/// 从 Provider 配置中提取原始 API Key
fn extract_key_raw(&self, provider: &Provider) -> Option<String> {
if let Some(env) = provider.settings_config.get("env") {
// 优先使用 GOOGLE_GEMINI_API_KEY
if let Some(key) = env.get("GOOGLE_GEMINI_API_KEY").and_then(|v| v.as_str()) {
return Some(key.to_string());
}
// 备选 GEMINI_API_KEY
// 使用 GEMINI_API_KEY
if let Some(key) = env.get("GEMINI_API_KEY").and_then(|v| v.as_str()) {
return Some(key.to_string());
}
@@ -276,7 +272,7 @@ mod tests {
let adapter = GeminiAdapter::new();
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "AIza-test-key-12345678"
"GEMINI_API_KEY": "AIza-test-key-12345678"
}
}));
@@ -291,7 +287,7 @@ mod tests {
let adapter = GeminiAdapter::new();
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "ya29.test-access-token-12345"
"GEMINI_API_KEY": "ya29.test-access-token-12345"
}
}));
@@ -308,7 +304,7 @@ mod tests {
let adapter = GeminiAdapter::new();
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test-token\",\"refresh_token\":\"1//refresh\"}"
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test-token\",\"refresh_token\":\"1//refresh\"}"
}
}));
@@ -324,7 +320,7 @@ mod tests {
// API Key
let api_key_provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "AIza-test-key"
"GEMINI_API_KEY": "AIza-test-key"
}
}));
assert_eq!(
@@ -335,7 +331,7 @@ mod tests {
// OAuth access_token
let oauth_provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "ya29.test-token"
"GEMINI_API_KEY": "ya29.test-token"
}
}));
assert_eq!(
@@ -346,7 +342,7 @@ mod tests {
// OAuth JSON
let oauth_json_provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test\"}"
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test\"}"
}
}));
assert_eq!(

View File

@@ -369,7 +369,7 @@ mod tests {
fn test_from_app_type_gemini_api_key() {
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "AIza-test-key"
"GEMINI_API_KEY": "AIza-test-key"
}
}));
@@ -381,7 +381,7 @@ mod tests {
fn test_from_app_type_gemini_cli_oauth() {
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "ya29.test-access-token"
"GEMINI_API_KEY": "ya29.test-access-token"
}
}));
@@ -393,7 +393,7 @@ mod tests {
fn test_from_app_type_gemini_cli_json() {
let provider = create_provider(json!({
"env": {
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test\",\"refresh_token\":\"1//test\"}"
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test\",\"refresh_token\":\"1//test\"}"
}
}));

View File

@@ -210,13 +210,13 @@ impl ProxyService {
}
}
// Gemini: 同步 GOOGLE_API_KEY
// Gemini: 同步 GEMINI_API_KEY
if let Ok(live_config) = self.read_gemini_live() {
if let Some(provider_id) = self.db.get_current_provider("gemini").ok().flatten() {
if let Ok(Some(mut provider)) = self.db.get_provider_by_id(&provider_id, "gemini") {
// 从 live 配置提取 token
if let Some(env) = live_config.get("env") {
if let Some(token) = env.get("GOOGLE_API_KEY").and_then(|v| v.as_str()) {
if let Some(token) = env.get("GEMINI_API_KEY").and_then(|v| v.as_str()) {
if !token.is_empty() {
// 更新 provider 的 settings_config
if let Some(env_obj) = provider
@@ -224,10 +224,10 @@ impl ProxyService {
.get_mut("env")
.and_then(|v| v.as_object_mut())
{
env_obj.insert("GOOGLE_API_KEY".to_string(), json!(token));
env_obj.insert("GEMINI_API_KEY".to_string(), json!(token));
} else {
provider.settings_config["env"] = json!({
"GOOGLE_API_KEY": token
"GEMINI_API_KEY": token
});
}
// 保存到数据库
@@ -368,27 +368,35 @@ impl ProxyService {
log::info!("Claude Live 配置已接管,代理地址: {}", proxy_url);
}
// Codex: 修改 OPENAI_BASE_URL使用占位符替代真实 Token(代理会注入真实 Token
// Codex: 修改 config.toml 的 base_urlauth.json 的 OPENAI_API_KEY(代理会注入真实 Token
if let Ok(mut live_config) = self.read_codex_live() {
// 1. 修改 auth.json 中的 OPENAI_API_KEY使用占位符
if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) {
auth.insert("OPENAI_BASE_URL".to_string(), json!(&proxy_url));
// 使用占位符,避免显示缺少 key 的警告
auth.insert("OPENAI_API_KEY".to_string(), json!("PROXY_MANAGED"));
}
// 2. 修改 config.toml 中的 base_url
let config_str = live_config
.get("config")
.and_then(|v| v.as_str())
.unwrap_or("");
let updated_config = Self::update_toml_base_url(config_str, &proxy_url);
live_config["config"] = json!(updated_config);
self.write_codex_live(&live_config)?;
log::info!("Codex Live 配置已接管,代理地址: {}", proxy_url);
}
// Gemini: 修改 GEMINI_API_BASE,使用占位符替代真实 Token代理会注入真实 Token
// Gemini: 修改 GOOGLE_GEMINI_BASE_URL,使用占位符替代真实 Token代理会注入真实 Token
if let Ok(mut live_config) = self.read_gemini_live() {
if let Some(env) = live_config.get_mut("env").and_then(|v| v.as_object_mut()) {
env.insert("GEMINI_API_BASE".to_string(), json!(&proxy_url));
env.insert("GOOGLE_GEMINI_BASE_URL".to_string(), json!(&proxy_url));
// 使用占位符,避免显示缺少 key 的警告
env.insert("GOOGLE_API_KEY".to_string(), json!("PROXY_MANAGED"));
env.insert("GEMINI_API_KEY".to_string(), json!("PROXY_MANAGED"));
} else {
live_config["env"] = json!({
"GEMINI_API_BASE": &proxy_url,
"GOOGLE_API_KEY": "PROXY_MANAGED"
"GOOGLE_GEMINI_BASE_URL": &proxy_url,
"GEMINI_API_KEY": "PROXY_MANAGED"
});
}
self.write_gemini_live(&live_config)?;
@@ -526,6 +534,19 @@ impl ProxyService {
// ==================== Live 配置读写辅助方法 ====================
/// 更新 TOML 字符串中的 base_url
fn update_toml_base_url(toml_str: &str, new_url: &str) -> String {
use toml_edit::DocumentMut;
let mut doc = toml_str
.parse::<DocumentMut>()
.unwrap_or_else(|_| DocumentMut::new());
doc["base_url"] = toml_edit::value(new_url);
doc.to_string()
}
fn read_claude_live(&self) -> Result<Value, String> {
let path = get_claude_settings_path();
if !path.exists() {