Compare commits

...

2 Commits

Author SHA1 Message Date
lili
d4cb740a86 docs(creds/ops): 补 dev 栈 HMAC 回调密钥入档 + 修 staging-ops 过时"secret 空"claim
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
AIGC_CALLBACK_SECRET=e8aab273…(32hex,Complete A 起已配、env 文件持久化)此前只在 mini-desktop
huijing-dev.env、未入仓凭据档(§9 缺口);staging-ops §100 还写着"空(验签关)"是过时。补凭据档
可复现副本(两端逐字相同=验签开、空=内网可伪造回调内测必须非空)+ 校正 §100 为非空 + 补 quota flag。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 07:57:21 -07:00
lili
231b17194a fix(cheap-gen): new-api 额度耗尽真机探测揪出误判——401+「额度已用尽」修为 quota_exhausted
内测开放项真环境探测(2026-07-08,dev 栈恢复后):建 remain_quota=0 测试 token 打 new-api
/v1/chat/completions,坐实 new-api 对额度耗尽返 HTTP 401 + message「该令牌额度已用尽
!token.UnlimitedQuota && token.RemainQuota = 0」——非代码假设的 402/403,且关键词只认「额度不足/
insufficient」漏了「额度已用尽」。旧 classify_newapi_failure_reason:401→llm_error(可重试)、
关键词不匹配 → 把耗尽误判成可重试 → 反复重试到 15min watchdog 超时,而非干净"额度用尽"。

修:message 额度关键词优先(含实测「额度已用尽」「RemainQuota = 0」),401 不再无脑判 llm_error
(401 与凭据失效重叠,按 message 分辨:耗尽→quota_exhausted、真凭据失效→llm_error 可重试);
402/403 保守保留判耗尽。删代码 §3.9【阶段2 待验】TODO(已真机坐实)。新增真机 401 回归用例,
26 测过。正是代码自带 TODO 与「真环境>桩」要抓的:桩测永远发现不了 new-api 用 401 不用 402/403。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 07:52:57 -07:00
4 changed files with 32 additions and 16 deletions

View File

@ -97,7 +97,7 @@ huijing 后端把生成请求投给 Python 生成线,靠 `aigc.executor` 这
派发链路:`submitGenerate` 落库 queued → `GenTaskProducer` 发 RocketMQ → `GenTaskConsumer` CAS 认领 → `WorkerDispatchClient` POST job 到 `worker-url` → Python worker 生成 → 回调 `/dify/callback-internal` → 落版本 + 运行包;`@Scheduled` tick 兜底轮询 DB。
dev 正确配置固化在 `/root/huijing-dev.env`chmod 600、单一事实源`/root/restart-huijing-dev.sh` 从它 `systemd-run` 重建 transient unit `huijing-dev`。关键项:`AIGC_EXECUTOR_ENABLED=true`(装配整组,默认 false = 不分派)、`AIGC_WORKER_URL=http://127.0.0.1:9501/generate`(逐字节对齐 worker 的 `DEFAULT_PORT=9501` + POST `/generate`)、`AIGC_CALLBACK_SECRET=` 空HMAC 验签关worker 侧 `CALLBACK_SECRET` 亦空 = 两端对齐,非空则两端必须逐字相同)、`NEWAPI_KEY` = api-key值见凭据档MiniMax 计费dispatcher 默认 `http`= `WorkerDispatchClient`,别开 `saa` 进程内旧路、dispatchTemplateId 默认 `generic`(见下)。
dev 正确配置固化在 `/root/huijing-dev.env`chmod 600、单一事实源`/root/restart-huijing-dev.sh` 从它 `systemd-run` 重建 transient unit `huijing-dev`。关键项:`AIGC_EXECUTOR_ENABLED=true`(装配整组,默认 false = 不分派)、`AIGC_WORKER_URL=http://127.0.0.1:9501/generate`(逐字节对齐 worker 的 `DEFAULT_PORT=9501` + POST `/generate`)、`AIGC_CALLBACK_SECRET` 非空 32hexComplete A 起已配、值见凭据档worker 起服带 `--callback-secret <同值>`,两端逐字相同 = HMAC 验签开;空=验签关=内网可伪造回调,内测必须非空)、`AIGC_NEWAPI_QUOTA_ENABLED=true`WU2 预置池 per-user 计费)、`NEWAPI_KEY` = api-key值见凭据档全局兜底/MiniMax 计费dispatcher 默认 `http`= `WorkerDispatchClient`,别开 `saa` 进程内旧路、dispatchTemplateId 默认 `generic`(见下)。
**分派闸是硬相等 `templateId=='generic'`**`AigcGenerateExecutor.java:535-536`):只有 templateId 恰等 `generic` 才投 worker其余business-sim 等旧模板名)一律 fall-through 到进程内 LLM 旧路、到不了 :9501。便宜档真实流一句话、不选模板= 草稿空 templateId → 后端归一 `generic` → 派 worker品类由 Python 层按 brief 文本判。**冒烟 / e2e 必须用 generic别用 business-sim**——它虽在 `SUPPORTED_TEMPLATE_IDS` 提交校验放行,却与分派闸解耦、会静默走旧路。

View File

@ -50,27 +50,33 @@ def newapi_error_status_text(exc) -> tuple:
def classify_newapi_failure_reason(status=None, text: str = "") -> str:
"""one-api/new-api 惯例把网关非 2xx 分辨成失败因(WU2 §3.9)。
"""new-api 真实响应把网关非 2xx 分辨成失败因(WU2 §3.9)。
返回 'quota_exhausted'(额度耗尽,干净失败不可重试) 'llm_error'(凭据失效/其他,可重试不塌成额度耗尽)
判据:HTTP 402/403 额度耗尽;401 凭据失效(维持 llm_error);状态码取不到时按错误体关键词兜底
阶段2 待验402/403 与确切错误体 message S0 只坐实了管理面未测 /v1/chat/completions 耗尽错误体,
确切匹配待 S4 真机耗尽/凭据失效测试坐实;此处先给 one-api 惯例初值(状态码优先关键词兜底)
判据(2026-07-08 真机耗尽探测坐实,取代旧的纯状态码假设):new-api **额度耗尽返 HTTP 401**
message 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = 0**401 与凭据失效状态码重叠**,
故必须按错误体 message 分辨不能只看状态码(旧代码 401llm_error 会把耗尽误判成可重试 反复重试到
watchdog 超时,而非干净"额度用尽")所以: message 命中额度关键词 quota_exhausted(优先,不看状态码);
否则 HTTP 402/403(one-api 惯例额度码,不与凭据失效重叠) quota_exhausted; 其余(401 无额度关键词
= 真凭据失效 / 其他) llm_error 可重试
"""
try:
s = int(status) if status is not None else None
except (TypeError, ValueError):
s = None
if s in (402, 403):
return "quota_exhausted"
if s == 401:
return "llm_error" # 凭据失效:维持可重试,不塌成 quota_exhausted(§3.9 ②)
# 状态码取不到时(被框架包装成普通异常)按 one-api 耗尽错误体惯例关键词兜底 —— 确切 message 阶段2 待精确。
t = (text or "").lower()
quota_markers = ("insufficient user quota", "insufficient quota", "余额不足", "额度不足", "quota not enough")
# 额度耗尽关键词(实测 new-api 中文 "该令牌额度已用尽" + 错误体内联判定条件 "RemainQuota = 0";兼容 one-api 英文/通用)。
quota_markers = (
"额度已用尽", "额度已用完", "额度不足", "令牌额度", "余额不足", # new-api/one-api 中文
"remainquota = 0", "remainquota=0", # new-api 错误体内联条件文本
"insufficient user quota", "insufficient quota", "quota not enough", # one-api 英文
)
if any(m in t for m in quota_markers):
return "quota_exhausted"
return "llm_error"
return "quota_exhausted" # ① message 优先:catch new-api 真实的 401+额度耗尽
if s in (402, 403):
return "quota_exhausted" # ② one-api 惯例额度码(与凭据失效 401 不重叠,保守判耗尽)
return "llm_error" # ③ 401 无额度关键词=真凭据失效 / 其他 → 可重试
# 源工程 2.0 profile 必填三枚举(contracts/agent-loop/source-project.schema.json:39)。
_REQUIRED_PROFILE_KEYS = ("tickModel", "inputModel", "progressModel")

View File

@ -142,16 +142,25 @@ def test_explicit_failure_reason_wins_over_summary():
def test_classify_newapi_status_codes():
"""one-api 惯例:402/403 → quota_exhausted;401 → llm_error(凭据失效,可重试,不塌成额度耗尽)"""
"""状态码分辨:402/403(one-api 惯例额度码)→ quota_exhausted;裸 401 / 500 / None(无额度关键词)→ llm_error 可重试"""
assert R.classify_newapi_failure_reason(402) == "quota_exhausted"
assert R.classify_newapi_failure_reason(403) == "quota_exhausted"
assert R.classify_newapi_failure_reason(401) == "llm_error"
assert R.classify_newapi_failure_reason(401) == "llm_error" # 裸 401 无 message = 真凭据失效
assert R.classify_newapi_failure_reason(500) == "llm_error"
assert R.classify_newapi_failure_reason(None) == "llm_error"
def test_classify_newapi_real_quota_exhausted_401():
"""2026-07-08 真机耗尽探测坐实:new-api 对额度耗尽返 HTTP 401 + 「该令牌额度已用尽 …RemainQuota = 0」。
401 与凭据失效状态码重叠,故必须按 message 分辨耗尽判 quota_exhausted(干净失败),真凭据失效仍 llm_error(可重试)
这是关键回归:旧代码 401llm_error 会把耗尽误判成可重试 反复重试到 watchdog 超时"""
real = "[sk-air***0AS] 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = 0 (request id: 20260708...)"
assert R.classify_newapi_failure_reason(401, real) == "quota_exhausted"
assert R.classify_newapi_failure_reason(401, "无效的令牌,请检查") == "llm_error" # 真凭据失效仍可重试、不塌成耗尽
def test_classify_newapi_keyword_fallback():
"""状态码取不到(被框架包装)时按 one-api 错误体关键词兜底分辨额度耗尽(阶段2 待精确 message)。"""
"""message 关键词分辨额度耗尽(状态码取不到 / 被框架包装时同样生效,优先于状态码)。"""
assert R.classify_newapi_failure_reason(None, "error: insufficient user quota") == "quota_exhausted"
assert R.classify_newapi_failure_reason(None, "当前分组余额不足,请充值") == "quota_exhausted"
assert R.classify_newapi_failure_reason(None, "connection reset by peer") == "llm_error"

View File

@ -26,6 +26,7 @@ canonical: true
- **`NEWAPI_PG_PASSWORD`** = new-api 库(`infra-postgres`)的 postgres 超级用户口令 = **`f6710e2d0294eb1c10e26a805a64bc54`**(用户名 **`root`** 非 postgres;经 `docker exec -e PGPASSWORD=… infra-postgres psql -U root -d new-api` 连,已实测可连、2026-07-08 池水位 11 条可用 token
- **`NEWAPI_ROOT_TOKEN`**(选填,建户/设 quota 用):脚本未设时自动从 `new-api.users.id=1` 读,一般无需显式给。
- **补池**:池空则新用户 claim 不到额度、拿不到生成配额 → 提高 `--count` 重跑脚本(ensure 语义、按确定性 username 去重、不重复建号)。
- **dev 栈 backend↔worker 回调 HMAC 密钥** `AIGC_CALLBACK_SECRET` = **`e8aab27300f74bca3717ce0670644dfc`**(32hex;后端经 `/root/huijing-dev.env` 注入、便宜档 worker 起服带 `--callback-secret <同值>`,两端必须逐字相同;空=验签关闭=内网可伪造回调,内测必须非空)。运行单源 = mini-desktop `/root/huijing-dev.env`(chmod 600),此处为可复现副本。
- **可用模型档(对比测试 / 证路用 · 2026-06-25 补)**
- **`glm-5.2`** —— **Anthropic message 协议**(走 `/v1/messages`),定位 **opus 高级模型档**,上下文 **512K**。后续作**高级档对比测试 / 强模型 cross-check 证路**用,填补复杂档「真强模型证路」此前缺 opus 档的空缺(原 deepseek-v4-pro 跨族证路力弱)。
- 便宜档生成主力 = **MiniMax-M3**Anthropic 原生分离 thinking,见上「协议端点」);其余 provider 模型见 application.yaml `channels.*`