From 55aa97894354fb0c4e8fe1facc4580a7246d5d13 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 15 Jun 2025 18:55:26 -0400 Subject: [PATCH] hunyuan 3d 2.1 --- ...queue_hunyuan_3d_21_image_to_3d_webhook.rs | 65 +++++++++++++++++++ ...queue_hunyuan_3d_2_image_to_3d_webhook.rs} | 13 +--- .../src/requests/webhook/object/mod.rs | 3 +- .../generate_hunyuan2_image_to_3d_handler.rs | 8 +-- .../src/endpoints/fal_ai/hunyuan3d/mod.rs | 14 ++++ .../src/endpoints/fal_ai/hunyuan3d/v21/mod.rs | 64 ++++++++++++++++++ 6 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_21_image_to_3d_webhook.rs rename crates/lib/api_clients/fal_client/src/requests/webhook/object/{enqueue_hunyuan2_image_to_3d_webhook.rs => enqueue_hunyuan_3d_2_image_to_3d_webhook.rs} (75%) create mode 100644 crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/v21/mod.rs diff --git a/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_21_image_to_3d_webhook.rs b/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_21_image_to_3d_webhook.rs new file mode 100644 index 0000000000..b599151b90 --- /dev/null +++ b/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_21_image_to_3d_webhook.rs @@ -0,0 +1,65 @@ +use crate::creds::fal_api_key::FalApiKey; +use crate::error::classify_fal_error::classify_fal_error; +use crate::error::fal_error_plus::FalErrorPlus; +use fal::prelude::fal_ai::hunyuan3d::v21::{v21, Hunyuan3DInput}; +use fal::webhook::WebhookResponse; +use reqwest::IntoUrl; + +pub struct Hunyuan3d21Args<'a, U: IntoUrl, V: IntoUrl> { + pub image_url: U, + pub webhook_url: V, + pub api_key: &'a FalApiKey, +} + +pub async fn enqueue_hunyuan_3d_2_1_image_to_3d_webhook( + args: Hunyuan3d21Args<'_, U, V> +) -> Result { + + let image_url = args.image_url.as_str().to_string(); + + let request = Hunyuan3DInput { + input_image_url: image_url, + textured_mesh: Some(true), + // TODO: Maybe expose these later + guidance_scale: None, + num_inference_steps: None, + octree_resolution: None, + seed: None, + }; + + let result = v21(request) + .with_api_key(&args.api_key.0) + .queue_webhook(args.webhook_url) + .await; + + result.map_err(|err| classify_fal_error(err)) +} + +#[cfg(test)] +mod tests { + use crate::creds::fal_api_key::FalApiKey; + use crate::requests::webhook::object::enqueue_hunyuan_3d_21_image_to_3d_webhook::{enqueue_hunyuan_3d_2_1_image_to_3d_webhook, Hunyuan3d21Args}; + use errors::AnyhowResult; + use std::fs::read_to_string; + + #[tokio::test] + #[ignore] + async fn test_hunyuan3d_21() -> AnyhowResult<()> { + let image_url = "https://cdn-2.fakeyou.com/media/p/a/c/7/j/pac7jgp2tkehm7j7sm4sky1fpnkrnbve/image_pac7jgp2tkehm7j7sm4sky1fpnkrnbve.png"; + + // XXX: Don't commit secrets! + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + + let api_key = FalApiKey::from_str(&secret); + + let args = Hunyuan3d21Args { + image_url: image_url, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + + let result = enqueue_hunyuan_3d_2_1_image_to_3d_webhook(args).await?; + + Ok(()) + } +} diff --git a/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan2_image_to_3d_webhook.rs b/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_2_image_to_3d_webhook.rs similarity index 75% rename from crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan2_image_to_3d_webhook.rs rename to crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_2_image_to_3d_webhook.rs index 7a7cf296e5..57715bab88 100644 --- a/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan2_image_to_3d_webhook.rs +++ b/crates/lib/api_clients/fal_client/src/requests/webhook/object/enqueue_hunyuan_3d_2_image_to_3d_webhook.rs @@ -5,21 +5,14 @@ use fal::endpoints::fal_ai::hunyuan3d::v2::{v2, Hunyuan3DInput}; use fal::webhook::WebhookResponse; use reqwest::IntoUrl; -pub struct Hunyuan2Args<'a, U: IntoUrl, V: IntoUrl> { +pub struct Hunyuan3d2Args<'a, U: IntoUrl, V: IntoUrl> { pub image_url: U, pub webhook_url: V, pub api_key: &'a FalApiKey, } -#[derive(Copy, Clone, Debug)] -pub enum Kling16Duration { - Default, - FiveSeconds, - TenSeconds, -} - -pub async fn enqueue_hunyuan2_image_to_3d_webhook( - args: Hunyuan2Args<'_, U, V> +pub async fn enqueue_hunyuan_3d_2_image_to_3d_webhook( + args: Hunyuan3d2Args<'_, U, V> ) -> Result { let image_url = args.image_url.as_str().to_string(); diff --git a/crates/lib/api_clients/fal_client/src/requests/webhook/object/mod.rs b/crates/lib/api_clients/fal_client/src/requests/webhook/object/mod.rs index ca65e4d859..c818cc0721 100644 --- a/crates/lib/api_clients/fal_client/src/requests/webhook/object/mod.rs +++ b/crates/lib/api_clients/fal_client/src/requests/webhook/object/mod.rs @@ -1 +1,2 @@ -pub mod enqueue_hunyuan2_image_to_3d_webhook; \ No newline at end of file +pub mod enqueue_hunyuan_3d_2_image_to_3d_webhook; +pub mod enqueue_hunyuan_3d_21_image_to_3d_webhook; \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/generate_hunyuan2_image_to_3d_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/generate_hunyuan2_image_to_3d_handler.rs index cca36b1b8a..14a824ae92 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/generate_hunyuan2_image_to_3d_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/generate_hunyuan2_image_to_3d_handler.rs @@ -19,8 +19,8 @@ use artcraft_api_defs::generate::object::generate_hunyuan_2_image_to_3d::Generat use artcraft_api_defs::generate::object::generate_hunyuan_2_image_to_3d::GenerateHunyuan2ImageTo3dResponse; use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath; use enums::common::visibility::Visibility; -use fal_client::requests::webhook::object::enqueue_hunyuan2_image_to_3d_webhook::enqueue_hunyuan2_image_to_3d_webhook; -use fal_client::requests::webhook::object::enqueue_hunyuan2_image_to_3d_webhook::Hunyuan2Args; +use fal_client::requests::webhook::object::enqueue_hunyuan_3d_2_image_to_3d_webhook::enqueue_hunyuan_3d_2_image_to_3d_webhook; +use fal_client::requests::webhook::object::enqueue_hunyuan_3d_2_image_to_3d_webhook::Hunyuan3d2Args; use http_server_common::request::get_request_ip::get_request_ip; use http_server_common::response::serialize_as_json_error::serialize_as_json_error; use idempotency::uuid::generate_random_uuid; @@ -167,13 +167,13 @@ pub async fn generate_hunyuan_2_image_to_3d_handler( info!("Fal webhook URL: {}", server_state.fal.webhook_url); - let args = Hunyuan2Args { + let args = Hunyuan3d2Args { image_url: media_links.cdn_url, webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; - let fal_result = enqueue_hunyuan2_image_to_3d_webhook(args) + let fal_result = enqueue_hunyuan_3d_2_image_to_3d_webhook(args) .await .map_err(|err| { warn!("Error calling enqueue_hunyuan2_image_to_3d_webhook: {:?}", err); diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/mod.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/mod.rs index 8eadf1faa1..0d46dc31f0 100644 --- a/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/mod.rs +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/mod.rs @@ -12,3 +12,17 @@ ))) )] pub mod v2; +#[cfg(any( + feature = "endpoints", + feature = "endpoints_fal-ai", + feature = "endpoints_fal-ai_hunyuan3d" +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + feature = "endpoints", + feature = "endpoints_fal-ai", + feature = "endpoints_fal-ai_hunyuan3d" + ))) +)] +pub mod v21; diff --git a/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/v21/mod.rs b/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/v21/mod.rs new file mode 100644 index 0000000000..80e703521a --- /dev/null +++ b/crates/vendor/fal-rs/src/endpoints/fal_ai/hunyuan3d/v21/mod.rs @@ -0,0 +1,64 @@ +use crate::prelude::FalRequest; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Hunyuan3DInput { + /// Guidance scale for the model. + #[serde(skip_serializing_if = "Option::is_none")] + pub guidance_scale: Option, + /// URL of image to use while generating the 3D model. + /// "https://storage.googleapis.com/falserverless/model_tests/video_models/robot.png" + pub input_image_url: String, + /// Number of inference steps to perform. + #[serde(skip_serializing_if = "Option::is_none")] + pub num_inference_steps: Option, + /// Octree resolution for the model. + #[serde(skip_serializing_if = "Option::is_none")] + pub octree_resolution: Option, + /// The same seed and the same prompt given to the same version of the model + /// will output the same image every time. + #[serde(skip_serializing_if = "Option::is_none")] + pub seed: Option, + /// If set true, textured mesh will be generated and the price charged would be 3 times that of white mesh. + #[serde(skip_serializing_if = "Option::is_none")] + pub textured_mesh: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ObjectOutput { + /// Generated 3D object file. + /// {"content_type":"application/octet-stream","file_name":"white_mesh.glb","file_size":720696,"url":"https://v3.fal.media/files/lion/WqIhtKPaSoeBtC30qzIGG_white_mesh.glb"} + pub model_mesh: File, + /// Seed value used for generation. + pub seed: i64, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct File { + /// The mime type of the file. + /// "image/png" + #[serde(skip_serializing_if = "Option::is_none")] + pub content_type: Option, + /// File data + #[serde(skip_serializing_if = "Option::is_none")] + pub file_data: Option, + /// The name of the file. It will be auto-generated if not provided. + /// "z9RV14K95DvU.png" + #[serde(skip_serializing_if = "Option::is_none")] + pub file_name: Option, + /// The size of the file in bytes. + /// 4404019 + #[serde(skip_serializing_if = "Option::is_none")] + pub file_size: Option, + /// The URL where the file can be downloaded from. + pub url: String, +} + +/// Hunyuan3D +/// +/// Category: image-to-3d +/// Machine Type: H100 +/// License Type: commercial +pub fn v21(params: Hunyuan3DInput) -> FalRequest { + FalRequest::new("fal-ai/hunyuan3d-v21", params) +}