Skip to content

Commit cdafc38

Browse files
committed
fix(codearts): 参数无效不再误判权益耗尽,整渠道 12h 冷却
`classify_error_code` 的 `"1005" in text` 是裸子串判据,而 `InferHub.001001005.400 The request param is invalid` 的错误码前缀 `001001005` 里含 `1005` → 判 PLAN → 12h 账号级冷却。实测 11-03 14:41–14:55 三条 codearts 凭证相继命中,整条渠道(约 2.2 万剩余 积分)静默下线 12h,负载全压到 codebuddy。 与 04d5a74 同一手法:用错误码前缀做前置 marker(`_PARAM_INVALID_MARKERS` = ("001001005", "param is invalid") → INVALID),位置在模型能力 marker 之后、`"1005" in text` 之前。语义是请求侧问题:换号无用、跳过整个上游 并以 400 收尾;真业务码 1005(权益耗尽)仍判 PLAN。 测试:001001005 / param is invalid / HTTP 400 形态 → INVALID; 1005 / 1005 quota / insufficient balance 仍 PLAN(防回归)。 TECHNICAL §9.1.3 同步。
1 parent 257f38b commit cdafc38

3 files changed

Lines changed: 48 additions & 0 deletions

File tree

‎TECHNICAL.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ PRAGMA foreign_keys = ON; -- 库里没有任何外键:users ↔ api_keys
673673
- `ErrKind.SOFT` → `rate_limit`:SOFT 主要是上游 429/限流,记「额度耗尽」比笼统 `upstream_error`(「渠道错误」)可行动;链路层超时被上一步拦走,不会误标。
674674
- `ErrKind.CONTEXT`(CodeBuddy 11115 `prompt is too long`)→ `context_overflow`;处置同请求级错误(不罚号、仍换号),但不能再混进「请求无效」。
675675
- `ErrKind.CAPABILITY`(CodeArts 001001020 not multimodal)→ `model_capability`;处置同 INVALID(`REJECT_KINDS` 一起跳过整个上游),但「模型不支持该请求形态」不是「请求写错了」。
676+
- **CodeArts `InferHub.001001005.400 param is invalid` → INVALID**(2026-11-04 修):`classify_error_code` 的 `"1005" in text` 是**裸子串**判据,而 `001001005` 是上游错误码前缀、子串里含 `1005`,于是「请求参数不合法」被判成 `PLAN` → 12h 账号级冷却。实测 2026-11-03 14:41–14:55 三条 codearts 凭证相继命中,整条渠道(约 2.2 万剩余积分)静默下线 12h。现以 `_PARAM_INVALID_MARKERS = ("001001005", "param is invalid")` 前置判 INVALID(位置在 `_MODEL_CAPABILITY_MARKERS` 之后、`"1005" in text` 之前);真业务码 `1005`(权益耗尽)仍判 PLAN。与 `001001020` 同一手法:点分「前缀 + 子码」形态**不能**用裸子串匹配。
676677
- 三个新值进 `CONTROLLED_ERROR_TYPES`(前端 `USAGE_ERROR_LABEL` 同步);`model_capability` 计入模型可用性视图,`context_overflow` 不计(客户端上下文问题,与「上游不认模型」无关)。
677678
- **明细口径**:硬失败(DEAD/RESTRICTED/INVALID/CAPABILITY)每次尝试各落一行;限流 / 额度 / 超时 / 上下文等其余分类每次请求仅一行(收尾行),不逐次落——逐次落会把 `usage_hourly.requests` 与 Key 日请求额度一起刷高。
678679

‎src/provider/codearts/events.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@
103103
# 落到 `supports_images=True` 的 codebuddy,最终 400 收尾(4 条未救回失败)。
104104
# 与 `classify_status` 一致:同样的错误走 HTTP 400 时本来就走 INVALID。
105105
_MODEL_CAPABILITY_MARKERS = ("001001020", "not multimodal")
106+
# 请求参数本身不合法:`InferHub.001001005.400 The request param is invalid,
107+
# Please check it`(实测原文,2026-11-03 14:41 三条 codearts 凭证相继命中)。
108+
#
109+
# 必须排在下面的 `"1005" in text` 之前:`001001005` 是上游错误码前缀,子串里
110+
# 含 `1005`,会命中「权益耗尽」那条**子串**判据 → PLAN → 12h 账号级冷却。
111+
# 实测后果是三条凭证依次被判「套餐耗尽」,整条渠道(约 2.2 万剩余积分)空转
112+
# 12h,全部负载压到 codebuddy。语义上这条是请求侧问题:换号无用、换渠道/改
113+
# 请求才有用,判 INVALID(REJECT_KINDS:跳过整个上游并以 400 收尾)。
114+
#
115+
# 与 `001001020`(能力不匹配)同一手法:都用错误码前缀做前置 marker,不靠
116+
# 业务码数字匹配——`CODE.001001005.400` 这种「点分前缀 + 子码」形态里,任何
117+
# 裸子串判据都会误伤。
118+
_PARAM_INVALID_MARKERS = ("001001005", "param is invalid")
106119
# HTTP 状态码侧的限流/并发标记。与流内业务码不同,这里判的是**响应体文本**:
107120
# 上游把并发超限放在 HTTP 400(不是 429),命中即应算可重试的限流。
108121
# 并发与否的区分见 _CONCURRENCY_MARKERS(先判并发,再判模型级限流)。
@@ -341,6 +354,10 @@ def classify_error_code(code: str | None) -> ErrKind:
341354
# 统计口径独立成 `model_capability`:处置同 INVALID(跳过上游 + 400),
342355
# 但归因面板要说「这个模型不支持这次请求形态」而不是「请求写错了」。
343356
return ErrKind.CAPABILITY
357+
if any(marker in text for marker in _PARAM_INVALID_MARKERS):
358+
# 请求参数不合法(001001005):见 _PARAM_INVALID_MARKERS 注释。必须排在
359+
# `"1005" in text` 之前,否则子串 `1005` 会把「参数无效」判成「权益耗尽」。
360+
return ErrKind.INVALID
344361
if "1005" in text or "quota" in text or "insufficient" in text:
345362
return ErrKind.PLAN
346363
if "apig.0301" in text or "decrypt token" in text:

‎tests/provider/codearts/test_events.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,36 @@ def test_events_classify_request_marker_is_not_credential_fault():
214214
assert classify("The request model is not multimodal") is ErrKind.CAPABILITY
215215

216216

217+
def test_events_param_invalid_is_not_plan():
218+
"""`InferHub.001001005.400 param is invalid` 是请求侧错误 → INVALID,不是 PLAN。
219+
220+
`001001005` 是点分错误码前缀,子串里含 `1005`;此前靠
221+
`"1005" in text` 判据直接落到「权益耗尽」→ **12h 账号级冷却**。实测
222+
2026-11-03 14:41–14:55 三条 codearts 凭证相继命中,整条渠道静默下线 12h
223+
(见 docs/oss-feature-migration-scan-2026-11-03.md §0.1)。
224+
225+
判 INVALID 的处置是「换号无用、跳过整个上游、以 400 收尾」,与「等额度」
226+
完全相反;同时真正的业务码 1005(权益耗尽)必须仍然判 PLAN。
227+
"""
228+
classify = codearts_events.classify_error_code
229+
assert classify(
230+
"InferHub.001001005.400 The request param is invalid, Please check it"
231+
) is ErrKind.INVALID
232+
assert classify("001001005") is ErrKind.INVALID
233+
assert classify("The request param is invalid") is ErrKind.INVALID
234+
# 防回归:真 PLAN 不能被放宽的 marker 抢走(`00001005` 只含 1005 子串)
235+
assert classify("1005") is ErrKind.PLAN
236+
assert classify("1005 quota exceeded") is ErrKind.PLAN
237+
assert classify("insufficient balance") is ErrKind.PLAN
238+
239+
240+
def test_events_param_invalid_status_path_is_invalid():
241+
"""HTTP 400 形态的同一错误也判 INVALID(`classify_status` 与流内同口径)。"""
242+
body = (b'{"error_code":"InferHub.001001005.400",'
243+
b'"error_msg":"The request param is invalid, Please check it"}')
244+
assert codearts_events.classify_status(400, body) is ErrKind.INVALID
245+
246+
217247
def test_events_multimodal_rejection_skips_provider_instead_of_rotating():
218248
"""HTTP 400 与流内两条路径对同一错误给出同一分类(都跳过该上游)。"""
219249
body = (b'{"error_code":"InferHub.001001020.406",'

0 commit comments

Comments
 (0)