上游瞬时状态不再回落成 400
- 408 / 425 / 429 与 platform-tripo 的 is_retryable 口径对齐,分别回 504 / 502 / 429 - 补上这三档与客户端 4xx 的分支测试
This commit is contained in:
@@ -48,25 +48,62 @@ pub(crate) fn map_pricing_store_error(error: EditorGenerationPricingError) -> Ap
|
||||
}))
|
||||
}
|
||||
|
||||
/// provider 错误归一:参数类回 400,其余按“上游不可用/上游拒绝”分流。
|
||||
/// provider 错误归一:参数类回 400,瞬时状态按可重试语义上报,其余按“上游不可用/上游拒绝”分流。
|
||||
pub(crate) fn map_provider_error(error: TripoError) -> AppError {
|
||||
let (status, reason) = match &error {
|
||||
TripoError::InvalidParameters { .. } | TripoError::SdkInvalidArgument(_) => {
|
||||
(StatusCode::BAD_REQUEST, "tripo-invalid-parameters")
|
||||
}
|
||||
TripoError::Api { status, .. } if matches!(status, Some(400..=499)) => {
|
||||
(StatusCode::BAD_REQUEST, "tripo-api-rejected")
|
||||
}
|
||||
TripoError::TaskFailure { .. } => (StatusCode::BAD_GATEWAY, "tripo-task-failed"),
|
||||
TripoError::OutputSchema { .. } => (StatusCode::BAD_GATEWAY, "tripo-output-schema"),
|
||||
TripoError::Request { .. } | TripoError::Sdk { .. } | TripoError::Api { .. } => {
|
||||
(StatusCode::BAD_GATEWAY, "tripo-upstream-error")
|
||||
}
|
||||
let (status, reason, message) = match &error {
|
||||
TripoError::InvalidParameters { .. } | TripoError::SdkInvalidArgument(_) => (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"tripo-invalid-parameters",
|
||||
error.to_string(),
|
||||
),
|
||||
// 408 / 425 / 429 与 platform-tripo 的 is_retryable 口径一致,属于瞬时状态:
|
||||
// 混进 400 会让客户端把限流 / 上游超时误读成自己参数写错了。
|
||||
TripoError::Api {
|
||||
status: Some(429), ..
|
||||
} => (
|
||||
StatusCode::TOO_MANY_REQUESTS,
|
||||
"tripo-api-rate-limited",
|
||||
"3D 生成服务当前限流,请稍后重试。".to_string(),
|
||||
),
|
||||
TripoError::Api {
|
||||
status: Some(408), ..
|
||||
} => (
|
||||
StatusCode::GATEWAY_TIMEOUT,
|
||||
"tripo-api-timeout",
|
||||
"3D 生成服务响应超时,请稍后重试。".to_string(),
|
||||
),
|
||||
TripoError::Api {
|
||||
status: Some(425), ..
|
||||
} => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"tripo-api-retry-later",
|
||||
"3D 生成服务暂时无法处理该请求,请稍后重试。".to_string(),
|
||||
),
|
||||
TripoError::Api { status, .. } if matches!(status, Some(400..=499)) => (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"tripo-api-rejected",
|
||||
error.to_string(),
|
||||
),
|
||||
TripoError::TaskFailure { .. } => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"tripo-task-failed",
|
||||
error.to_string(),
|
||||
),
|
||||
TripoError::OutputSchema { .. } => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"tripo-output-schema",
|
||||
error.to_string(),
|
||||
),
|
||||
TripoError::Request { .. } | TripoError::Sdk { .. } | TripoError::Api { .. } => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"tripo-upstream-error",
|
||||
error.to_string(),
|
||||
),
|
||||
};
|
||||
AppError::from_status(status).with_details(json!({
|
||||
"provider": TRIPO_PROVIDER,
|
||||
"reason": reason,
|
||||
"message": error.to_string(),
|
||||
"message": message,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -100,4 +137,33 @@ mod tests {
|
||||
});
|
||||
assert_eq!(error.status_code(), StatusCode::BAD_GATEWAY);
|
||||
}
|
||||
|
||||
/// 408 / 425 / 429 是 platform-tripo 认定的瞬时状态,不能按「参数有问题」回 400。
|
||||
#[test]
|
||||
fn provider_transient_4xx_is_not_reported_as_bad_request() {
|
||||
for (status, expected) in [
|
||||
(429u16, StatusCode::TOO_MANY_REQUESTS),
|
||||
(408, StatusCode::GATEWAY_TIMEOUT),
|
||||
(425, StatusCode::BAD_GATEWAY),
|
||||
] {
|
||||
let error = map_provider_error(TripoError::Api {
|
||||
code: 1001,
|
||||
message: Some("transient".to_string()),
|
||||
suggestion: None,
|
||||
status: Some(status),
|
||||
});
|
||||
assert_eq!(error.status_code(), expected, "status {status}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_client_side_4xx_stays_bad_request() {
|
||||
let error = map_provider_error(TripoError::Api {
|
||||
code: 1002,
|
||||
message: Some("bad parameter".to_string()),
|
||||
suggestion: None,
|
||||
status: Some(400),
|
||||
});
|
||||
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user