feat(stream-check): refresh default models and detect model-not-found errors (#2099)

* chore(stream-check): update default health check models to latest

Replaces deprecated gpt-5.1-codex@low with gpt-5.4@low and switches
the Gemini default from gemini-3-pro-preview to gemini-3-flash-preview
to pick the lightest variant of the latest series for fast, low-cost
health checks.

https://claude.ai/code/session_01NGWLchcTP76rJHjiP5Ehte

* feat(stream-check): detect model-not-found errors with dedicated toast

Health check previously classified failures purely by HTTP status code,
which meant deprecated/invalid models showed up as a generic "Not found
(404)" error pointing users to check the Base URL — misleading when the
URL is fine and only the test model is wrong (e.g. gpt-5.1-codex after
it was retired).

Backend: add detect_error_category() that inspects 4xx response bodies
for model-not-found indicators (model_not_found, does not exist,
invalid model, not_found_error, etc.) and returns a "modelNotFound"
category. Thread the resolved test model through build_stream_check_result
so the failed result carries it in model_used. Add StreamCheckResult
.error_category field (serde-skipped when None).

Frontend: useStreamCheck branches on errorCategory === "modelNotFound"
before the HTTP-status fallback and renders a toast.error with the model
name and a description pointing to Model Test Config. Add i18n keys
(modelNotFound / modelNotFoundHint) for zh/en/ja.

Tests: unit-test detect_error_category against real OpenAI/Anthropic
error shapes, 5xx false-positive avoidance, and plain 401 auth errors.

https://claude.ai/code/session_01NGWLchcTP76rJHjiP5Ehte

* fix(stream-check): add missing error_category field in fallback

The error_category field was added to StreamCheckResult in this branch
but the fallback constructor in stream_check_all_providers was not
updated, which broke cargo build.

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Jason Young
2026-04-15 15:25:32 +08:00
committed by GitHub
parent ef41e4da46
commit 507bf038a9
8 changed files with 128 additions and 10 deletions
@@ -25,7 +25,7 @@ export function ModelTestConfigPanel() {
degradedThresholdMs: "6000",
claudeModel: "claude-haiku-4-5-20251001",
codexModel: "gpt-5.4@low",
geminiModel: "gemini-3-pro-preview",
geminiModel: "gemini-3-flash-preview",
testPrompt: "Who are you?",
});
+16
View File
@@ -46,6 +46,22 @@ export function useStreamCheck(appId: AppId) {
// 降级状态也重置熔断器,因为至少能通信
resetCircuitBreaker.mutate({ providerId, appType: appId });
} else if (result.errorCategory === "modelNotFound") {
// 专门处理"模型不存在/已下架":指向配置入口,比通用 404 文案更有指导性
toast.error(
t("streamCheck.modelNotFound", {
providerName: providerName,
model: result.modelUsed,
defaultValue: `${providerName} 测试模型 ${result.modelUsed} 不存在或已下架`,
}),
{
description: t("streamCheck.modelNotFoundHint", {
defaultValue: "",
}),
duration: 10000,
closeButton: true,
},
);
} else {
const httpStatus = result.httpStatus;
const hintKey = httpStatus
+2
View File
@@ -2131,6 +2131,8 @@
"failed": "{{providerName}} check failed: {{message}}",
"rejected": "{{providerName}} check rejected: {{message}}",
"error": "{{providerName}} check error: {{error}}",
"modelNotFound": "{{providerName}} test model {{model}} does not exist or has been deprecated",
"modelNotFoundHint": "This model may have been retired by the provider. Update the default test model in \"Model Test Config\".",
"httpHint": {
"400": "Provider rejected request format. Health check probe may differ from actual usage.",
"401": "API key may be invalid, or provider uses OAuth auth. Check failure doesn't mean it's unusable.",
+2
View File
@@ -2131,6 +2131,8 @@
"failed": "{{providerName}} のチェックに失敗しました: {{message}}",
"rejected": "{{providerName}} のチェックが拒否されました: {{message}}",
"error": "{{providerName}} のチェックでエラーが発生しました: {{error}}",
"modelNotFound": "{{providerName}} のテストモデル {{model}} は存在しないか廃止されています",
"modelNotFoundHint": "このモデルはプロバイダーにより廃止された可能性があります。「モデルテスト設定」でデフォルトのテストモデルを更新してください。",
"httpHint": {
"400": "リクエスト形式が拒否されました。ヘルスチェックの形式は実際の使用と異なる場合があります。",
"401": "APIキーが無効か、OAuthなどの認証方式を使用しています。チェック失敗は実際に使えないことを意味しません。",
+2
View File
@@ -2132,6 +2132,8 @@
"failed": "{{providerName}} 检查失败: {{message}}",
"rejected": "{{providerName}} 检查被拒: {{message}}",
"error": "{{providerName}} 检查出错: {{error}}",
"modelNotFound": "{{providerName}} 测试模型 {{model}} 不存在或已下架",
"modelNotFoundHint": "该模型可能已被供应商弃用。请在\"模型测试配置\"中更新默认测试模型。",
"httpHint": {
"400": "供应商拒绝了请求格式。健康检查的探测格式可能与实际使用不同。",
"401": "API Key 可能无效,或供应商使用 OAuth 等认证方式。检查失败不代表实际不可用。",
+2
View File
@@ -24,6 +24,8 @@ export interface StreamCheckResult {
modelUsed: string;
testedAt: number;
retryCount: number;
/** 细粒度错误分类,如 "modelNotFound" */
errorCategory?: string;
}
// ===== 流式健康检查 API =====