diff --git a/server-rs/Cargo.lock b/server-rs/Cargo.lock index 7b54fd8a3..e74fa84aa 100644 --- a/server-rs/Cargo.lock +++ b/server-rs/Cargo.lock @@ -4161,6 +4161,7 @@ version = "0.1.0" dependencies = [ "serde_json", "shared-contracts", + "tokio", "tripo3d-sdk", "url", ] diff --git a/server-rs/crates/platform-tripo/Cargo.toml b/server-rs/crates/platform-tripo/Cargo.toml index 471af15d8..5aa6ac0bd 100644 --- a/server-rs/crates/platform-tripo/Cargo.toml +++ b/server-rs/crates/platform-tripo/Cargo.toml @@ -9,3 +9,6 @@ shared-contracts = { workspace = true } serde_json = { workspace = true } tripo3d-sdk = { workspace = true } url = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs b/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs new file mode 100644 index 000000000..672eef477 --- /dev/null +++ b/server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs @@ -0,0 +1,145 @@ +//! Submit one request through each supported 3D generation endpoint. +//! +//! Configuration is intentionally environment-only: +//! +//! ```text +//! TRIPO_API_KEY=... TRIPO_BASE_URL=https://openapi.tripo3d.com/v3 \ +//! cargo run --locked -p platform-tripo --example tripo_generation_smoke \ +//! --manifest-path server-rs/Cargo.toml +//! ``` +//! +//! The example performs one-shot task queries after submission. It does not +//! expose or call the SDK's polling helpers. + +use std::{env, time::Duration}; + +use platform_tripo::TripoProviderClient; +use shared_contracts::model3d::{ + common::{Model3dModelVersion, Model3dTextureQuality}, + image_to_model::Model3dImageToModelRequest, + multiview_to_model::{Model3dMultiviewInputs, Model3dMultiviewToModelRequest}, + text_to_model::Model3dTextToModelRequest, +}; + +const SAMPLE_IMAGE_URL: &str = + "https://raw.githubusercontent.com/VAST-AI-Research/tripo-python-sdk/master/example.png"; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let api_key = required_env("TRIPO_API_KEY")?; + let base_url = required_env("TRIPO_BASE_URL")?; + let client = TripoProviderClient::new(platform_tripo::TripoSettings::new( + api_key, + base_url, + Duration::from_secs(60), + 2, + "genarrative-tripo-smoke/1".to_string(), + ))?; + + let text = client + .submit(&Model3dTextToModelRequest { + prompt: "a low-poly wooden treasure chest".to_string(), + model: Some(Model3dModelVersion::H31), + negative_prompt: None, + image_seed: None, + model_seed: None, + texture_seed: None, + texture: Some(true), + pbr: Some(true), + texture_quality: Some(Model3dTextureQuality::Standard), + texture_version: None, + delight: None, + geometry_quality: None, + face_limit: None, + auto_size: None, + quad: None, + smart_low_poly: None, + generate_parts: None, + compress: None, + export_uv: None, + export_orientation: None, + }) + .await?; + println!("text_to_model task_id={}", text.task_id); + + let image = client + .submit_image_to_model(&Model3dImageToModelRequest { + input: SAMPLE_IMAGE_URL.to_string(), + model: Some(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); + + let multiview = client + .submit_multiview_to_model(&Model3dMultiviewToModelRequest { + inputs: Model3dMultiviewInputs::Views { + front: SAMPLE_IMAGE_URL.to_string(), + left: Some(SAMPLE_IMAGE_URL.to_string()), + back: None, + right: None, + }, + model: Some(Model3dModelVersion::H31), + model_seed: None, + texture_seed: None, + texture: Some(true), + pbr: Some(true), + texture_quality: Some(Model3dTextureQuality::Standard), + texture_version: None, + delight: None, + geometry_quality: None, + texture_alignment: 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!("multiview_to_model task_id={}", multiview.task_id); + + for (name, handle) in [ + ("text_to_model", text), + ("image_to_model", image), + ("multiview_to_model", multiview), + ] { + let snapshot = client.get_task(&handle).await?; + println!( + "{name} status={:?} progress={:?}", + snapshot.status, snapshot.progress + ); + } + + Ok(()) +} + +fn required_env(name: &str) -> Result { + let value = + env::var(name).map_err(|_| format!("missing required environment variable {name}"))?; + if value.trim().is_empty() { + return Err(format!("environment variable {name} must not be blank")); + } + Ok(value) +}