refactor(error): 统一异常处理和响应格式

这次提交重构了整个应用的异常处理机制,保证了处理方式的一致性,还能提供更详细的错误信息。

主要改动包括:
- 修改了 `ApiClient`,现在抛出的异常会同时包含状态码和消息。这样上游服务就能传递准确的 HTTP 错误响应啦。
- 更新了所有服务层(`gemini`、`openai`、`vertex`、`embedding`),现在会捕获这些结构化的异常,不再从字符串里解析错误消息了。
- 增强了路由级别的错误处理,特别是针对流式端点,能正确捕获初始化错误,并返回结构化的 JSON 错误响应,而不是格式错误的 SSE 事件。
- 在所有 API 路由中添加了 `allowed_token` 的日志记录,方便追踪和调试授权问题。
- 还有一些常规的代码清理,比如调整了 import 顺序和格式化代码,提高了可读性和可维护性。
This commit is contained in:
snaily
2025-09-18 03:11:45 +08:00
parent e104a50cf4
commit 67dd1af583
12 changed files with 557 additions and 416 deletions
@@ -1,6 +1,4 @@
import datetime
import json
import re
import time
from typing import Any, AsyncGenerator, Dict, Union
@@ -80,13 +78,9 @@ class OpenAICompatiableService:
return response
except Exception as e:
is_success = False
error_log_msg = str(e)
status_code = e.args[0]
error_log_msg = e.args[1]
logger.error(f"Normal API call failed with error: {error_log_msg}")
match = re.search(r"status code (\d+)", error_log_msg)
if match:
status_code = int(match.group(1))
else:
status_code = 500
await add_error_log(
gemini_key=api_key,
@@ -138,15 +132,11 @@ class OpenAICompatiableService:
except Exception as e:
retries += 1
is_success = False
error_log_msg = str(e)
status_code = e.args[0]
error_log_msg = e.args[1]
logger.warning(
f"Streaming API call failed with error: {error_log_msg}. Attempt {retries} of {max_retries}"
)
match = re.search(r"status code (\d+)", error_log_msg)
if match:
status_code = int(match.group(1))
else:
status_code = 500
await add_error_log(
gemini_key=current_attempt_key,
@@ -170,14 +160,14 @@ class OpenAICompatiableService:
logger.error(
f"No valid API key available after {retries} retries."
)
break
raise
else:
logger.error("KeyManager not available for retry logic.")
break
if retries >= max_retries:
logger.error(f"Max retries ({max_retries}) reached for streaming.")
break
raise
finally:
end_time = time.perf_counter()
latency_ms = int((end_time - start_time) * 1000)
@@ -189,6 +179,3 @@ class OpenAICompatiableService:
latency_ms=latency_ms,
request_time=request_datetime,
)
if not is_success and retries >= max_retries:
yield f"data: {json.dumps({'error': 'Streaming failed after retries'})}\n\n"
yield "data: [DONE]\n\n"