From 25ee11771ccbfcd66a5f6595f7ab3d484a32d0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 21 Sep 2026 15:21:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5Tripo=20provider=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=B8=8E=E6=9C=AC=E6=AC=A1=E8=A1=8C=E4=B8=BA=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20-=20=E8=84=B1=E6=95=8F=E8=BE=93=E5=87=BA=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=8E=9F=E5=88=86=E9=9A=94=E7=AC=A6=E3=80=81=E5=B8=A6?= =?UTF-8?q?=20userinfo=20=E7=9A=84=E5=9C=B0=E5=9D=80=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=8D=B3=E6=8B=92=E7=BB=9D=20-=20=E5=9B=BE=E7=89=87=E4=B8=8E?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E5=BC=95=E7=94=A8=E6=8F=90=E4=BA=A4=E5=89=8D?= =?UTF-8?q?=20trim=EF=BC=9B=E6=A0=A1=E9=AA=8C=E5=8E=9F=E5=9B=A0=E5=88=86?= =?UTF-8?q?=20Required=20/=20InvalidCombination=20/=20OutOfRange=20-=20?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=89=8D=E8=A6=81=E6=B1=82=E5=BF=AB=E7=85=A7?= =?UTF-8?q?=E4=B8=BA=E5=AE=8C=E6=88=90=E6=80=81=EF=BC=8Cfilename=20?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=90=8D=E6=94=B6=E7=B4=A7=E4=B8=BA=E7=9F=AD?= =?UTF-8?q?=20ASCII=20=E5=AD=97=E6=AF=8D=E6=95=B0=E5=AD=97=20-=20=E9=A1=BA?= =?UTF-8?q?=E5=B8=A6=E6=8A=8A=E6=96=87=E6=A1=A3=E9=87=8C=E7=9A=84=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=8C=85=E8=A3=85=E7=B1=BB=E5=9E=8B=E5=90=8D=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=BD=93=E5=89=8D=E7=9A=84=20TripoDownloadedArtifact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../【技术方案】Tripo 3D模型Provider集成-2026-09-18.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/technical/【技术方案】Tripo 3D模型Provider集成-2026-09-18.md b/docs/technical/【技术方案】Tripo 3D模型Provider集成-2026-09-18.md index b31234d75..25fe3a261 100644 --- a/docs/technical/【技术方案】Tripo 3D模型Provider集成-2026-09-18.md +++ b/docs/technical/【技术方案】Tripo 3D模型Provider集成-2026-09-18.md @@ -16,7 +16,7 @@ `platform-tripo` 是唯一接触 `tripo3d-sdk` 的边界。它返回自己的 provider DTO,不让 SDK 类型穿透到未来的 `api-server`。Tripo 的 `success` 映射为 `completed`;`failed`/`banned` 映射为 `failed`;`cancelled` 和 `expired` 保留为独立终态;未知状态返回结构化错误。 -provider task 结果与产品资源结果分离。provider adapter 不生成 `resourceId` 或 `assetId`;未来应用层在资源持久化后再构造带资源 ID 的产品 DTO。模型 URL 被视为临时 provider 引用,下载由显式方法完成。`TripoUrl` 的 `Debug` 与 `redacted()` 只输出 scheme、host 与 path,隐藏可能带签名的 query 与 fragment;完整 URL 仅通过 `as_str()` 显式读取;`TripoUrl` 不实现 `Display`,避免 `{}` 这类通用格式化把带签名的完整地址写进日志,冒烟示例同样只打印脱敏后的地址。 +provider task 结果与产品资源结果分离。provider adapter 不生成 `resourceId` 或 `assetId`;未来应用层在资源持久化后再构造带资源 ID 的产品 DTO。模型 URL 被视为临时 provider 引用,下载由显式方法完成。`TripoUrl` 的 `Debug` 与 `redacted()` 只输出 scheme、host 与 path,隐藏可能带签名的 query 与 fragment,并保留命中的分隔符(`?` 或 `#`),不把 fragment 显示成 query;带 userinfo 的地址在解析阶段直接拒绝。完整 URL 仅通过 `as_str()` 显式读取;`TripoUrl` 不实现 `Display`,避免 `{}` 这类通用格式化把带签名的完整地址写进日志,冒烟示例同样只打印脱敏后的地址。 task 尚未完成时 `output` 为空;完成后 `output` 必须是与 task type 一致的 enum variant,不使用把不同 endpoint 字段揉在一起的通用可选字段结构。真实 provider smoke 确认:text-to-model 结果固定包含 `model_url`、`rendered_image_url`、`generated_image_url`;image-to-model 和 multiview-to-model 结果固定包含 `model_url`、`rendered_image_url`。这些字段在各自结果 struct 中均为必填 `TripoUrl`;缺失、URL 非法或 task type 不受支持时返回 `TripoError::OutputSchema`。 @@ -26,7 +26,7 @@ task 尚未完成时 `output` 为空;完成后 `output` 必须是与 task type - text-to-model:`prompt` 必填,上限 1024 字符;`negative_prompt` 上限 255 字符。空白 prompt 在提交前拒绝。 - image-to-model:`input` 是单张参考图引用,序列化为裸字符串并由服务端推断是公开 URL 还是 `file_token`;空白 `input` 在提交前拒绝。 -- multiview-to-model:`inputs` 是带 `kind` 的 tagged enum,二选一——`views { front, left, back, right }` 或复用已有结果的 `taskId`。`views` 下 `front` 必填,其余视图至少再提供一张(少于两张视图直接拒绝);左/后/右视图为空白字符串时按未提供处理,不会上传空文件。`taskId` 走统一的 task id 校验。 +- multiview-to-model:`inputs` 是带 `kind` 的 tagged enum,二选一——`views { front, left, back, right }` 或复用已有结果的 `taskId`。`views` 下 `front` 必填,其余视图至少再提供一张(少于两张视图直接拒绝);左/后/右视图为空白字符串时按未提供处理,不会上传空文件。图片与视图引用都先去掉首尾空白再交给 SDK,`taskId` 走统一的 task id 校验。 三个入口的 `model` 都必填,其余生成参数可选。SDK params 未命名的 `texture_version` 与 `delight`,由 image-to-model 和 multiview-to-model 通过 extra 字段透传。 @@ -34,7 +34,7 @@ task 尚未完成时 `output` 为空;完成后 `output` 必须是与 task type 三个生成请求使用官方文档已确认的 enum:模型版本、纹理版本、纹理质量(含 `fast`)、几何质量、纹理对齐、输入方向、导出方向和压缩类型(`geometry`)。文档未将 `style` 列为这三个 endpoint 的请求字段,因此不再保留占位 enum。`model` 在三个请求中都是必填字段(缺失或取值非法时在反序列化阶段即拒绝);除它以外的参数可选,在 Rust 侧为 `Option`,TypeScript 绑定对应 `field?: T | null`,调用方既可省略字段也可显式传 `null`。TypeScript 绑定仍按目录生成到 `packages/shared/src/contracts/model3d/`,并由该目录的 `index.ts` 与 `packages/shared` 的 barrel 统一再导出;生成命令 `npm run contracts:model3d:generate` 写入的是未格式化的 ts-rs 输出,需再执行 `prettier --write packages/shared/src/contracts/model3d` 后提交。 -提交前由 provider 统一执行组合校验:模型版本决定 family;`fast` 必须配 `v3.5-20260815`;几何质量 / 压缩 / 自动尺寸 / P 系列与 H 系列能力按模型版本限制;`quad`、`generate_parts`、`smart_low_poly` 和 `texture/pbr` 组合按文档的互斥关系拒绝;`face_limit` 按模型、quad 和 smart-low-poly 模式检查范围。SDK 返回的参数错误仍统一归一为 `TripoError`。 +提交前由 provider 统一执行组合校验:模型版本决定 family;`fast` 必须配 `v3.5-20260815`;几何质量 / 压缩 / 自动尺寸 / P 系列与 H 系列能力按模型版本限制;`quad`、`generate_parts`、`smart_low_poly` 和 `texture/pbr` 组合按文档的互斥关系拒绝;`face_limit` 按模型、quad 和 smart-low-poly 模式检查范围。校验失败按原因分档:缺字段为 `Required`、参数组合互斥为 `InvalidCombination`、长度或范围越界为 `OutOfRange`。SDK 返回的参数错误仍统一归一为 `TripoError`。 契约分层:三个入口都导出 request 契约,`common/` 导出共享 enum;产品级结果契约(`Model3dGenerationSubmission`、`Model3dGenerationJob`、`Model3dGenerationResult`、`Model3dArtifact`)目前只服务 text-to-model。image-to-model 与 multiview-to-model 的完成结果仍停在 provider 层的 `TripoImageToModelResult`、`TripoMultiviewToModelResult`,等应用层真正做资源持久化时再决定是否提升为产品契约。 @@ -42,7 +42,7 @@ task 尚未完成时 `output` 为空;完成后 `output` 必须是与 task type `get_task` 按 `TripoTaskHandle` 做单次查询,返回通用 `TripoTaskSnapshot`,由 `task_type` 决定 `output` 的具体 variant;adapter 不做轮询、不阻塞等待。 -`download_model` 接受 `TripoTaskSnapshot`,从严格 endpoint 结果中取得已校验的 `model_url`,由 provider 自己的无鉴权 reqwest client 打开签名 URL 并返回 `TripoDownloadedModel` 流包装。包装只公开 `url`、`content_type`、`content_length`、`filename(name)` 和 `next_chunk()`;不提供完整 `Vec`,调用方必须逐块消费。provider 在流结束时校验实际接收字节数与 `Content-Length`,响应体中断或长度不一致按结构化请求错误失败;SDK 保持第三方原样,不承担产物下载。smoke example 使用异步文件写入逐块落盘;未来接入 OSS 时应把同一数据流直接送入 OSS 分片上传,不经过完整内存缓冲。 +`download_model` 接受 `TripoTaskSnapshot`,先确认快照确实处于完成态,再从严格 endpoint 结果中取得已校验的 `model_url`,由 provider 自己的无鉴权 reqwest client 打开签名 URL 并返回 `TripoDownloadedModel` 流包装。包装只公开 `url`、`content_type`、`content_length`、`filename(name)` 和 `next_chunk()`;不提供完整 `Vec`,调用方必须逐块消费。`filename(name)` 的扩展名来自远端地址,只接受短的 ASCII 字母数字,其余退回 `glb`,避免远端地址里的 `%2F` 解码后拼出跨目录路径。provider 在流结束时校验实际接收字节数与 `Content-Length`,响应体中断或长度不一致按结构化请求错误失败;SDK 保持第三方原样,不承担产物下载。smoke example 使用异步文件写入逐块落盘;未来接入 OSS 时应把同一数据流直接送入 OSS 分片上传,不经过完整内存缓冲。 TODO:等待上游 `tripo-rust-sdk` 提供原生 artifact stream API 后,删除 provider-side reqwest 下载器,改由 SDK stream 直接承接。