b33a8a141c
- download_model / download_rendered_image 把「取响应头 + 读完 body」作为一次可重试事务, 每次尝试都重新取响应头并整体重下,不做断点续传 - body 传输失败与长度不一致归一成可重试的传输错误(不再带 HTTP 200),HTTP 状态只留在文案里 - 新增 TripoArtifactBytes(读完的产物)并公开 download_model / download_rendered_image 的 max_bytes 上限; 超限按 OutputSchema 失败且不重试,流式句柄 TripoDownloadedArtifact 收回 crate 内部 - api-server 的 read_artifact 改成对完整产物做落库前校验(只判空),体积上限转由调用方传入 - smoke 示例改为一次写入完整字节;新增「body 中途断开后整体重下成功」与「超限不重试」两个用例 - 同步技术方案与实施计划:把「只重试响应头」的描述改成「重试包住整个 body」
42 lines
1.6 KiB
Rust
42 lines
1.6 KiB
Rust
//! provider 产物的读取。
|
|
//!
|
|
//! 下载与「body 中途失败整体重下」都在 `platform-tripo` 的下载入口里完成(重试上限
|
|
//! 取自 provider 客户端配置),这里只做 api-server 自己的口径:产物不能为空。
|
|
//! TODO(stream): provider SDK 与 `platform-oss` 都支持流式 / 分片后,这里应当直接
|
|
//! 把流转交 OSS,不再把几十 MB 的模型完整读进 api-server 内存。
|
|
|
|
use axum::http::StatusCode;
|
|
use platform_tripo::TripoArtifactBytes;
|
|
use serde_json::json;
|
|
|
|
use crate::http_error::AppError;
|
|
|
|
use super::provider::TRIPO_PROVIDER;
|
|
|
|
/// 单次下载允许的单个产物上限。超过它说明响应异常或产物口径变了,
|
|
/// 宁可失败退款也不要把 worker 的内存打满。
|
|
pub(crate) const MODEL3D_MAX_ARTIFACT_BYTES: u64 = 512 * 1024 * 1024;
|
|
|
|
pub(crate) struct DownloadedArtifact {
|
|
pub(crate) bytes: Vec<u8>,
|
|
pub(crate) content_type: String,
|
|
}
|
|
|
|
/// 完整读出的产物在落库前的最后一道检查:空产物按上游内容不合法失败,
|
|
/// 不写出一个 0 字节的对象再让下游去猜。
|
|
pub(crate) fn read_artifact(artifact: TripoArtifactBytes) -> Result<DownloadedArtifact, AppError> {
|
|
if artifact.bytes.is_empty() {
|
|
return Err(
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": TRIPO_PROVIDER,
|
|
"reason": "model3d-artifact-empty",
|
|
"message": "provider 返回的 3D 产物为空。",
|
|
})),
|
|
);
|
|
}
|
|
Ok(DownloadedArtifact {
|
|
bytes: artifact.bytes,
|
|
content_type: artifact.content_type.unwrap_or_default(),
|
|
})
|
|
}
|