mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-07-05 23:01:34 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19941f7f50 | ||
|
|
d6981c204a | ||
|
|
d386cc7180 | ||
|
|
bed3647424 | ||
|
|
95b5acad66 |
@@ -515,7 +515,7 @@ async def verify_key(
|
|||||||
f"Verification exception for key: {redact_key_for_logging(api_key)}, incrementing failure count"
|
f"Verification exception for key: {redact_key_for_logging(api_key)}, incrementing failure count"
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSONResponse({"status": "invalid", "error": str(e)})
|
return JSONResponse({"status": "invalid", "error": e.args[1]})
|
||||||
|
|
||||||
|
|
||||||
@router.post("/verify-selected-keys")
|
@router.post("/verify-selected-keys")
|
||||||
@@ -559,7 +559,7 @@ async def verify_selected_keys(
|
|||||||
await key_manager.reset_key_failure_count(api_key)
|
await key_manager.reset_key_failure_count(api_key)
|
||||||
return api_key, "valid", None
|
return api_key, "valid", None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = str(e)
|
error_message = e.args[1]
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Key verification failed for {redact_key_for_logging(api_key)}: {error_message}"
|
f"Key verification failed for {redact_key_for_logging(api_key)}: {error_message}"
|
||||||
)
|
)
|
||||||
@@ -574,7 +574,7 @@ async def verify_selected_keys(
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
f"Bulk verification exception for key: {redact_key_for_logging(api_key)}, initializing failure count to 1"
|
f"Bulk verification exception for key: {redact_key_for_logging(api_key)}, initializing failure count to 1"
|
||||||
)
|
)
|
||||||
failed_keys[api_key] = error_message
|
failed_keys[api_key] = {"error_message": e.args[1], "error_code": e.args[0]}
|
||||||
return api_key, "invalid", error_message
|
return api_key, "invalid", error_message
|
||||||
|
|
||||||
tasks = [_verify_single_key(key) for key in keys_to_verify]
|
tasks = [_verify_single_key(key) for key in keys_to_verify]
|
||||||
@@ -585,11 +585,6 @@ async def verify_selected_keys(
|
|||||||
logger.error(
|
logger.error(
|
||||||
f"An unexpected error occurred during bulk verification task: {result}"
|
f"An unexpected error occurred during bulk verification task: {result}"
|
||||||
)
|
)
|
||||||
elif result:
|
|
||||||
if not isinstance(result, Exception) and result:
|
|
||||||
key, status, error = result
|
|
||||||
elif isinstance(result, Exception):
|
|
||||||
logger.error(f"Task execution error during bulk verification: {result}")
|
|
||||||
|
|
||||||
valid_count = len(successful_keys)
|
valid_count = len(successful_keys)
|
||||||
invalid_count = len(failed_keys)
|
invalid_count = len(failed_keys)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ async def chat_completion(
|
|||||||
async with handle_route_errors(logger, operation_name):
|
async with handle_route_errors(logger, operation_name):
|
||||||
logger.info(f"Handling chat completion request for model: {request.model}")
|
logger.info(f"Handling chat completion request for model: {request.model}")
|
||||||
logger.debug(f"Request: \n{request.model_dump_json(indent=2)}")
|
logger.debug(f"Request: \n{request.model_dump_json(indent=2)}")
|
||||||
|
logger.info(f"Using allowed token: {allowed_token}")
|
||||||
logger.info(f"Using API key: {redact_key_for_logging(current_api_key)}")
|
logger.info(f"Using API key: {redact_key_for_logging(current_api_key)}")
|
||||||
|
|
||||||
if not await model_service.check_model_support(request.model):
|
if not await model_service.check_model_support(request.model):
|
||||||
@@ -140,6 +141,7 @@ async def generate_image(
|
|||||||
operation_name = "generate_image"
|
operation_name = "generate_image"
|
||||||
async with handle_route_errors(logger, operation_name):
|
async with handle_route_errors(logger, operation_name):
|
||||||
logger.info(f"Handling image generation request for prompt: {request.prompt}")
|
logger.info(f"Handling image generation request for prompt: {request.prompt}")
|
||||||
|
logger.info(f"Using allowed token: {allowed_token}")
|
||||||
response = image_create_service.generate_images(request)
|
response = image_create_service.generate_images(request)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|||||||
@@ -659,7 +659,11 @@ class OpenAIChatService:
|
|||||||
error_type="openai-image-stream",
|
error_type="openai-image-stream",
|
||||||
error_log=error_log_msg,
|
error_log=error_log_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg={"image_data_truncated": image_data[:1000]},
|
request_msg=(
|
||||||
|
{"image_data_truncated": image_data[:1000]}
|
||||||
|
if settings.ERROR_LOG_RECORD_REQUEST_BODY
|
||||||
|
else None
|
||||||
|
),
|
||||||
request_datetime=request_datetime,
|
request_datetime=request_datetime,
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
@@ -709,7 +713,11 @@ class OpenAIChatService:
|
|||||||
error_type="openai-image-non-stream",
|
error_type="openai-image-non-stream",
|
||||||
error_log=error_log_msg,
|
error_log=error_log_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg={"image_data_truncated": image_data[:1000]},
|
request_msg=(
|
||||||
|
{"image_data_truncated": image_data[:1000]}
|
||||||
|
if settings.ERROR_LOG_RECORD_REQUEST_BODY
|
||||||
|
else None
|
||||||
|
),
|
||||||
request_datetime=request_datetime,
|
request_datetime=request_datetime,
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -99,34 +99,21 @@ class GeminiApiClient(ApiClient):
|
|||||||
|
|
||||||
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
||||||
url = f"{self.base_url}/models/{model}:generateContent?key={api_key}"
|
url = f"{self.base_url}/models/{model}:generateContent?key={api_key}"
|
||||||
|
response = await client.post(url, json=payload, headers=headers)
|
||||||
|
|
||||||
try:
|
if response.status_code != 200:
|
||||||
response = await client.post(url, json=payload, headers=headers)
|
error_content = response.text
|
||||||
|
logger.error(
|
||||||
|
f"API call failed - Status: {response.status_code}, Content: {error_content}"
|
||||||
|
)
|
||||||
|
raise Exception(response.status_code, error_content)
|
||||||
|
response_data = response.json()
|
||||||
|
|
||||||
if response.status_code != 200:
|
# 检查响应结构的基本信息
|
||||||
error_content = response.text
|
if not response_data.get("candidates"):
|
||||||
logger.error(
|
logger.warning("No candidates found in API response")
|
||||||
f"API call failed - Status: {response.status_code}, Content: {error_content}"
|
|
||||||
)
|
|
||||||
raise Exception(response.status_code, error_content)
|
|
||||||
|
|
||||||
response_data = response.json()
|
return response_data
|
||||||
|
|
||||||
# 检查响应结构的基本信息
|
|
||||||
if not response_data.get("candidates"):
|
|
||||||
logger.warning("No candidates found in API response")
|
|
||||||
|
|
||||||
return response_data
|
|
||||||
|
|
||||||
except httpx.TimeoutException as e:
|
|
||||||
logger.error(f"Request timeout: {e}")
|
|
||||||
raise Exception(500, f"Request timeout: {e}")
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
logger.error(f"Request error: {e}")
|
|
||||||
raise Exception(500, f"Request error: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected error: {e}")
|
|
||||||
raise Exception(500, f"Unexpected error: {e}")
|
|
||||||
|
|
||||||
async def stream_generate_content(
|
async def stream_generate_content(
|
||||||
self, payload: Dict[str, Any], model: str, api_key: str
|
self, payload: Dict[str, Any], model: str, api_key: str
|
||||||
@@ -196,28 +183,14 @@ class GeminiApiClient(ApiClient):
|
|||||||
headers = self._prepare_headers()
|
headers = self._prepare_headers()
|
||||||
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
||||||
url = f"{self.base_url}/models/{model}:embedContent?key={api_key}"
|
url = f"{self.base_url}/models/{model}:embedContent?key={api_key}"
|
||||||
|
response = await client.post(url, json=payload, headers=headers)
|
||||||
try:
|
if response.status_code != 200:
|
||||||
response = await client.post(url, json=payload, headers=headers)
|
error_content = response.text
|
||||||
|
logger.error(
|
||||||
if response.status_code != 200:
|
f"Embedding API call failed - Status: {response.status_code}, Content: {error_content}"
|
||||||
error_content = response.text
|
)
|
||||||
logger.error(
|
raise Exception(response.status_code, error_content)
|
||||||
f"Embedding API call failed - Status: {response.status_code}, Content: {error_content}"
|
return response.json()
|
||||||
)
|
|
||||||
raise Exception(response.status_code, error_content)
|
|
||||||
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
except httpx.TimeoutException as e:
|
|
||||||
logger.error(f"Embedding request timeout: {e}")
|
|
||||||
raise Exception(500, f"Request timeout: {e}")
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
logger.error(f"Embedding request error: {e}")
|
|
||||||
raise Exception(500, f"Request error: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected embedding error: {e}")
|
|
||||||
raise Exception(500, f"Unexpected embedding error: {e}")
|
|
||||||
|
|
||||||
async def batch_embed_contents(
|
async def batch_embed_contents(
|
||||||
self, payload: Dict[str, Any], model: str, api_key: str
|
self, payload: Dict[str, Any], model: str, api_key: str
|
||||||
@@ -237,28 +210,14 @@ class GeminiApiClient(ApiClient):
|
|||||||
headers = self._prepare_headers()
|
headers = self._prepare_headers()
|
||||||
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
async with httpx.AsyncClient(timeout=timeout, proxy=proxy_to_use) as client:
|
||||||
url = f"{self.base_url}/models/{model}:batchEmbedContents?key={api_key}"
|
url = f"{self.base_url}/models/{model}:batchEmbedContents?key={api_key}"
|
||||||
|
response = await client.post(url, json=payload, headers=headers)
|
||||||
try:
|
if response.status_code != 200:
|
||||||
response = await client.post(url, json=payload, headers=headers)
|
error_content = response.text
|
||||||
|
logger.error(
|
||||||
if response.status_code != 200:
|
f"Batch embedding API call failed - Status: {response.status_code}, Content: {error_content}"
|
||||||
error_content = response.text
|
)
|
||||||
logger.error(
|
raise Exception(response.status_code, error_content)
|
||||||
f"Batch embedding API call failed - Status: {response.status_code}, Content: {error_content}"
|
return response.json()
|
||||||
)
|
|
||||||
raise Exception(response.status_code, error_content)
|
|
||||||
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
except httpx.TimeoutException as e:
|
|
||||||
logger.error(f"Batch embedding request timeout: {e}")
|
|
||||||
raise Exception(500, f"Request timeout: {e}")
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
logger.error(f"Batch embedding request error: {e}")
|
|
||||||
raise Exception(500, f"Request error: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected batch embedding error: {e}")
|
|
||||||
raise Exception(500, f"Unexpected batch embedding error: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
class OpenaiApiClient(ApiClient):
|
class OpenaiApiClient(ApiClient):
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ class EmbeddingService:
|
|||||||
error_type="openai-embedding",
|
error_type="openai-embedding",
|
||||||
error_log=error_log_msg,
|
error_log=error_log_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg=request_msg_log,
|
request_msg=(
|
||||||
|
request_msg_log
|
||||||
|
if settings.ERROR_LOG_RECORD_REQUEST_BODY
|
||||||
|
else None
|
||||||
|
),
|
||||||
request_datetime=request_datetime,
|
request_datetime=request_datetime,
|
||||||
)
|
)
|
||||||
await add_request_log(
|
await add_request_log(
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class OpenAICompatiableService:
|
|||||||
error_type="openai-compatiable-non-stream",
|
error_type="openai-compatiable-non-stream",
|
||||||
error_log=error_log_msg,
|
error_log=error_log_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg=request,
|
request_msg=request if settings.ERROR_LOG_RECORD_REQUEST_BODY else None,
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -3,14 +3,16 @@
|
|||||||
继承自原始聊天服务,添加原生Gemini TTS支持(单人和多人),保持向后兼容
|
继承自原始聊天服务,添加原生Gemini TTS支持(单人和多人),保持向后兼容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import time
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
from app.service.chat.gemini_chat_service import GeminiChatService
|
|
||||||
from app.service.tts.native.tts_response_handler import TTSResponseHandler
|
from app.config.config import settings
|
||||||
|
from app.database.services import add_error_log, add_request_log
|
||||||
from app.domain.gemini_models import GeminiRequest
|
from app.domain.gemini_models import GeminiRequest
|
||||||
from app.log.logger import get_gemini_logger
|
from app.log.logger import get_gemini_logger
|
||||||
from app.database.services import add_request_log, add_error_log
|
from app.service.chat.gemini_chat_service import GeminiChatService
|
||||||
|
from app.service.tts.native.tts_response_handler import TTSResponseHandler
|
||||||
|
|
||||||
logger = get_gemini_logger()
|
logger = get_gemini_logger()
|
||||||
|
|
||||||
@@ -28,7 +30,9 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
super().__init__(base_url, key_manager)
|
super().__init__(base_url, key_manager)
|
||||||
# 使用TTS响应处理器替换原始处理器
|
# 使用TTS响应处理器替换原始处理器
|
||||||
self.response_handler = TTSResponseHandler()
|
self.response_handler = TTSResponseHandler()
|
||||||
logger.info("TTS Gemini Chat Service initialized with multi-speaker TTS support")
|
logger.info(
|
||||||
|
"TTS Gemini Chat Service initialized with multi-speaker TTS support"
|
||||||
|
)
|
||||||
|
|
||||||
async def generate_content(
|
async def generate_content(
|
||||||
self, model: str, request: GeminiRequest, api_key: str
|
self, model: str, request: GeminiRequest, api_key: str
|
||||||
@@ -55,7 +59,9 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
logger.error(f"TTS API call failed with error: {e}")
|
logger.error(f"TTS API call failed with error: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def _handle_tts_request(self, model: str, request: GeminiRequest, api_key: str) -> Dict[str, Any]:
|
async def _handle_tts_request(
|
||||||
|
self, model: str, request: GeminiRequest, api_key: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
处理TTS特定的请求,包含完整的日志记录功能
|
处理TTS特定的请求,包含完整的日志记录功能
|
||||||
"""
|
"""
|
||||||
@@ -89,14 +95,24 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
if request.generationConfig:
|
if request.generationConfig:
|
||||||
# 添加TTS特定字段
|
# 添加TTS特定字段
|
||||||
if request.generationConfig.responseModalities:
|
if request.generationConfig.responseModalities:
|
||||||
payload["generationConfig"]["responseModalities"] = request.generationConfig.responseModalities
|
payload["generationConfig"][
|
||||||
logger.info(f"Added responseModalities: {request.generationConfig.responseModalities}")
|
"responseModalities"
|
||||||
|
] = request.generationConfig.responseModalities
|
||||||
|
logger.info(
|
||||||
|
f"Added responseModalities: {request.generationConfig.responseModalities}"
|
||||||
|
)
|
||||||
|
|
||||||
if request.generationConfig.speechConfig:
|
if request.generationConfig.speechConfig:
|
||||||
payload["generationConfig"]["speechConfig"] = request.generationConfig.speechConfig
|
payload["generationConfig"][
|
||||||
logger.info(f"Added speechConfig: {request.generationConfig.speechConfig}")
|
"speechConfig"
|
||||||
|
] = request.generationConfig.speechConfig
|
||||||
|
logger.info(
|
||||||
|
f"Added speechConfig: {request.generationConfig.speechConfig}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning("No generationConfig found in request, TTS fields may be missing")
|
logger.warning(
|
||||||
|
"No generationConfig found in request, TTS fields may be missing"
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(f"TTS payload before API call: {payload}")
|
logger.info(f"TTS payload before API call: {payload}")
|
||||||
|
|
||||||
@@ -117,6 +133,7 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
|
|
||||||
# 尝试从错误消息中提取状态码
|
# 尝试从错误消息中提取状态码
|
||||||
import re
|
import re
|
||||||
|
|
||||||
match = re.search(r"status code (\d+)", error_msg)
|
match = re.search(r"status code (\d+)", error_msg)
|
||||||
if match:
|
if match:
|
||||||
status_code = int(match.group(1))
|
status_code = int(match.group(1))
|
||||||
@@ -130,7 +147,11 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
error_type="tts-api-error",
|
error_type="tts-api-error",
|
||||||
error_log=error_msg,
|
error_log=error_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg=request.model_dump(exclude_none=False)
|
request_msg=(
|
||||||
|
request.model_dump(exclude_none=False)
|
||||||
|
if settings.ERROR_LOG_RECORD_REQUEST_BODY
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.error(f"TTS API call failed: {error_msg}")
|
logger.error(f"TTS API call failed: {error_msg}")
|
||||||
@@ -147,5 +168,5 @@ class TTSGeminiChatService(GeminiChatService):
|
|||||||
is_success=is_success,
|
is_success=is_success,
|
||||||
status_code=status_code,
|
status_code=status_code,
|
||||||
latency_ms=latency_ms,
|
latency_ms=latency_ms,
|
||||||
request_time=request_datetime
|
request_time=request_datetime,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class TTSService:
|
|||||||
error_log_msg = ""
|
error_log_msg = ""
|
||||||
try:
|
try:
|
||||||
client = genai.Client(api_key=api_key)
|
client = genai.Client(api_key=api_key)
|
||||||
response =await client.aio.models.generate_content(
|
response = await client.aio.models.generate_content(
|
||||||
model=settings.TTS_MODEL,
|
model=settings.TTS_MODEL,
|
||||||
contents=f"Speak in a {settings.TTS_SPEED} speed voice: {request.input}",
|
contents=f"Speak in a {settings.TTS_SPEED} speed voice: {request.input}",
|
||||||
config={
|
config={
|
||||||
@@ -48,7 +48,11 @@ class TTSService:
|
|||||||
"speech_config": {
|
"speech_config": {
|
||||||
"voice_config": {
|
"voice_config": {
|
||||||
"prebuilt_voice_config": {
|
"prebuilt_voice_config": {
|
||||||
"voice_name": request.voice if request.voice in TTS_VOICE_NAMES else settings.TTS_VOICE_NAME
|
"voice_name": (
|
||||||
|
request.voice
|
||||||
|
if request.voice in TTS_VOICE_NAMES
|
||||||
|
else settings.TTS_VOICE_NAME
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -59,7 +63,9 @@ class TTSService:
|
|||||||
and response.candidates[0].content.parts
|
and response.candidates[0].content.parts
|
||||||
and response.candidates[0].content.parts[0].inline_data
|
and response.candidates[0].content.parts[0].inline_data
|
||||||
):
|
):
|
||||||
raw_audio_data = response.candidates[0].content.parts[0].inline_data.data
|
raw_audio_data = (
|
||||||
|
response.candidates[0].content.parts[0].inline_data.data
|
||||||
|
)
|
||||||
is_success = True
|
is_success = True
|
||||||
status_code = 200
|
status_code = 200
|
||||||
return _create_wav_file(raw_audio_data)
|
return _create_wav_file(raw_audio_data)
|
||||||
@@ -83,13 +89,17 @@ class TTSService:
|
|||||||
error_type="google-tts",
|
error_type="google-tts",
|
||||||
error_log=error_log_msg,
|
error_log=error_log_msg,
|
||||||
error_code=status_code,
|
error_code=status_code,
|
||||||
request_msg=request.input
|
request_msg=(
|
||||||
)
|
request.input
|
||||||
|
if settings.ERROR_LOG_RECORD_REQUEST_BODY
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
)
|
||||||
await add_request_log(
|
await add_request_log(
|
||||||
model_name=settings.TTS_MODEL,
|
model_name=settings.TTS_MODEL,
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
is_success=is_success,
|
is_success=is_success,
|
||||||
status_code=status_code,
|
status_code=status_code,
|
||||||
latency_ms=latency_ms,
|
latency_ms=latency_ms,
|
||||||
request_time=request_datetime
|
request_time=request_datetime,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -541,30 +541,13 @@ function showVerificationResultModal(data) {
|
|||||||
const errorGroups = {};
|
const errorGroups = {};
|
||||||
Object.entries(failedKeys).forEach(([key, error]) => {
|
Object.entries(failedKeys).forEach(([key, error]) => {
|
||||||
// 提取错误码或使用完整错误信息作为分组键
|
// 提取错误码或使用完整错误信息作为分组键
|
||||||
let errorCode = error;
|
let errorCode = error["error_code"];
|
||||||
|
let errorMessage = error["error_message"];
|
||||||
// 尝试提取常见的错误码模式
|
|
||||||
const errorCodePatterns = [
|
|
||||||
/status code (\d+)/,
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const pattern of errorCodePatterns) {
|
|
||||||
const match = error.match(pattern);
|
|
||||||
if (match) {
|
|
||||||
errorCode = match[1] || match[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有匹配到特定模式,使用500
|
|
||||||
if (errorCode === error) {
|
|
||||||
errorCode = 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errorGroups[errorCode]) {
|
if (!errorGroups[errorCode]) {
|
||||||
errorGroups[errorCode] = [];
|
errorGroups[errorCode] = [];
|
||||||
}
|
}
|
||||||
errorGroups[errorCode].push({ key, error });
|
errorGroups[errorCode].push({ key, errorMessage });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建分组展示容器
|
// 创建分组展示容器
|
||||||
@@ -609,7 +592,7 @@ function showVerificationResultModal(data) {
|
|||||||
const keysList = document.createElement("div");
|
const keysList = document.createElement("div");
|
||||||
keysList.className = "group-keys-list space-y-1";
|
keysList.className = "group-keys-list space-y-1";
|
||||||
|
|
||||||
keyErrorPairs.forEach(({ key, error }) => {
|
keyErrorPairs.forEach(({ key, errorMessage }) => {
|
||||||
const keyItem = document.createElement("div");
|
const keyItem = document.createElement("div");
|
||||||
keyItem.className = "flex flex-col items-start bg-gray-50 p-2 rounded border";
|
keyItem.className = "flex flex-col items-start bg-gray-50 p-2 rounded border";
|
||||||
|
|
||||||
@@ -624,7 +607,7 @@ function showVerificationResultModal(data) {
|
|||||||
const detailsButton = document.createElement("button");
|
const detailsButton = document.createElement("button");
|
||||||
detailsButton.className = "ml-2 px-2 py-0.5 bg-red-200 hover:bg-red-300 text-red-700 text-xs rounded transition-colors";
|
detailsButton.className = "ml-2 px-2 py-0.5 bg-red-200 hover:bg-red-300 text-red-700 text-xs rounded transition-colors";
|
||||||
detailsButton.innerHTML = '<i class="fas fa-info-circle mr-1"></i>详情';
|
detailsButton.innerHTML = '<i class="fas fa-info-circle mr-1"></i>详情';
|
||||||
detailsButton.dataset.error = error;
|
detailsButton.dataset.error = errorMessage;
|
||||||
detailsButton.onclick = (e) => {
|
detailsButton.onclick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const button = e.currentTarget;
|
const button = e.currentTarget;
|
||||||
@@ -984,7 +967,6 @@ function initializeGlobalBatchVerificationHandlers() {
|
|||||||
document.getElementById("verifyModal").classList.add("hidden");
|
document.getElementById("verifyModal").classList.add("hidden");
|
||||||
};
|
};
|
||||||
|
|
||||||
// executeVerifyAll 变为 initializeGlobalBatchVerificationHandlers 的局部函数
|
|
||||||
async function executeVerifyAll(type) {
|
async function executeVerifyAll(type) {
|
||||||
closeVerifyModal();
|
closeVerifyModal();
|
||||||
const keysToVerify = getSelectedKeys(type);
|
const keysToVerify = getSelectedKeys(type);
|
||||||
@@ -1055,8 +1037,6 @@ function initializeGlobalBatchVerificationHandlers() {
|
|||||||
invalid_count: Object.keys(allFailedKeys).length
|
invalid_count: Object.keys(allFailedKeys).length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// The confirmButton.onclick in showVerifyModal (defined earlier in initializeGlobalBatchVerificationHandlers)
|
|
||||||
// will correctly reference this local executeVerifyAll due to closure.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 进度条模态框函数 ---
|
// --- 进度条模态框函数 ---
|
||||||
@@ -2548,73 +2528,4 @@ function showVerifyModalForAllKeys(allKeys) {
|
|||||||
|
|
||||||
// 显示模态框
|
// 显示模态框
|
||||||
modalElement.classList.remove("hidden");
|
modalElement.classList.remove("hidden");
|
||||||
}
|
|
||||||
|
|
||||||
// 执行验证所有密钥
|
|
||||||
async function executeVerifyAllKeys(allKeys) {
|
|
||||||
closeVerifyModal();
|
|
||||||
|
|
||||||
// 获取批次大小
|
|
||||||
const batchSizeInput = document.getElementById("batchSize");
|
|
||||||
const batchSize = parseInt(batchSizeInput.value, 10) || 10;
|
|
||||||
|
|
||||||
// 开始批量验证
|
|
||||||
showProgressModal(`批量验证所有 ${allKeys.length} 个密钥`);
|
|
||||||
|
|
||||||
let allSuccessfulKeys = [];
|
|
||||||
let allFailedKeys = {};
|
|
||||||
let processedCount = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < allKeys.length; i += batchSize) {
|
|
||||||
const batch = allKeys.slice(i, i + batchSize);
|
|
||||||
const progressText = `正在验证批次 ${Math.floor(i / batchSize) + 1} / ${Math.ceil(allKeys.length / batchSize)} (密钥 ${i + 1}-${Math.min(i + batchSize, allKeys.length)})`;
|
|
||||||
|
|
||||||
updateProgress(i, allKeys.length, progressText);
|
|
||||||
addProgressLog(`处理批次: ${batch.length}个密钥...`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ keys: batch }),
|
|
||||||
};
|
|
||||||
const data = await fetchAPI(`/gemini/v1beta/verify-selected-keys`, options);
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
if (data.successful_keys && data.successful_keys.length > 0) {
|
|
||||||
allSuccessfulKeys = allSuccessfulKeys.concat(data.successful_keys);
|
|
||||||
addProgressLog(`✅ 批次成功: ${data.successful_keys.length} 个`);
|
|
||||||
}
|
|
||||||
if (data.failed_keys && Object.keys(data.failed_keys).length > 0) {
|
|
||||||
Object.assign(allFailedKeys, data.failed_keys);
|
|
||||||
addProgressLog(`❌ 批次失败: ${Object.keys(data.failed_keys).length} 个`, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addProgressLog(`- 批次返回空数据`, true);
|
|
||||||
}
|
|
||||||
} catch (apiError) {
|
|
||||||
addProgressLog(`❌ 批次请求失败: ${apiError.message}`, true);
|
|
||||||
// 将此批次的所有密钥标记为失败
|
|
||||||
batch.forEach(key => {
|
|
||||||
allFailedKeys[key] = apiError.message;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
processedCount += batch.length;
|
|
||||||
updateProgress(processedCount, allKeys.length, progressText);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(
|
|
||||||
allKeys.length,
|
|
||||||
allKeys.length,
|
|
||||||
`所有批次验证完成!`
|
|
||||||
);
|
|
||||||
|
|
||||||
// 关闭进度模态框并显示最终结果
|
|
||||||
closeProgressModal(false);
|
|
||||||
showVerificationResultModal({
|
|
||||||
successful_keys: allSuccessfulKeys,
|
|
||||||
failed_keys: allFailedKeys,
|
|
||||||
valid_count: allSuccessfulKeys.length,
|
|
||||||
invalid_count: Object.keys(allFailedKeys).length
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user