feat: router firewall — validate model against OCG whitelist + require messages; block client errors before upstream call (prevents rate-limit pollution)

This commit is contained in:
hmo
2026-08-15 17:37:39 +08:00
parent 42bcc4d4ec
commit aa0d005925
+27
View File
@@ -459,6 +459,17 @@ class RouterHandler(BaseHTTPRequestHandler):
self._json_response({"object": "list", "data": models}) self._json_response({"object": "list", "data": models})
# ── Chat Completions Proxy ── # ── Chat Completions Proxy ──
# 防火墙:OCG 已知模型白名单(2026-08-15 来自官方文档 go.mdx
# 请求 model 不在白名单 → 直接 400,不调用上游,防止错误模型名打到上游触发限流
KNOWN_OCG_MODELS = {
"deepseek-v4-flash", "deepseek-v4-pro", "kimi-k2.7-code", "kimi-k2.6",
"kimi-k2.5", "kimi-k3", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3",
"gpt-5.6-luna", "grok-4.5", "hy3", "hy3-preview", "mimo-v2-omni",
"mimo-v2-pro", "mimo-v2.5", "mimo-v2.5-pro", "minimax-m2.5",
"minimax-m2.7", "minimax-m3", "qwen3.5-plus", "qwen3.6-plus",
"qwen3.7-max", "qwen3.7-plus", "qwen3.8-max",
}
def _proxy_chat(self): def _proxy_chat(self):
start = time.time() start = time.time()
content_length = int(self.headers.get("Content-Length", 0)) content_length = int(self.headers.get("Content-Length", 0))
@@ -466,6 +477,7 @@ class RouterHandler(BaseHTTPRequestHandler):
is_stream = False is_stream = False
model_name = "unknown" model_name = "unknown"
req_data = None
if body: if body:
try: try:
req_data = json.loads(body) req_data = json.loads(body)
@@ -475,6 +487,21 @@ class RouterHandler(BaseHTTPRequestHandler):
except (json.JSONDecodeError, UnicodeDecodeError) as e: except (json.JSONDecodeError, UnicodeDecodeError) as e:
log.warning("failed to parse request body (len=%d): %s", len(body), e) log.warning("failed to parse request body (len=%d): %s", len(body), e)
# ── 防火墙:前置校验,阻止客户端错误传导到上游 ──
if not body or not req_data:
self._error_response(400, "Empty or invalid request body")
return
if not model_name or model_name == "unknown":
self._error_response(400, "Missing required field: model")
return
if "messages" not in req_data or not isinstance(req_data.get("messages"), list) or not req_data["messages"]:
self._error_response(400, "Missing required field: messages (non-empty list)")
return
if model_name not in self.KNOWN_OCG_MODELS:
log.warning("firewall: blocked unknown model '%s' (client error, no upstream call)", model_name)
self._error_response(400, f"Model '{model_name}' is not supported by this router")
return
# 2026-08-13 并发模式:header X-OCG-Mode: concurrent → round-robin 锁 key 轮询 # 2026-08-13 并发模式:header X-OCG-Mode: concurrent → round-robin 锁 key 轮询
concurrent_mode = self.headers.get("X-OCG-Mode", "").lower() == "concurrent" concurrent_mode = self.headers.get("X-OCG-Mode", "").lower() == "concurrent"