Skip to content

Commit 689dc53

Browse files
committed
反重力缓存
1 parent 3ab668d commit 689dc53

6 files changed

Lines changed: 105 additions & 29 deletions

File tree

config.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"COMPATIBILITY_MODE": "compatibility_mode_enabled",
4040
"RETURN_THOUGHTS_TO_FRONTEND": "return_thoughts_to_frontend",
4141
"ANTIGRAVITY_STREAM2NOSTREAM": "antigravity_stream2nostream",
42+
"ANTIGRAVITY_SWITCH_CREDENTIAL": "antigravity_switch_credential_enabled",
4243
"HOST": "host",
4344
"PORT": "port",
4445
"API_PASSWORD": "api_password",
@@ -354,6 +355,24 @@ async def get_antigravity_stream2nostream() -> bool:
354355
return bool(await get_config_value("antigravity_stream2nostream", True))
355356

356357

358+
async def get_antigravity_switch_credential_enabled() -> bool:
359+
"""
360+
Get antigravity switch credential setting.
361+
362+
控制antigravity在重试时是否切换凭证。
363+
禁用时会持续使用当前凭证,直到该凭证对当前模型进入CD或被禁用。
364+
365+
Environment variable: ANTIGRAVITY_SWITCH_CREDENTIAL
366+
Database config key: antigravity_switch_credential_enabled
367+
Default: False
368+
"""
369+
env_value = os.getenv("ANTIGRAVITY_SWITCH_CREDENTIAL")
370+
if env_value:
371+
return env_value.lower() in ("true", "1", "yes", "on")
372+
373+
return bool(await get_config_value("antigravity_switch_credential_enabled", False))
374+
375+
357376
async def get_oauth_proxy_url() -> str:
358377
"""
359378
Get OAuth proxy URL setting.

front/common.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,6 +2756,7 @@ function populateConfigForm() {
27562756
document.getElementById('compatibilityModeEnabled').checked = Boolean(c.compatibility_mode_enabled);
27572757
document.getElementById('returnThoughtsToFrontend').checked = Boolean(c.return_thoughts_to_frontend !== false);
27582758
document.getElementById('antigravityStream2nostream').checked = Boolean(c.antigravity_stream2nostream !== false);
2759+
document.getElementById('antigravitySwitchCredentialEnabled').checked = Boolean(c.antigravity_switch_credential_enabled);
27592760

27602761
setConfigField('antiTruncationMaxAttempts', c.anti_truncation_max_attempts || 3);
27612762

@@ -2809,6 +2810,7 @@ async function saveConfig() {
28092810
compatibility_mode_enabled: getChecked('compatibilityModeEnabled'),
28102811
return_thoughts_to_frontend: getChecked('returnThoughtsToFrontend'),
28112812
antigravity_stream2nostream: getChecked('antigravityStream2nostream'),
2813+
antigravity_switch_credential_enabled: getChecked('antigravitySwitchCredentialEnabled'),
28122814
anti_truncation_max_attempts: getInt('antiTruncationMaxAttempts', 3),
28132815
keepalive_url: getValue('keepaliveUrl'),
28142816
keepalive_interval: getInt('keepaliveInterval', 60)

front/control_panel.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,19 @@ <h4>兼容性配置</h4>
21942194
<br><strong>默认:</strong>已启用
21952195
</div>
21962196
</div>
2197+
2198+
<div class="form-group">
2199+
<label>
2200+
<input type="checkbox" id="antigravitySwitchCredentialEnabled" class="config-checkbox" />
2201+
Antigravity切换凭证
2202+
</label>
2203+
<small class="config-note">启用后重试时切换凭证;禁用时保持当前凭证,直到该凭证对当前模型进入CD或被禁用 <span
2204+
style="color: #28a745;">✓ 支持热更新</span></small>
2205+
<div class="config-info"
2206+
style="background-color: #e8f5e9; border: 1px solid #66bb6a; color: #1b5e20;">
2207+
<strong>✅ 说明:</strong>默认关闭,便于连续使用同一凭证;当发生模型CD或自动禁用时仍会切换。
2208+
</div>
2209+
</div>
21972210
</div>
21982211

21992212
<div class="config-group">

front/control_panel_mobile.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,21 @@ <h4 style="margin-top: 0; margin-bottom: 15px;">兼容性配置</h4>
19271927
<br><strong>默认:</strong>已启用
19281928
</div>
19291929
</div>
1930+
1931+
<div class="form-group">
1932+
<label style="display: flex; align-items: center; gap: 8px;">
1933+
<input type="checkbox" id="antigravitySwitchCredentialEnabled"
1934+
style="width: auto; margin: 0;" />
1935+
Antigravity切换凭证
1936+
</label>
1937+
<small
1938+
style="display: block; color: #666; font-size: 12px; margin-top: 5px;">启用后重试时切换凭证;禁用时保持当前凭证,直到该凭证对当前模型进入CD或被禁用
1939+
<span style="color: #28a745;">✓ 支持热更新</span></small>
1940+
<div
1941+
style="background-color: #e8f5e9; border: 1px solid #66bb6a; border-radius: 6px; padding: 12px; margin-top: 8px; font-size: 13px; color: #1b5e20;">
1942+
<strong>✅ 说明:</strong>默认关闭,便于连续使用同一凭证;当发生模型CD或自动禁用时仍会切换。
1943+
</div>
1944+
</div>
19301945
</div>
19311946

19321947
<div class="card">

src/api/antigravity.py

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from config import (
1515
get_antigravity_api_url,
1616
get_antigravity_stream2nostream,
17+
get_antigravity_switch_credential_enabled,
1718
get_auto_ban_error_codes,
1819
)
1920
from log import log
@@ -26,6 +27,7 @@
2627
# 导入共同的基础功能
2728
from src.api.utils import (
2829
handle_error_with_retry,
30+
check_should_auto_ban,
2931
get_retry_config,
3032
record_api_call_success,
3133
record_api_call_error,
@@ -262,6 +264,7 @@ async def stream_request(
262264
Response对象(错误时)或 bytes流/str流(成功时)
263265
"""
264266
model_name = body.get("model", "")
267+
switch_credential_enabled = await get_antigravity_switch_credential_enabled()
265268

266269
# 1. 获取有效凭证
267270
cred_result = await credential_manager.get_valid_credential(
@@ -361,6 +364,7 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
361364
for attempt in range(max_retries + 1):
362365
success_recorded = False # 标记是否已记录成功
363366
need_retry = False # 标记是否需要重试
367+
should_force_switch = False
364368

365369
try:
366370
async for chunk in stream_post_async(
@@ -393,8 +397,13 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
393397
except Exception:
394398
pass
395399

400+
if cooldown_until is not None:
401+
should_force_switch = True
402+
elif await check_should_auto_ban(status_code):
403+
should_force_switch = True
404+
396405
# 预热下一个凭证
397-
if next_cred_task is None and attempt < max_retries:
406+
if (switch_credential_enabled or should_force_switch) and next_cred_task is None and attempt < max_retries:
398407
next_cred_task = asyncio.create_task(
399408
credential_manager.get_valid_credential(
400409
mode="antigravity", model_name=model_name
@@ -479,21 +488,24 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
479488
if need_retry:
480489
log.info(f"[ANTIGRAVITY STREAM] 重试请求 (attempt {attempt + 2}/{max_retries + 1})...")
481490

482-
switched, next_cred_task = await _switch_credential_for_retry(
483-
next_cred_task=next_cred_task,
484-
retry_interval=retry_interval,
485-
refresh_credential_fast=refresh_credential_fast,
486-
apply_cred_result=apply_cred_result,
487-
log_prefix="[ANTIGRAVITY STREAM]",
488-
)
489-
if not switched:
490-
log.error("[ANTIGRAVITY STREAM] 重试时无可用凭证或令牌")
491-
yield Response(
492-
content=json.dumps({"error": "当前无可用凭证"}),
493-
status_code=500,
494-
media_type="application/json"
491+
if switch_credential_enabled or should_force_switch:
492+
switched, next_cred_task = await _switch_credential_for_retry(
493+
next_cred_task=next_cred_task,
494+
retry_interval=retry_interval,
495+
refresh_credential_fast=refresh_credential_fast,
496+
apply_cred_result=apply_cred_result,
497+
log_prefix="[ANTIGRAVITY STREAM]",
495498
)
496-
return
499+
if not switched:
500+
log.error("[ANTIGRAVITY STREAM] 重试时无可用凭证或令牌")
501+
yield Response(
502+
content=json.dumps({"error": "当前无可用凭证"}),
503+
status_code=500,
504+
media_type="application/json"
505+
)
506+
return
507+
else:
508+
await asyncio.sleep(retry_interval)
497509
continue # 重试
498510

499511
except Exception as e:
@@ -558,6 +570,7 @@ async def non_stream_request(
558570
log.debug("[ANTIGRAVITY] 使用传统非流式模式")
559571

560572
model_name = body.get("model", "")
573+
switch_credential_enabled = await get_antigravity_switch_credential_enabled()
561574

562575
# 1. 获取有效凭证
563576
cred_result = await credential_manager.get_valid_credential(
@@ -654,6 +667,7 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
654667

655668
for attempt in range(max_retries + 1):
656669
need_retry = False # 标记是否需要重试
670+
should_force_switch = False
657671

658672
try:
659673
response = await post_async(
@@ -725,8 +739,13 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
725739
except Exception:
726740
pass
727741

742+
if cooldown_until is not None:
743+
should_force_switch = True
744+
elif await check_should_auto_ban(status_code):
745+
should_force_switch = True
746+
728747
# 并行预热下一个凭证,不阻塞当前处理
729-
if next_cred_task is None and attempt < max_retries:
748+
if (switch_credential_enabled or should_force_switch) and next_cred_task is None and attempt < max_retries:
730749
next_cred_task = asyncio.create_task(
731750
credential_manager.get_valid_credential(
732751
mode="antigravity", model_name=model_name
@@ -767,20 +786,23 @@ def apply_cred_result(cred_result: Tuple[str, Dict[str, Any]]) -> bool:
767786
if need_retry:
768787
log.info(f"[ANTIGRAVITY] 重试请求 (attempt {attempt + 2}/{max_retries + 1})...")
769788

770-
switched, next_cred_task = await _switch_credential_for_retry(
771-
next_cred_task=next_cred_task,
772-
retry_interval=retry_interval,
773-
refresh_credential_fast=refresh_credential_fast,
774-
apply_cred_result=apply_cred_result,
775-
log_prefix="[ANTIGRAVITY]",
776-
)
777-
if not switched:
778-
log.error("[ANTIGRAVITY] 重试时无可用凭证或令牌")
779-
return Response(
780-
content=json.dumps({"error": "当前无可用凭证"}),
781-
status_code=500,
782-
media_type="application/json"
789+
if switch_credential_enabled or should_force_switch:
790+
switched, next_cred_task = await _switch_credential_for_retry(
791+
next_cred_task=next_cred_task,
792+
retry_interval=retry_interval,
793+
refresh_credential_fast=refresh_credential_fast,
794+
apply_cred_result=apply_cred_result,
795+
log_prefix="[ANTIGRAVITY]",
783796
)
797+
if not switched:
798+
log.error("[ANTIGRAVITY] 重试时无可用凭证或令牌")
799+
return Response(
800+
content=json.dumps({"error": "当前无可用凭证"}),
801+
status_code=500,
802+
media_type="application/json"
803+
)
804+
else:
805+
await asyncio.sleep(retry_interval)
784806
continue # 重试
785807

786808
except Exception as e:

src/panel/config_routes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ async def get_config(token: str = Depends(verify_panel_token)):
5858

5959
# Antigravity流式转非流式配置
6060
current_config["antigravity_stream2nostream"] = await config.get_antigravity_stream2nostream()
61+
current_config["antigravity_switch_credential_enabled"] = await config.get_antigravity_switch_credential_enabled()
6162

6263
# 保活配置
6364
current_config["keepalive_url"] = await config.get_keepalive_url()
@@ -142,6 +143,10 @@ async def save_config(request: ConfigSaveRequest, token: str = Depends(verify_pa
142143
if not isinstance(new_config["antigravity_stream2nostream"], bool):
143144
raise HTTPException(status_code=400, detail="Antigravity流式转非流式开关必须是布尔值")
144145

146+
if "antigravity_switch_credential_enabled" in new_config:
147+
if not isinstance(new_config["antigravity_switch_credential_enabled"], bool):
148+
raise HTTPException(status_code=400, detail="Antigravity切换凭证开关必须是布尔值")
149+
145150
# 验证保活配置
146151
if "keepalive_url" in new_config:
147152
if not isinstance(new_config["keepalive_url"], str):

0 commit comments

Comments
 (0)