共享记忆补记本轮 3D 收口的四条决策
- 产物 content type 不再兜底:白名单外一律按上游内容不合法失败(并作废预览条目里的回落口径) - 两个改价字段 geometryQuality / textureQuality 允许缺省,按 standard 计价 - provider 客户端构造即校验配置(零超时 / 空白凭据)与产物重试包住整个 body 记录在同一条
This commit is contained in:
@@ -9425,3 +9425,30 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 影响面:`server-rs/crates/shared-contracts/src/model3d/common/generation_target_ref.rs`(tagged enum → 平坦结构,`packages/shared/src/contracts/model3d/common/Model3dGenerationTargetRef.ts` 随之变宽)、`server-rs/crates/api-server/src/tripo3d/{validation.rs,target.rs,job.rs,worker.rs}`、`server-rs/crates/shared-contracts/tests/model3d_api_request_contract.rs`、`src/components/image-editor/model3d-generation/{Model3dGenerationSubmission.ts,useModel3dGenerationTask.ts}`、`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`、`src/components/image-editor/ImageCanvasAssetRowView.tsx`、技术方案 / 里程碑 / 实施计划三份 3D 文档与共享记忆。
|
||||
- 验证方式:`cargo test -p api-server tripo3d::`(56 passed,含 `target_accepts_both_locators_but_requires_at_least_one`、`flat_locator_maps_to_point_lookup_without_rewriting_ids`、`unavailable_field_names_the_given_locators`、`completed_result_is_strict_per_endpoint_and_free_of_provider_facts`)、`cargo test -p shared-contracts`、`npm run contracts:model3d:generate`、`npx vitest run src/components/image-editor/model3d-generation src/components/image-editor/ImageCanvasAssetRowView.test.tsx`(67 passed)、`npm run typecheck`、`npm run check:encoding`、`npm run check:rustfmt`、`node scripts/check-doc-index.mjs`、`git diff --check`。
|
||||
- 关联文档:[技术方案 Tripo 3D生成API集成](../../technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md)、[里程碑 Tripo生成API契约与数据模型](../plans/【里程碑】Tripo生成API契约与数据模型-2026-09-21.md)、[实施计划 Tripo生成前端入口](../plans/【实施计划】Tripo生成前端入口-2026-09-21.md)、[实施计划 Tripo生成结果前端预览接入](../plans/【实施计划】Tripo生成结果前端预览接入-2026-09-21.md)。
|
||||
|
||||
## 2026-09-23 3D 产物 content type 不再兜底:白名单外一律按上游内容不合法失败
|
||||
|
||||
- 背景:`tripo3d/storage.rs` 原先对字节嗅探失败的情况做两段兜底 —— 声明值为空或 `application/octet-stream` 时按槽位回落成 `model/gltf-binary` / `image/webp`,其余非空声明原样放行;而对象键扩展名只认少数几个 MIME、其余再回落 `.glb` / `.webp`。provider 回 `text/plain`、`text/html` 或任何意外类型时,就会落成「错的 content type + 拼出来的扩展名」,同一个错值还会进 OSS metadata 与 `asset_object.content_type`。
|
||||
- 决策:字节优先的口径不变,但「两边都对不上」时不再猜 —— 声明值必须落在槽位白名单内(模型:`model/gltf-binary`、`model/gltf+json`、`model/fbx`、`application/x-fbx`;预览:`image/png`、`image/jpeg`、`image/jpg`、`image/webp`),否则 `resolve_artifact_content_type` 返回 `502 model3d-artifact-content-type-unsupported`,不落库、由 worker 失败退款。`fallback_extension` 删除,`content type ↔ 对象键扩展名` 收敛成槽位上的唯一映射表。
|
||||
- 原因:兜底出来的类型会连同扩展名一起写进持久化元数据,比一次可见的失败更难发现;错误体只回显截断到 64 字符的声明值,不把 provider 的任意文本带进响应。
|
||||
- 代价与取舍:嗅探不到的罕见格式(例如 `image/avif`)不再被接受,会失败退款 —— 这是有意的「宁可失败也不要错值」。本条取代上一条「预览产物 content type 改为按字节识别」里「识别范围外保持既有回落」的写法。
|
||||
- 影响面:`server-rs/crates/api-server/src/tripo3d/storage.rs`(槽位白名单、`resolve_artifact_content_type` 改为返回 `Result`、定向用例重写)、[技术方案 Tripo 3D生成API集成](../../technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md)。
|
||||
- 验证方式:`cargo test --locked -p api-server tripo3d::storage`(7 passed,含白名单外声明、跨槽位类型与字节认不出三组)。
|
||||
|
||||
## 2026-09-23 3D 请求的两个改价字段允许缺省:`geometryQuality` / `textureQuality` 按 standard 计价
|
||||
|
||||
- 背景:API 层原先要求 `generation.geometryQuality` 对每个模型版本显式给出,`texture=true` 时还要求显式给出 `generation.textureQuality`;而 provider 预检只允许 v3.1 / v3.0 传 `geometry_quality`,并拒绝 v2.5 的任何 `texture_quality`。结果是定价表里已定价的 v2.5 / P1 / P2 无论填不填都过不了校验(`texture=true` 的 v2.5 更是永远不可达)。
|
||||
- 决策:两个字段都允许缺省,缺省按 `Model3dTextureQuality::Standard` / `Model3dGeometryQuality::Standard` 计入定价 —— `standard` 本来就不产生 add-on,因此缺省报价与显式 `standard` 完全一致;只有显式 `detailed` / `extreme` 才命中 `hdGeometry` / `hdTexture` / `ultraTexture`。请求本身不改写,缺省字段不会被塞进 provider 请求体。`texture`、`quad`、`smartLowPoly`、`generateParts` 与 `texture=false` 时禁止出现 `textureQuality` 的约束保持不变。
|
||||
- 原因:把「不会改价的缺省」从必填里摘出来,才不会让能力预检与必填规则互相打架;真正会改价的参数仍然必须显式给出,避免报价与扣费在「调用方少传字段」时分叉。
|
||||
- 代价与取舍:调用方少传这两个字段不再报错,参数缺失的定位信息少了一处;仍未解决的是「非 v3 家族显式传 `geometryQuality=standard` 会被 provider 预检拒绝」(review #80),需要时按该条另行决定。
|
||||
- 影响面:`server-rs/crates/api-server/src/tripo3d/validation.rs`(`PricingParamView::validate` 与定向用例)、[技术方案 Tripo 3D生成API集成](../../technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md)。
|
||||
- 验证方式:`cargo test --locked -p api-server tripo3d`(57 passed,含 `geometry_quality_is_optional_and_defaults_to_standard`、`texture_on_defaults_texture_quality_to_standard`)。
|
||||
|
||||
## 2026-09-23 provider 客户端两个边界收口:构造即校验配置,产物重试包住整个 body
|
||||
|
||||
- 背景:① `TripoSettings::new` 接受 `request_timeout = Duration::ZERO` 与空白 `api_key` / `base_url`,而零超时在 `tokio::time::timeout` 下「立即超时」、在 `reqwest` 下却是「不超时」,同一个值两种语义;② 产物下载的重试只包住 `download_artifact_once`(拿到响应头就返回),真正几十 MB 的字节在 `next_chunk` 里传输,中途断流不会重入重试循环,而且那时错误还带着 `status: Some(200)`,连 `is_retryable` 都判不出可重试。
|
||||
- 决策:① `TripoSettings::new` 改为返回 `Result`,零超时与空白凭据按 `TripoError::Configuration` 在构造时失败,`TripoProviderClient::new` 在使用前再校验一次(字段公开,结构体字面量能绕过构造函数);② 下载入口把「取响应头 + 读完 body」作为一次可重试事务,body 中途失败与长度不一致都归一成可重试的传输错误(`status: None`,HTTP 状态只留在文案里),每次尝试都重新取响应头并整体重下(不做断点续传),体积上限由调用方以 `max_bytes` 传入、超限按 `OutputSchema` 失败且不重试;对外返回读完的 `TripoArtifactBytes`,流式句柄 `TripoDownloadedArtifact` 收回 crate 内部。
|
||||
- 原因:重试范围必须与实际传输范围一致,否则一次 CDN 抖动会毁掉已经扣费、provider 任务也跑完的生成;配置错误应该在构造时大声失败,而不是拖到第一次产物下载时以「stalled」这种指错方向的报错暴露。
|
||||
- 代价与取舍:重下会丢掉已读字节并重新传输(换连接、重新取响应头),比断点续传多花流量,但代码与状态都更少,也避开了签名地址过期的问题;`download_model` / `download_rendered_image` 的签名加了 `max_bytes` 并改为返回完整字节,属于 `platform-tripo` 的公共 API 变更。
|
||||
- 影响面:`server-rs/crates/platform-tripo/src/common/{config,client,types,error}.rs`、`server-rs/crates/api-server/src/tripo3d/{artifacts,worker,errors,provider}.rs`、`platform-tripo` smoke 示例、[技术方案 Tripo 3D模型Provider集成](../../technical/【技术方案】Tripo 3D模型Provider集成-2026-09-18.md)。
|
||||
- 验证方式:`cargo test --locked -p platform-tripo`(16 passed,含 `artifact_download_retries_the_whole_body_after_a_broken_transfer`、`artifact_download_rejects_bodies_over_the_caller_limit`、`zero_timeout_and_blank_credentials_are_rejected`)、`cargo test --locked -p api-server tripo3d`(57 passed)、`cargo check -p platform-tripo --examples`。
|
||||
|
||||
Reference in New Issue
Block a user