From 7790a181c4ea6daf70e6129732ae83b1feebac84 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 12:39:20 +0800 Subject: [PATCH] =?UTF-8?q?provider=E6=8C=89=E5=8F=82=E6=95=B0=E5=88=86?= =?UTF-8?q?=E5=B1=82=E5=B9=B6=E5=85=AC=E5=BC=80=E7=BB=84=E5=90=88=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=85=A5=E5=8F=A3=20-=20submit=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E7=94=9F=E6=88=90=E5=8F=82=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=BE=93=E5=85=A5=E7=94=B1=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E6=96=B9=E8=A7=A3=E6=9E=90=E5=90=8E=E5=8D=95=E7=8B=AC=E4=BC=A0?= =?UTF-8?q?=E5=85=A5=20-=20=E5=85=AC=E5=BC=80=20validate=5Ftext=5Fto=5Fmod?= =?UTF-8?q?el=5Fparams=20/=20validate=5Fimage=5Fto=5Fmodel=5Fparams=20?= =?UTF-8?q?=E4=BE=9B=E5=B9=B3=E5=8F=B0=E5=B1=82=E5=9C=A8=E5=89=AF=E4=BD=9C?= =?UTF-8?q?=E7=94=A8=E5=89=8D=E9=A2=84=E6=A3=80=20-=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=88=B0=20provider=20=E6=A0=A1=E9=AA=8C=E9=80=89=E9=A1=B9?= =?UTF-8?q?=E7=9A=84=E6=98=A0=E5=B0=84=E6=94=B6=E6=95=9B=E4=B8=BA=20From?= =?UTF-8?q?=20=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=8E=BB=E6=8E=89=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E6=8B=BC=E8=A3=85=20-=20smoke=20=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E8=B7=9F=E9=9A=8F=E6=96=B0=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../examples/tripo_generation_smoke.rs | 54 +++++++------- .../platform-tripo/src/common/validation.rs | 40 ++++++++++ .../src/image_to_model/client.rs | 74 +++++++++---------- .../platform-tripo/src/image_to_model/mod.rs | 3 + .../src/image_to_model/validation.rs | 11 +++ server-rs/crates/platform-tripo/src/lib.rs | 2 + .../src/text_to_model/client.rs | 50 ++++++------- .../platform-tripo/src/text_to_model/mod.rs | 2 + .../src/text_to_model/validation.rs | 34 +++------ 9 files changed, 153 insertions(+), 117 deletions(-) create mode 100644 server-rs/crates/platform-tripo/src/image_to_model/validation.rs diff --git a/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs b/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs index d9127b85a..cf5747e19 100644 --- a/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs +++ b/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs @@ -17,9 +17,9 @@ use std::{env, time::Duration}; use platform_tripo::TripoProviderClient; use shared_contracts::model3d::{ common::{Model3dModelVersion, Model3dTaskStatus, Model3dTextureQuality}, - image_to_model::Model3dImageToModelRequest, + image_to_model::Model3dImageToModelParams, multiview_to_model::{Model3dMultiviewInputs, Model3dMultiviewToModelRequest}, - text_to_model::Model3dTextToModelRequest, + text_to_model::Model3dTextToModelParams, }; use tokio::{fs::File, io::AsyncWriteExt}; use tripo3d_sdk::{ClientOptions, TripoClient, WaitOptions}; @@ -46,7 +46,7 @@ async fn main() -> Result<(), Box> { ))?; let text = client - .submit(&Model3dTextToModelRequest { + .submit(&Model3dTextToModelParams { prompt: "a low-poly wooden treasure chest".to_string(), model: Model3dModelVersion::H31, negative_prompt: None, @@ -72,29 +72,31 @@ async fn main() -> Result<(), Box> { println!("text_to_model task_id={}", text.task_id); let image = client - .submit_image_to_model(&Model3dImageToModelRequest { - input: SAMPLE_IMAGE_URL.to_string(), - model: Model3dModelVersion::H31, - enable_image_autofix: None, - model_seed: None, - texture_seed: None, - texture: Some(true), - pbr: Some(true), - texture_quality: Some(Model3dTextureQuality::Standard), - texture_version: None, - delight: None, - texture_alignment: None, - geometry_quality: None, - face_limit: None, - auto_size: None, - orientation: None, - quad: None, - smart_low_poly: None, - generate_parts: None, - compress: None, - export_uv: None, - export_orientation: None, - }) + .submit_image_to_model( + SAMPLE_IMAGE_URL, + &Model3dImageToModelParams { + model: Model3dModelVersion::H31, + enable_image_autofix: None, + model_seed: None, + texture_seed: None, + texture: Some(true), + pbr: Some(true), + texture_quality: Some(Model3dTextureQuality::Standard), + texture_version: None, + delight: None, + texture_alignment: None, + geometry_quality: None, + face_limit: None, + auto_size: None, + orientation: None, + quad: None, + smart_low_poly: None, + generate_parts: None, + compress: None, + export_uv: None, + export_orientation: None, + }, + ) .await?; println!("image_to_model task_id={}", image.task_id); diff --git a/server-rs/crates/platform-tripo/src/common/validation.rs b/server-rs/crates/platform-tripo/src/common/validation.rs index 8e0282643..00dfaa4f6 100644 --- a/server-rs/crates/platform-tripo/src/common/validation.rs +++ b/server-rs/crates/platform-tripo/src/common/validation.rs @@ -2,6 +2,8 @@ use shared_contracts::model3d::common::{ Model3dCompression, Model3dGeometryQuality, Model3dModelVersion, Model3dTextureQuality, Model3dTextureVersion, }; +use shared_contracts::model3d::image_to_model::Model3dImageToModelParams; +use shared_contracts::model3d::text_to_model::Model3dTextToModelParams; use super::{TripoError, TripoField, TripoValidationReason}; @@ -20,6 +22,44 @@ pub(crate) struct TripoGenerationOptions { pub compress: Option, } +impl From<&Model3dTextToModelParams> for TripoGenerationOptions { + fn from(params: &Model3dTextToModelParams) -> Self { + Self { + model: params.model, + texture: params.texture, + pbr: params.pbr, + texture_quality: params.texture_quality, + texture_version: params.texture_version, + geometry_quality: params.geometry_quality, + face_limit: params.face_limit, + auto_size: params.auto_size, + quad: params.quad, + smart_low_poly: params.smart_low_poly, + generate_parts: params.generate_parts, + compress: params.compress, + } + } +} + +impl From<&Model3dImageToModelParams> for TripoGenerationOptions { + fn from(params: &Model3dImageToModelParams) -> Self { + Self { + model: params.model, + texture: params.texture, + pbr: params.pbr, + texture_quality: params.texture_quality, + texture_version: params.texture_version, + geometry_quality: params.geometry_quality, + face_limit: params.face_limit, + auto_size: params.auto_size, + quad: params.quad, + smart_low_poly: params.smart_low_poly, + generate_parts: params.generate_parts, + compress: params.compress, + } + } +} + pub(crate) fn validate_generation_options( options: &TripoGenerationOptions, ) -> Result<(), TripoError> { 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 8ed928534..25b773fa2 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 @@ -2,87 +2,79 @@ use shared_contracts::model3d::common::{ Model3dCompression, Model3dExportOrientation, Model3dGeometryQuality, Model3dInputOrientation, Model3dTextureAlignment, Model3dTextureQuality, Model3dTextureVersion, }; -use shared_contracts::model3d::image_to_model::Model3dImageToModelRequest; +use shared_contracts::model3d::image_to_model::Model3dImageToModelParams; use tripo3d_sdk::{models::FileInput, params::ImageToModelParams}; use crate::common::{ - TripoError, TripoField, TripoGenerationOptions, TripoProviderClient, TripoTaskHandle, - TripoValidationReason, validate_generation_options, + TripoError, TripoField, TripoProviderClient, TripoTaskHandle, TripoValidationReason, }; +use super::validation::validate_image_to_model_params; + impl TripoProviderClient { + /// 图片输入由调用方解析后传入:provider 只负责把它交给 SDK, + /// 站内资源 / 素材的归属校验属于平台层。 pub async fn submit_image_to_model( &self, - request: &Model3dImageToModelRequest, + input: &str, + params: &Model3dImageToModelParams, ) -> Result { - if request.input.trim().is_empty() { + let input = input.trim(); + if input.is_empty() { return Err(TripoError::InvalidParameters { field: Some(TripoField::Input), reason: TripoValidationReason::Required, message: "input must not be blank".into(), }); } - validate_generation_options(&TripoGenerationOptions { - model: request.model, - texture: request.texture, - pbr: request.pbr, - texture_quality: request.texture_quality, - texture_version: request.texture_version, - geometry_quality: request.geometry_quality, - face_limit: request.face_limit, - auto_size: request.auto_size, - quad: request.quad, - smart_low_poly: request.smart_low_poly, - generate_parts: request.generate_parts, - compress: request.compress, - })?; + validate_image_to_model_params(params)?; let task_id = self .client - .image_to_model(to_sdk_params(request)) + .image_to_model(to_sdk_params(input, params)) .await .map_err(TripoError::from)?; Ok(TripoTaskHandle { task_id }) } } -fn to_sdk_params(request: &Model3dImageToModelRequest) -> ImageToModelParams { +fn to_sdk_params(input: &str, params: &Model3dImageToModelParams) -> ImageToModelParams { ImageToModelParams { - input: FileInput::from(request.input.trim()), - model: Some(request.model.as_str().to_owned()), - enable_image_autofix: request.enable_image_autofix, - model_seed: request.model_seed, - texture_seed: request.texture_seed, - texture: request.texture, - pbr: request.pbr, - texture_quality: request + input: FileInput::from(input), + model: Some(params.model.as_str().to_owned()), + enable_image_autofix: params.enable_image_autofix, + model_seed: params.model_seed, + texture_seed: params.texture_seed, + texture: params.texture, + pbr: params.pbr, + texture_quality: params .texture_quality .map(Model3dTextureQuality::as_str) .map(str::to_owned), - extra: extra_fields(request.texture_version, request.delight), - texture_alignment: request + extra: extra_fields(params.texture_version, params.delight), + texture_alignment: params .texture_alignment .map(Model3dTextureAlignment::as_str) .map(str::to_owned), - geometry_quality: request + geometry_quality: params .geometry_quality .map(Model3dGeometryQuality::as_str) .map(str::to_owned), - face_limit: request.face_limit, - auto_size: request.auto_size, - orientation: request + face_limit: params.face_limit, + auto_size: params.auto_size, + orientation: params .orientation .map(Model3dInputOrientation::as_str) .map(str::to_owned), - quad: request.quad, - smart_low_poly: request.smart_low_poly, - generate_parts: request.generate_parts, - compress: request + quad: params.quad, + smart_low_poly: params.smart_low_poly, + generate_parts: params.generate_parts, + compress: params .compress .map(Model3dCompression::as_str) .map(str::to_owned), - export_uv: request.export_uv, - export_orientation: request + export_uv: params.export_uv, + export_orientation: params .export_orientation .map(Model3dExportOrientation::as_str) .map(str::to_owned), diff --git a/server-rs/crates/platform-tripo/src/image_to_model/mod.rs b/server-rs/crates/platform-tripo/src/image_to_model/mod.rs index 61ca8d331..e07463772 100644 --- a/server-rs/crates/platform-tripo/src/image_to_model/mod.rs +++ b/server-rs/crates/platform-tripo/src/image_to_model/mod.rs @@ -1,2 +1,5 @@ mod client; pub mod result; +mod validation; + +pub use validation::validate_image_to_model_params; diff --git a/server-rs/crates/platform-tripo/src/image_to_model/validation.rs b/server-rs/crates/platform-tripo/src/image_to_model/validation.rs new file mode 100644 index 000000000..e3ad4d223 --- /dev/null +++ b/server-rs/crates/platform-tripo/src/image_to_model/validation.rs @@ -0,0 +1,11 @@ +use shared_contracts::model3d::image_to_model::Model3dImageToModelParams; + +use crate::common::{TripoError, validate_generation_options}; + +/// image-to-model 的请求预检:只覆盖生成参数与模型能力组合, +/// 图片本体是否可解析由调用方保证(provider 在 submit 时仍会拒绝空输入)。 +pub fn validate_image_to_model_params( + params: &Model3dImageToModelParams, +) -> Result<(), TripoError> { + validate_generation_options(¶ms.into()) +} diff --git a/server-rs/crates/platform-tripo/src/lib.rs b/server-rs/crates/platform-tripo/src/lib.rs index 1bf20cfea..d8284a759 100644 --- a/server-rs/crates/platform-tripo/src/lib.rs +++ b/server-rs/crates/platform-tripo/src/lib.rs @@ -13,3 +13,5 @@ pub use common::{ TripoTaskFailure, TripoTaskHandle, TripoTaskOutput, TripoTaskSnapshot, TripoTaskType, TripoUrl, TripoValidationReason, }; +pub use image_to_model::validate_image_to_model_params; +pub use text_to_model::validate_text_to_model_params; diff --git a/server-rs/crates/platform-tripo/src/text_to_model/client.rs b/server-rs/crates/platform-tripo/src/text_to_model/client.rs index fa0dbc66f..e253faa22 100644 --- a/server-rs/crates/platform-tripo/src/text_to_model/client.rs +++ b/server-rs/crates/platform-tripo/src/text_to_model/client.rs @@ -3,55 +3,55 @@ use shared_contracts::model3d::common::{ Model3dCompression, Model3dExportOrientation, Model3dGeometryQuality, Model3dTextureQuality, Model3dTextureVersion, }; -use shared_contracts::model3d::text_to_model::Model3dTextToModelRequest; +use shared_contracts::model3d::text_to_model::Model3dTextToModelParams; -use super::validation::validate_text_to_model_request; +use super::validation::validate_text_to_model_params; impl TripoProviderClient { pub async fn submit( &self, - request: &Model3dTextToModelRequest, + params: &Model3dTextToModelParams, ) -> Result { - validate_text_to_model_request(request)?; + validate_text_to_model_params(params)?; let task_id = self .client - .text_to_model(to_sdk_params(request)) + .text_to_model(to_sdk_params(params)) .await .map_err(TripoError::from)?; Ok(TripoTaskHandle { task_id }) } } -fn to_sdk_params(request: &Model3dTextToModelRequest) -> tripo3d_sdk::params::TextToModelParams { +fn to_sdk_params(params: &Model3dTextToModelParams) -> tripo3d_sdk::params::TextToModelParams { tripo3d_sdk::params::TextToModelParams { - prompt: request.prompt.clone(), - model: Some(request.model.as_str().to_owned()), - negative_prompt: request.negative_prompt.clone(), - image_seed: request.image_seed, - model_seed: request.model_seed, - texture_seed: request.texture_seed, - texture: request.texture, - pbr: request.pbr, - texture_quality: request + prompt: params.prompt.clone(), + model: Some(params.model.as_str().to_owned()), + negative_prompt: params.negative_prompt.clone(), + image_seed: params.image_seed, + model_seed: params.model_seed, + texture_seed: params.texture_seed, + texture: params.texture, + pbr: params.pbr, + texture_quality: params .texture_quality .map(Model3dTextureQuality::as_str) .map(str::to_owned), - extra: extra_fields(request.texture_version, request.delight), - geometry_quality: request + extra: extra_fields(params.texture_version, params.delight), + geometry_quality: params .geometry_quality .map(Model3dGeometryQuality::as_str) .map(str::to_owned), - face_limit: request.face_limit, - auto_size: request.auto_size, - quad: request.quad, - smart_low_poly: request.smart_low_poly, - generate_parts: request.generate_parts, - compress: request + face_limit: params.face_limit, + auto_size: params.auto_size, + quad: params.quad, + smart_low_poly: params.smart_low_poly, + generate_parts: params.generate_parts, + compress: params .compress .map(Model3dCompression::as_str) .map(str::to_owned), - export_uv: request.export_uv, - export_orientation: request + export_uv: params.export_uv, + export_orientation: params .export_orientation .map(Model3dExportOrientation::as_str) .map(str::to_owned), diff --git a/server-rs/crates/platform-tripo/src/text_to_model/mod.rs b/server-rs/crates/platform-tripo/src/text_to_model/mod.rs index 8899f51c1..43e3ccce4 100644 --- a/server-rs/crates/platform-tripo/src/text_to_model/mod.rs +++ b/server-rs/crates/platform-tripo/src/text_to_model/mod.rs @@ -1,3 +1,5 @@ mod client; pub mod result; mod validation; + +pub use validation::validate_text_to_model_params; diff --git a/server-rs/crates/platform-tripo/src/text_to_model/validation.rs b/server-rs/crates/platform-tripo/src/text_to_model/validation.rs index 96b74b70c..472379133 100644 --- a/server-rs/crates/platform-tripo/src/text_to_model/validation.rs +++ b/server-rs/crates/platform-tripo/src/text_to_model/validation.rs @@ -1,14 +1,11 @@ -use shared_contracts::model3d::text_to_model::Model3dTextToModelRequest; +use shared_contracts::model3d::text_to_model::Model3dTextToModelParams; -use crate::common::{ - TripoError, TripoField, TripoGenerationOptions, TripoValidationReason, - validate_generation_options, -}; +use crate::common::{TripoError, TripoField, TripoValidationReason, validate_generation_options}; -pub(crate) fn validate_text_to_model_request( - request: &Model3dTextToModelRequest, -) -> Result<(), TripoError> { - if request.prompt.trim().is_empty() { +/// text-to-model 的请求预检:调用方在产生 provider 副作用之前调用, +/// 用于把参数错误映射成调用方的入参错误,而不是等 submit 失败。 +pub fn validate_text_to_model_params(params: &Model3dTextToModelParams) -> Result<(), TripoError> { + if params.prompt.trim().is_empty() { return Err(TripoError::InvalidParameters { field: Some(TripoField::Prompt), reason: TripoValidationReason::Required, @@ -16,14 +13,14 @@ pub(crate) fn validate_text_to_model_request( }); } - if request.prompt.chars().count() > 1024 { + if params.prompt.chars().count() > 1024 { return Err(TripoError::InvalidParameters { field: Some(TripoField::Prompt), reason: TripoValidationReason::InvalidCombination, message: "prompt must not exceed 1024 characters".into(), }); } - if request + if params .negative_prompt .as_deref() .is_some_and(|value| value.chars().count() > 255) @@ -35,20 +32,7 @@ pub(crate) fn validate_text_to_model_request( }); } - validate_generation_options(&TripoGenerationOptions { - model: request.model, - texture: request.texture, - pbr: request.pbr, - texture_quality: request.texture_quality, - texture_version: request.texture_version, - geometry_quality: request.geometry_quality, - face_limit: request.face_limit, - auto_size: request.auto_size, - quad: request.quad, - smart_low_poly: request.smart_low_poly, - generate_parts: request.generate_parts, - compress: request.compress, - })?; + validate_generation_options(¶ms.into())?; Ok(()) }