fix: surface invalid model credentials without retry loop

This commit is contained in:
chenpei 2026-09-08 12:43:26 +08:00
parent 1f1e878b75
commit aa412fb893
5 changed files with 21515 additions and 6 deletions

View File

@ -299,3 +299,12 @@
- 第二次服务器真实内核验收通过:铰链生成 113053 字节 STEP 和 61632 字节 GLB,孔工序数为 5;旋钮生成 30274 字节 STEP 和 21168 字节 GLB,孔工序数为 1。两个样例的 B-rep 都为零干涉,报告在服务器 `hardware-template-brep-v113-fused/report.json`。
- 本地结构图谱与制造层 91 项专项回归通过;最终完整回归 227 项全部通过(233.019秒,其中 1 项真实内核用例按设计仅在服务器开关启用时执行),Python 语法和 `git diff --check` 通过。
- 仍未完成公司实际五金 SKU 模板批准、原始多图到 v2 图谱的新识别、多产品冻结后 BOM/成本盲测和总成本误差不超过 5%。本版仍是未部署候选,不得用以上样例通过替代真实多产品验收。
## 2026-09-08 v115-v116 原图重跑与模型鉴权终止门(候选,未上线)
- v115 启动前持续只读检查服务器真实任务状态;先等待正在执行的得物1688任务完成,确认 Chat API 活动、等待队列和持久任务均为 0 后,才在 `/tmp/dalongxia-cad-v85-eI8HeF` 使用四张原始产品图启动全新识别。没有复用公司 BOM、旧报告或历史模型结果,也没有修改生产目录、重启或部署服务。
- v115 在进入 CAD 前被阿里云模型接口以 HTTP 401 `invalid_api_key` 拒绝。服务器密钥文件存在、权限为 600,且其内容指纹与用户最近提供的密钥完全一致;对同一接口的最小请求也稳定返回 401,证明这是远端鉴权凭据失效,不是任务缺图、缺尺寸或本地环境未加载密钥。
- 旧异常分支把结构读图的 401 吞成普通机器重读失败,随后继续做尺寸完整性判断,最终误报为缺少 x/y/z 轴并可能引导用户补传素材。这条“鉴权失败后继续资格判断”的路线判定错误,已停止使用。
- v116 将 401/403 单独归类为不可重试的 `model_credentials_invalid`。模型请求只执行一次,源资格层立即以 `external_dependency_failed` 终止,不再重复请求模型,也不再向用户虚构缺图、缺尺寸或 CAD 问题。服务器隔离现场耗时 3.753 秒,返回中文阻塞信息“读图模型已拒绝当前鉴权凭据”。
- 候选完整回归 229 项通过(246.290 秒),1 项真实 CAD 内核用例按设计因本机未启用服务器依赖而跳过;新增专项测试验证 401 只请求一次、鉴权失败不进入读图/CAD循环。v115 因外部鉴权阻塞,尚未实际验证上一轮分阶段 CAD 修复门能否在这组原图上完成收敛,不能宣称原图重跑成功。
- 当前仍需新的有效阿里云模型密钥才能继续多视图识别、CAD/BOM冻结盲测和公司成本误差不超过 5% 的目标。本轮候选未上线、未重启生产服务,接口状态必须保持测试失败/继续优化。

View File

@ -50,6 +50,14 @@ def terminal_dependency_feedback(error: Exception | str) -> dict | None:
"next_owner": "runtime_configuration",
"retryable": False,
}
if any(token in message for token in ("模型鉴权失败", "HTTP Error 401", "HTTP Error 403", "invalid_api_key")):
return {
"code": "model_credentials_invalid",
"severity": "blocking",
"detail": "读图模型已拒绝当前鉴权凭据",
"next_owner": "runtime_configuration",
"retryable": False,
}
return None
@ -1197,16 +1205,20 @@ def main():
else:
print(json.dumps({"case": case, "phase": "ai_source_evidence_qualification"}), flush=True)
source_structure, source_dimensions = {}, {}
source_dependency = None
try:
source_structure = engine._read_source_structure(source_assets)
except Exception as exc:
source_structure = {"status": "machine_reread_failed", "error": f"{type(exc).__name__}: {exc}"}
try:
source_dimensions = engine._read_source_dimension_labels(
source_assets[-1], [source_assets[0], source_assets[1]]
)
except Exception as exc:
source_dimensions = {"status": "machine_reread_failed", "error": f"{type(exc).__name__}: {exc}"}
source_dependency = terminal_dependency_feedback(exc)
if source_dependency is None:
try:
source_dimensions = engine._read_source_dimension_labels(
source_assets[-1], [source_assets[0], source_assets[1]]
)
except Exception as exc:
source_dimensions = {"status": "machine_reread_failed", "error": f"{type(exc).__name__}: {exc}"}
source_dependency = terminal_dependency_feedback(exc)
dimension_ledger = build_dimension_ledger(
source_dimensions.get("overall_dimension_labels") or [], source_image_id="image4"
)
@ -1218,6 +1230,20 @@ def main():
"topology_contract": topology_contract,
"policy": "AI先交叉识别和CAD复核;只有不可见且成本敏感的残余冲突才询问用户",
}
if source_dependency:
report["status"] = "external_dependency_failed"
report["source_qualification"]["blocking"] = source_dependency
reports.append(report)
(case_dir / "report.json").write_text(
json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8"
)
(out / "report.json").write_text(
json.dumps(reports, ensure_ascii=False, indent=2), encoding="utf-8"
)
print(json.dumps({
"case": case, "status": report["status"], "blocking": source_dependency,
}, ensure_ascii=False), flush=True)
continue
source_qualification = report["source_qualification"]
source_structure = source_qualification.get("structure") or {}
source_dimensions = source_qualification.get("dimensions") or {}

File diff suppressed because it is too large Load Diff

View File

@ -79,6 +79,9 @@ class ManufacturingOracleTest(unittest.TestCase):
feedback = terminal_dependency_feedback(RuntimeError("家具读图模型尚未配置密钥"))
self.assertEqual("model_credentials_missing", feedback["code"])
self.assertFalse(feedback["retryable"])
invalid = terminal_dependency_feedback(RuntimeError("读图模型鉴权失败(HTTP 401)"))
self.assertEqual("model_credentials_invalid", invalid["code"])
self.assertFalse(invalid["retryable"])
self.assertIsNone(terminal_dependency_feedback(RuntimeError("ordinary compile error")))
def test_visual_review_drops_self_declared_non_issues_and_unproven_proportion_guess(self):

File diff suppressed because it is too large Load Diff