diff --git a/server-rs/crates/api-server/src/tripo3d/worker.rs b/server-rs/crates/api-server/src/tripo3d/worker.rs index 7a27d698a..aa445cce7 100644 --- a/server-rs/crates/api-server/src/tripo3d/worker.rs +++ b/server-rs/crates/api-server/src/tripo3d/worker.rs @@ -71,6 +71,11 @@ pub(crate) async fn process_model3d_job( run_model3d_job(state, caller, job, request, provider_deadline), ) .await + // 这里返回的字符串会成为 job 的 `last_error_message`,并原样回给前端(见 + // `editor_generation_queue_state` 的 `error` 字段),所以刻意只取 provider 错误体里 + // 人类可读的 message:用户可见文案不带内部 reason / taskStatus 枚举。 + // 评审 #59 提过把结构化 details 一并写进去,按决定不改 —— 那会改掉已落库与前端展示的 + // 文案口径;排障要定位 provider 侧任务时看 job 的 provider task id(checkpoint)。 .map_err(|error| error.body_text()) } diff --git a/server-rs/crates/platform-tripo/src/common/error.rs b/server-rs/crates/platform-tripo/src/common/error.rs index e3bf269af..74d967152 100644 --- a/server-rs/crates/platform-tripo/src/common/error.rs +++ b/server-rs/crates/platform-tripo/src/common/error.rs @@ -113,6 +113,13 @@ pub enum TripoError { field: &'static str, message: String, }, + /// SDK 自身报出的错误(本地 IO、反序列化等),只保留文本。 + /// + /// 按 ADR 0001「不把 `tripo3d-sdk` 的错误模型带出平台边界」,这里不保留 + /// `std::io::Error` / serde 错误的类型,也不实现 `Error::source()`:调用方拿到的是 + /// 平台层归一化后的错误,而不是第三方错误链。评审 #60 提过拆 `SdkIo` / `SdkSerde` + /// 变体或加 `source` 字段,按决定不改 —— 那是公共错误枚举的 breaking 变更, + /// 收益只到排障细分一层。 Sdk { message: String, }, @@ -425,6 +432,8 @@ impl From for TripoError { // 等待超时是瞬时的:重试一次可能就等到了。 retryable: true, }, + // Io 与 Serde 拍平成同一个文本变体:上游 SDK 对这两种错误只保证 Display 文本, + // 平台层也不再向上区分(理由见 `Sdk` 变体的注释,评审 #60)。 tripo3d_sdk::Error::Io(error) => Self::Sdk { message: error.to_string(), }, diff --git a/server-rs/crates/platform-tripo/src/common/mapping.rs b/server-rs/crates/platform-tripo/src/common/mapping.rs index 9c05599ef..2055cd4c2 100644 --- a/server-rs/crates/platform-tripo/src/common/mapping.rs +++ b/server-rs/crates/platform-tripo/src/common/mapping.rs @@ -98,6 +98,10 @@ fn map_output( TripoTaskType::TextToModel => Ok(TripoTaskOutput::TextToModel(TripoTextToModelResult { model_url, rendered_image_url, + // Tripo API 文档把 generated_image_url 列为 text_to_model 输出的固定字段, + // 真实 provider smoke 也确认它总是存在(见技术方案《Tripo 3D模型Provider集成》)。 + // 因此按 ADR 0001「结果缺必填字段即以 output schema error 失败关闭」保留必填: + // 真缺失说明上游偏离了文档,属于要暴露的异常,不做静默兜底(评审 #61,按决定不改)。 generated_image_url: required_url( task_id, "generated_image_url", diff --git a/server-rs/crates/platform-tripo/src/image_to_model/client.rs b/server-rs/crates/platform-tripo/src/image_to_model/client.rs index d2fcd1cc7..7311e03d2 100644 --- a/server-rs/crates/platform-tripo/src/image_to_model/client.rs +++ b/server-rs/crates/platform-tripo/src/image_to_model/client.rs @@ -12,6 +12,12 @@ use super::validation::validate_image_to_model_params; /// /// 平台层只接受站内引用,所以调用方只会产出这两种形态:先上传拿 `file_token`, /// 或给出公网可读地址。不提供裸字符串变体,避免 SDK 把字符串按前缀推断成 task_id。 +/// +/// `PublicUrl` 只做非空白校验、不做 scheme / host 白名单:Tripo API 文档允许 `url` 指向任意 +/// 公网可读地址,本地再收窄会误伤文档允许的形态。当前生产路径全部走 `upload_image` → +/// `FileToken`,`PublicUrl` 只由冒烟示例构造,没有不可信输入的现役入口;将来若把外部传入的 +/// 地址接到这条路径,应在构造侧按 `TripoUrl::parse` 的口径校验 scheme 并拒绝 userinfo / +/// 内网主机(评审 #65,按决定不在本层加校验)。 #[derive(Clone, Debug, Eq, PartialEq)] pub enum TripoImageInput { FileToken(String),