Tauri can now generate Marble natively (#1122)

This commit is contained in:
Brandon Thomas authored and GitHub committed 2026-03-09 14:04:28 -04:00
1 parent 15d286135a
commit 52fdf237ad
36 files changed
+656 -18

No files matched your search

Generated
+1
View File
@@ -797,6 +797,7 @@ dependencies = [
"tokio",
"uuid 1.18.1",
"workspace-build-acceleration",
"world_labs_api",
]
[[package]]
@@ -17,10 +17,11 @@ workspace-build-acceleration.workspace = true
# Internal
artcraft_api_defs.workspace = true
artcraft_client.workspace = true
fal_client.workspace = true
seedance2pro.workspace = true
artcraft_client.workspace = true
tokens.workspace = true
world_labs_api.workspace = true
# External
anyhow.workspace = true
@@ -0,0 +1,13 @@
use serde_derive::{Deserialize, Serialize};
/// Common splat models supported by the router.
/// Not all models are available through all providers.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CommonSplatModel {
#[serde(rename = "marble_0p1_mini")]
Marble0p1Mini,
#[serde(rename = "marble_0p1_plus")]
Marble0p1Plus,
}
@@ -1,6 +1,7 @@
pub mod common_aspect_ratio;
pub mod common_image_model;
pub mod common_resolution;
pub mod common_splat_model;
pub mod common_video_model;
pub mod image_list_ref;
pub mod image_ref;
@@ -0,0 +1,76 @@
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_mini::PlanArtcraftMarble0p1Mini;
use crate::generate::generate_splat::splat_generation_cost_estimate::SplatGenerationCostEstimate;
use world_labs_api::api::api_types::world_labs_model::WorldLabsModel;
use world_labs_api::pricing::check_pricing::{calculate_cost, InputType};
pub(crate) fn estimate_splat_cost_artcraft_marble_0p1_mini(
plan: &PlanArtcraftMarble0p1Mini<'_>,
) -> SplatGenerationCostEstimate {
let input_type = if plan.reference_image.is_some() {
InputType::ImageNonPanorama
} else {
InputType::Text
};
let cost = calculate_cost(WorldLabsModel::Marble0p1Mini, input_type);
let cost_in_usd_cents = cost.us_dollar_cents as u64;
SplatGenerationCostEstimate {
cost_in_credits: Some(cost_in_usd_cents),
cost_in_usd_cents: Some(cost_in_usd_cents),
is_free: false,
is_unlimited: false,
is_rate_limited: false,
has_watermark: false,
}
}
#[cfg(test)]
mod tests {
use crate::api::common_splat_model::CommonSplatModel;
use crate::api::provider::Provider;
use crate::generate::generate_splat::generate_splat_request::GenerateSplatRequest;
use tokens::tokens::media_files::MediaFileToken;
fn estimate_usd_cents(prompt: Option<&str>, image_tokens: Option<&Vec<MediaFileToken>>) -> u64 {
let request = GenerateSplatRequest {
model: CommonSplatModel::Marble0p1Mini,
provider: Provider::Artcraft,
prompt,
reference_images: image_tokens.map(crate::api::image_list_ref::ImageListRef::MediaFileTokens),
idempotency_token: None,
};
request.build()
.expect("build should succeed")
.estimate_costs()
.cost_in_usd_cents
.expect("cost_in_usd_cents should be present")
}
#[test]
fn test_estimate_cost_text_only() {
let cost = estimate_usd_cents(Some("a room"), None);
assert_eq!(cost, 18); // 230 credits → 18 cents
}
#[test]
fn test_estimate_cost_no_image_no_prompt() {
let cost = estimate_usd_cents(None, None);
assert_eq!(cost, 18);
}
#[test]
fn test_estimate_cost_image() {
let tokens = vec![MediaFileToken("test_token".to_string())];
let cost = estimate_usd_cents(None, Some(&tokens));
assert_eq!(cost, 18); // 230 credits → 18 cents (same as text for mini)
}
#[test]
fn test_estimate_cost_image_with_prompt() {
let tokens = vec![MediaFileToken("test_token".to_string())];
let cost = estimate_usd_cents(Some("a cozy room"), Some(&tokens));
assert_eq!(cost, 18); // 230 credits → 18 cents
}
}
@@ -0,0 +1,76 @@
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_plus::PlanArtcraftMarble0p1Plus;
use crate::generate::generate_splat::splat_generation_cost_estimate::SplatGenerationCostEstimate;
use world_labs_api::api::api_types::world_labs_model::WorldLabsModel;
use world_labs_api::pricing::check_pricing::{calculate_cost, InputType};
pub(crate) fn estimate_splat_cost_artcraft_marble_0p1_plus(
plan: &PlanArtcraftMarble0p1Plus<'_>,
) -> SplatGenerationCostEstimate {
let input_type = if plan.reference_image.is_some() {
InputType::ImageNonPanorama
} else {
InputType::Text
};
let cost = calculate_cost(WorldLabsModel::Marble0p1Plus, input_type);
let cost_in_usd_cents = cost.us_dollar_cents as u64;
SplatGenerationCostEstimate {
cost_in_credits: Some(cost_in_usd_cents),
cost_in_usd_cents: Some(cost_in_usd_cents),
is_free: false,
is_unlimited: false,
is_rate_limited: false,
has_watermark: false,
}
}
#[cfg(test)]
mod tests {
use crate::api::common_splat_model::CommonSplatModel;
use crate::api::provider::Provider;
use crate::generate::generate_splat::generate_splat_request::GenerateSplatRequest;
use tokens::tokens::media_files::MediaFileToken;
fn estimate_usd_cents(prompt: Option<&str>, image_tokens: Option<&Vec<MediaFileToken>>) -> u64 {
let request = GenerateSplatRequest {
model: CommonSplatModel::Marble0p1Plus,
provider: Provider::Artcraft,
prompt,
reference_images: image_tokens.map(crate::api::image_list_ref::ImageListRef::MediaFileTokens),
idempotency_token: None,
};
request.build()
.expect("build should succeed")
.estimate_costs()
.cost_in_usd_cents
.expect("cost_in_usd_cents should be present")
}
#[test]
fn test_estimate_cost_text_only() {
let cost = estimate_usd_cents(Some("a room"), None);
assert_eq!(cost, 126); // 1580 credits → 126 cents
}
#[test]
fn test_estimate_cost_no_image_no_prompt() {
let cost = estimate_usd_cents(None, None);
assert_eq!(cost, 126);
}
#[test]
fn test_estimate_cost_image() {
let tokens = vec![MediaFileToken("test_token".to_string())];
let cost = estimate_usd_cents(None, Some(&tokens));
assert_eq!(cost, 126); // 1580 credits → 126 cents (same as text for plus)
}
#[test]
fn test_estimate_cost_image_with_prompt() {
let tokens = vec![MediaFileToken("test_token".to_string())];
let cost = estimate_usd_cents(Some("a cozy room"), Some(&tokens));
assert_eq!(cost, 126); // 1580 credits → 126 cents
}
}
@@ -0,0 +1,2 @@
pub mod estimate_splat_cost_artcraft_marble_0p1_mini;
pub mod estimate_splat_cost_artcraft_marble_0p1_plus;
@@ -0,0 +1 @@
pub(crate) mod artcraft;
@@ -0,0 +1,32 @@
use crate::client::router_artcraft_client::RouterArtcraftClient;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::errors::provider_error::ProviderError;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_mini::PlanArtcraftMarble0p1Mini;
use crate::generate::generate_splat::generate_splat_response::{
ArtcraftSplatResponsePayload, GenerateSplatResponse,
};
use artcraft_api_defs::generate::splat::generate_worldlabs_marble_0p1_mini_splat::GenerateWorldlabsMarble0p1MiniSplatRequest;
use artcraft_client::endpoints::generate::splat::generate_worldlabs_marble_0p1_mini_splat::generate_worldlabs_marble_0p1_mini_splat;
pub async fn execute_artcraft_marble_0p1_mini(
plan: &PlanArtcraftMarble0p1Mini<'_>,
artcraft_client: &RouterArtcraftClient,
) -> Result<GenerateSplatResponse, ArtcraftRouterError> {
let request = GenerateWorldlabsMarble0p1MiniSplatRequest {
uuid_idempotency_token: plan.idempotency_token.clone(),
image_media_file_token: plan.reference_image.cloned(),
prompt: plan.prompt.map(|p| p.to_string()),
};
let response = generate_worldlabs_marble_0p1_mini_splat(
&artcraft_client.api_host,
Some(&artcraft_client.credentials),
request,
)
.await
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
Ok(GenerateSplatResponse::Artcraft(ArtcraftSplatResponsePayload {
inference_job_token: response.inference_job_token,
}))
}
@@ -0,0 +1,32 @@
use crate::client::router_artcraft_client::RouterArtcraftClient;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::errors::provider_error::ProviderError;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_plus::PlanArtcraftMarble0p1Plus;
use crate::generate::generate_splat::generate_splat_response::{
ArtcraftSplatResponsePayload, GenerateSplatResponse,
};
use artcraft_api_defs::generate::splat::generate_worldlabs_marble_0p1_plus_splat::GenerateWorldlabsMarble0p1PlusSplatRequest;
use artcraft_client::endpoints::generate::splat::generate_worldlabs_marble_0p1_plus_splat::generate_worldlabs_marble_0p1_plus_splat;
pub async fn execute_artcraft_marble_0p1_plus(
plan: &PlanArtcraftMarble0p1Plus<'_>,
artcraft_client: &RouterArtcraftClient,
) -> Result<GenerateSplatResponse, ArtcraftRouterError> {
let request = GenerateWorldlabsMarble0p1PlusSplatRequest {
uuid_idempotency_token: plan.idempotency_token.clone(),
image_media_file_token: plan.reference_image.cloned(),
prompt: plan.prompt.map(|p| p.to_string()),
};
let response = generate_worldlabs_marble_0p1_plus_splat(
&artcraft_client.api_host,
Some(&artcraft_client.credentials),
request,
)
.await
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
Ok(GenerateSplatResponse::Artcraft(ArtcraftSplatResponsePayload {
inference_job_token: response.inference_job_token,
}))
}
@@ -0,0 +1,2 @@
pub mod generate_splat_artcraft_marble_0p1_mini;
pub mod generate_splat_artcraft_marble_0p1_plus;
@@ -0,0 +1 @@
pub mod artcraft;
@@ -0,0 +1,49 @@
use crate::api::common_splat_model::CommonSplatModel;
use crate::api::image_list_ref::ImageListRef;
use crate::api::provider::Provider;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_mini::plan_generate_splat_artcraft_marble_0p1_mini;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_plus::plan_generate_splat_artcraft_marble_0p1_plus;
use crate::generate::generate_splat::splat_generation_plan::SplatGenerationPlan;
pub struct GenerateSplatRequest<'a> {
/// Which model to use.
pub model: CommonSplatModel,
/// Which provider to use.
pub provider: Provider,
/// The prompt for splat generation (optional).
pub prompt: Option<&'a str>,
/// Reference images (optional).
pub reference_images: Option<ImageListRef<'a>>,
/// Some providers support idempotency.
/// If not supplied, we'll generate one for the required providers.
pub idempotency_token: Option<&'a str>,
}
impl<'a> GenerateSplatRequest<'a> {
/// Read the splat generation request, construct a plan, then yield a means to execute it.
pub fn build(&self) -> Result<SplatGenerationPlan<'_>, ArtcraftRouterError> {
match self.provider {
Provider::Artcraft => match self.model {
CommonSplatModel::Marble0p1Mini => {
plan_generate_splat_artcraft_marble_0p1_mini(self).map(SplatGenerationPlan::ArtcraftMarble0p1Mini)
}
CommonSplatModel::Marble0p1Plus => {
plan_generate_splat_artcraft_marble_0p1_plus(self).map(SplatGenerationPlan::ArtcraftMarble0p1Plus)
}
},
Provider::Fal => Err(ArtcraftRouterError::UnsupportedModel(
format!("Splat generation via Fal is not yet supported (model: {:?})", self.model)
)),
}
}
pub fn get_or_generate_idempotency_token(&self) -> String {
self.idempotency_token.map(|t| t.to_string())
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string())
}
}
@@ -0,0 +1,19 @@
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
#[derive(Clone, Debug)]
pub struct ArtcraftSplatResponsePayload {
pub inference_job_token: InferenceJobToken,
}
#[derive(Clone, Debug)]
pub enum GenerateSplatResponse {
Artcraft(ArtcraftSplatResponsePayload),
}
impl GenerateSplatResponse {
pub fn get_artcraft_payload(&self) -> Option<ArtcraftSplatResponsePayload> {
match self {
Self::Artcraft(p) => Some(p.clone()),
}
}
}
@@ -0,0 +1,7 @@
pub mod cost;
pub mod execute;
pub mod generate_splat_request;
pub mod generate_splat_response;
pub mod plan;
pub mod splat_generation_cost_estimate;
pub mod splat_generation_plan;
@@ -0,0 +1,2 @@
pub mod plan_generate_splat_artcraft_marble_0p1_mini;
pub mod plan_generate_splat_artcraft_marble_0p1_plus;
@@ -0,0 +1,38 @@
use crate::api::image_list_ref::ImageListRef;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::errors::client_error::ClientError;
use crate::generate::generate_splat::generate_splat_request::GenerateSplatRequest;
use tokens::tokens::media_files::MediaFileToken;
#[derive(Debug, Clone)]
pub struct PlanArtcraftMarble0p1Mini<'a> {
pub prompt: Option<&'a str>,
pub reference_image: Option<&'a MediaFileToken>,
pub idempotency_token: String,
}
pub fn plan_generate_splat_artcraft_marble_0p1_mini<'a>(
request: &'a GenerateSplatRequest<'a>,
) -> Result<PlanArtcraftMarble0p1Mini<'a>, ArtcraftRouterError> {
let reference_image = resolve_single_image_ref(request.reference_images)?;
Ok(PlanArtcraftMarble0p1Mini {
prompt: request.prompt,
reference_image,
idempotency_token: request.get_or_generate_idempotency_token(),
})
}
fn resolve_single_image_ref<'a>(
image_list_ref: Option<ImageListRef<'a>>,
) -> Result<Option<&'a MediaFileToken>, ArtcraftRouterError> {
match image_list_ref {
None => Ok(None),
Some(ImageListRef::MediaFileTokens(tokens)) => {
Ok(tokens.first())
}
Some(ImageListRef::Urls(_)) => {
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
}
}
}
@@ -0,0 +1,38 @@
use crate::api::image_list_ref::ImageListRef;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::errors::client_error::ClientError;
use crate::generate::generate_splat::generate_splat_request::GenerateSplatRequest;
use tokens::tokens::media_files::MediaFileToken;
#[derive(Debug, Clone)]
pub struct PlanArtcraftMarble0p1Plus<'a> {
pub prompt: Option<&'a str>,
pub reference_image: Option<&'a MediaFileToken>,
pub idempotency_token: String,
}
pub fn plan_generate_splat_artcraft_marble_0p1_plus<'a>(
request: &'a GenerateSplatRequest<'a>,
) -> Result<PlanArtcraftMarble0p1Plus<'a>, ArtcraftRouterError> {
let reference_image = resolve_single_image_ref(request.reference_images)?;
Ok(PlanArtcraftMarble0p1Plus {
prompt: request.prompt,
reference_image,
idempotency_token: request.get_or_generate_idempotency_token(),
})
}
fn resolve_single_image_ref<'a>(
image_list_ref: Option<ImageListRef<'a>>,
) -> Result<Option<&'a MediaFileToken>, ArtcraftRouterError> {
match image_list_ref {
None => Ok(None),
Some(ImageListRef::MediaFileTokens(tokens)) => {
Ok(tokens.first())
}
Some(ImageListRef::Urls(_)) => {
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
}
}
}
@@ -0,0 +1 @@
pub mod artcraft;
@@ -0,0 +1,9 @@
/// The result of estimating the cost of a splat generation plan.
pub struct SplatGenerationCostEstimate {
pub cost_in_credits: Option<u64>,
pub cost_in_usd_cents: Option<u64>,
pub is_free: bool,
pub is_unlimited: bool,
pub is_rate_limited: bool,
pub has_watermark: bool,
}
@@ -0,0 +1,45 @@
use crate::client::router_client::RouterClient;
use crate::errors::artcraft_router_error::ArtcraftRouterError;
use crate::generate::generate_splat::cost::artcraft::estimate_splat_cost_artcraft_marble_0p1_mini::estimate_splat_cost_artcraft_marble_0p1_mini;
use crate::generate::generate_splat::cost::artcraft::estimate_splat_cost_artcraft_marble_0p1_plus::estimate_splat_cost_artcraft_marble_0p1_plus;
use crate::generate::generate_splat::execute::artcraft::generate_splat_artcraft_marble_0p1_mini::execute_artcraft_marble_0p1_mini;
use crate::generate::generate_splat::execute::artcraft::generate_splat_artcraft_marble_0p1_plus::execute_artcraft_marble_0p1_plus;
use crate::generate::generate_splat::generate_splat_response::GenerateSplatResponse;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_mini::PlanArtcraftMarble0p1Mini;
use crate::generate::generate_splat::plan::artcraft::plan_generate_splat_artcraft_marble_0p1_plus::PlanArtcraftMarble0p1Plus;
use crate::generate::generate_splat::splat_generation_cost_estimate::SplatGenerationCostEstimate;
#[derive(Debug)]
pub enum SplatGenerationPlan<'a> {
ArtcraftMarble0p1Mini(PlanArtcraftMarble0p1Mini<'a>),
ArtcraftMarble0p1Plus(PlanArtcraftMarble0p1Plus<'a>),
}
impl<'a> SplatGenerationPlan<'a> {
pub async fn generate_splat(
&self,
client: &RouterClient,
) -> Result<GenerateSplatResponse, ArtcraftRouterError> {
match self {
SplatGenerationPlan::ArtcraftMarble0p1Mini(plan) => {
let artcraft_client = client.get_artcraft_client_ref()?;
execute_artcraft_marble_0p1_mini(plan, artcraft_client).await
}
SplatGenerationPlan::ArtcraftMarble0p1Plus(plan) => {
let artcraft_client = client.get_artcraft_client_ref()?;
execute_artcraft_marble_0p1_plus(plan, artcraft_client).await
}
}
}
pub fn estimate_costs(&self) -> SplatGenerationCostEstimate {
match self {
SplatGenerationPlan::ArtcraftMarble0p1Mini(plan) => {
estimate_splat_cost_artcraft_marble_0p1_mini(plan)
}
SplatGenerationPlan::ArtcraftMarble0p1Plus(plan) => {
estimate_splat_cost_artcraft_marble_0p1_plus(plan)
}
}
}
}
@@ -1,2 +1,3 @@
pub mod generate_image;
pub mod generate_splat;
pub mod generate_video;
@@ -0,0 +1,26 @@
use crate::core::commands::enqueue::generate_error::GenerateError;
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
use crate::core::events::generation_events::common::GenerationAction;
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
use log::error;
use tauri::AppHandle;
pub(super) fn get_storyteller_creds_or_error(
app: &AppHandle,
storyteller_creds_manager: &StorytellerCredentialManager,
) -> Result<StorytellerCredentialSet, GenerateError> {
match storyteller_creds_manager.get_credentials()? {
Some(creds) => Ok(creds),
None => {
error!("No Artcraft credentials are set. Can't perform action.");
let event =
GenerationEnqueueFailureEvent::no_artcraft_credentials(GenerationAction::GenerateGaussian);
event.send_infallible(app);
Err(GenerateError::needs_storyteller_credentials())
}
}
}
@@ -0,0 +1,32 @@
use crate::core::commands::enqueue::generate_error::GenerateError;
use crate::core::commands::enqueue::image_to_gaussian::artcraft::get_storyteller_creds_or_error::get_storyteller_creds_or_error;
use crate::core::commands::enqueue::image_to_gaussian::artcraft::handle_artcraft_splat_via_router::handle_artcraft_splat_via_router;
use crate::core::commands::enqueue::image_to_gaussian::enqueue_image_to_gaussian_command::{EnqueueImageToGaussianRequest, GaussianModel};
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
use crate::core::events::generation_events::common::GenerationModel;
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
use artcraft_router::api::common_splat_model::CommonSplatModel;
use tauri::AppHandle;
pub async fn handle_gaussian_artcraft(
request: &EnqueueImageToGaussianRequest,
app: &AppHandle,
app_env_configs: &AppEnvConfigs,
storyteller_creds_manager: &StorytellerCredentialManager,
) -> Result<TaskEnqueueSuccess, GenerateError> {
let creds = get_storyteller_creds_or_error(app, storyteller_creds_manager)?;
match request.model {
None => Err(GenerateError::no_model_specified()),
Some(GaussianModel::Marble0p1Mini) => {
handle_artcraft_splat_via_router(request, app_env_configs, &creds, CommonSplatModel::Marble0p1Mini, GenerationModel::WorldlabsMarble0p1Mini).await
}
Some(
GaussianModel::Marble0p1Plus |
GaussianModel::WorldLabsMarble // Legacy variant — route to Plus
) => {
handle_artcraft_splat_via_router(request, app_env_configs, &creds, CommonSplatModel::Marble0p1Plus, GenerationModel::WorldlabsMarble0p1Plus).await
}
}
}
@@ -0,0 +1,64 @@
use crate::core::commands::enqueue::generate_error::GenerateError;
use crate::core::commands::enqueue::image_to_gaussian::enqueue_image_to_gaussian_command::EnqueueImageToGaussianRequest;
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
use crate::core::events::generation_events::common::GenerationModel;
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
use artcraft_router::api::common_splat_model::CommonSplatModel;
use artcraft_router::api::image_list_ref::ImageListRef;
use artcraft_router::api::provider::Provider;
use artcraft_router::client::router_artcraft_client::RouterArtcraftClient;
use artcraft_router::client::router_client::RouterClient;
use artcraft_router::generate::generate_splat::generate_splat_request::GenerateSplatRequest;
use enums::common::generation_provider::GenerationProvider;
use enums::tauri::tasks::task_type::TaskType;
use log::{error, info};
pub(super) async fn handle_artcraft_splat_via_router(
request: &EnqueueImageToGaussianRequest,
app_env_configs: &AppEnvConfigs,
creds: &StorytellerCredentialSet,
model: CommonSplatModel,
generation_model: GenerationModel,
) -> Result<TaskEnqueueSuccess, GenerateError> {
let client = RouterClient::Artcraft(RouterArtcraftClient::new(
app_env_configs.storyteller_host.clone(),
creds.clone(),
));
let reference_image_tokens = request.image_media_tokens.as_ref();
let router_request = GenerateSplatRequest {
model,
provider: Provider::Artcraft,
prompt: request.prompt.as_deref(),
reference_images: reference_image_tokens.map(ImageListRef::MediaFileTokens),
idempotency_token: None,
};
let plan = router_request.build()?;
info!("Splat Generation Plan: {:?}", plan);
let response = match plan.generate_splat(&client).await {
Ok(resp) => {
info!("Successfully enqueued splat generation.");
resp
}
Err(err) => {
error!("Failed to enqueue splat generation: {:?}", err);
return Err(GenerateError::from(err));
}
};
let job_id = response.get_artcraft_payload()
.map(|p| p.inference_job_token.to_string())
.ok_or(GenerateError::ResponseHadNoJobTokens)?;
Ok(TaskEnqueueSuccess {
task_type: TaskType::ImageGeneration,
model: Some(generation_model),
provider: GenerationProvider::Artcraft,
provider_job_id: Some(job_id),
})
}
@@ -0,0 +1,3 @@
mod get_storyteller_creds_or_error;
mod handle_artcraft_splat_via_router;
pub(super) mod handle_artcraft_gaussian;
@@ -1,5 +1,6 @@
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason};
use crate::core::commands::enqueue::image_to_gaussian::artcraft::handle_artcraft_gaussian::handle_gaussian_artcraft;
use crate::core::commands::enqueue::image_to_gaussian::worldlabs::handle_worldlabs_marble::handle_worldlabs_marble;
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus};
@@ -40,6 +41,12 @@ use crate::core::state::artcraft_usage_tracker::artcraft_usage_type::{ArtcraftUs
pub enum GaussianModel {
#[serde(rename = "world_labs_marble")]
WorldLabsMarble,
#[serde(rename = "marble_0p1_mini")]
Marble0p1Mini,
#[serde(rename = "marble_0p1_plus")]
Marble0p1Plus,
}
#[derive(Deserialize)]
@@ -47,7 +54,12 @@ pub struct EnqueueImageToGaussianRequest {
/// REQUIRED.
/// The model to use.
pub model: Option<GaussianModel>,
/// OPTIONAL.
/// The provider to use (defaults to Artcraft/Storyteller).
/// Not all (provider, model) combinations are valid.
pub provider: Option<GenerationProvider>,
/// OPTIONAL.
/// Text prompt for the gaussian generation. Required.
pub prompt: Option<String>,
@@ -224,20 +236,32 @@ pub async fn dispatch_request(
app_env_configs: &AppEnvConfigs,
) -> Result<TaskEnqueueSuccess, GenerateError> {
match request.model {
None => {
return Err(GenerateError::BadInput(BadInputReason::NoModelSpecified));
}
Some(GaussianModel::WorldLabsMarble) => {
return handle_worldlabs_marble(
if request.model.is_none() {
return Err(GenerateError::BadInput(BadInputReason::NoModelSpecified));
}
let provider = request.provider.unwrap_or(GenerationProvider::Artcraft);
match provider {
GenerationProvider::WorldLabs => {
handle_worldlabs_marble(
app,
app_data_root,
app_env_configs,
request,
worldlabs_creds_manager,
).await;
).await
}
};
Err(GenerateError::NoProviderAvailable)
GenerationProvider::Artcraft => {
handle_gaussian_artcraft(
request,
app,
app_env_configs,
storyteller_creds_manager,
).await
}
_ => {
Err(GenerateError::NoProviderAvailable)
}
}
}
@@ -1,2 +1,3 @@
mod artcraft;
pub mod enqueue_image_to_gaussian_command;
mod worldlabs;
@@ -99,6 +99,8 @@ impl TaskEnqueueSuccess{
Some(GenerationModel::Hunyuan3d2_1) => Some(TaskModelType::Hunyuan3d2_1),
Some(GenerationModel::Hunyuan3d3) => Some(TaskModelType::Hunyuan3d3),
Some(GenerationModel::WorldlabsMarble) => Some(TaskModelType::WorldlabsMarble),
Some(GenerationModel::WorldlabsMarble0p1Mini) => Some(TaskModelType::WorldlabsMarble0p1Mini),
Some(GenerationModel::WorldlabsMarble0p1Plus) => Some(TaskModelType::WorldlabsMarble0p1Plus),
Some(GenerationModel::Midjourney) => Some(TaskModelType::Midjourney), // NB: This is a generic Midjourney model, version unknown.
// TODO: These seem wrong -
@@ -59,6 +59,10 @@ pub enum GenerationModel {
#[serde(rename = "worldlabs_marble")]
WorldlabsMarble,
#[serde(rename = "worldlabs_marble_0p1_mini")]
WorldlabsMarble0p1Mini,
#[serde(rename = "worldlabs_marble_0p1_plus")]
WorldlabsMarble0p1Plus,
// Generic Midjourney model, version unknown.
#[serde(rename = "midjourney")]
@@ -101,6 +101,10 @@ pub enum TaskModelType {
Hunyuan3d3,
#[serde(rename = "worldlabs_marble")]
WorldlabsMarble,
#[serde(rename = "marble_0p1_mini")]
WorldlabsMarble0p1Mini,
#[serde(rename = "marble_0p1_plus")]
WorldlabsMarble0p1Plus,
}
impl_enum_display_and_debug_using_to_str!(TaskModelType);
@@ -156,6 +160,8 @@ impl TaskModelType {
Self::Hunyuan3d2_1 => "hunyuan_3d_2.1",
Self::Hunyuan3d3 => "hunyuan_3d_3",
Self::WorldlabsMarble => "worldlabs_marble",
Self::WorldlabsMarble0p1Mini => "marble_0p1_mini",
Self::WorldlabsMarble0p1Plus => "marble_0p1_plus",
}
}
@@ -205,6 +211,8 @@ impl TaskModelType {
"hunyuan_3d_2.1" => Ok(Self::Hunyuan3d2_1),
"hunyuan_3d_3" => Ok(Self::Hunyuan3d3),
"worldlabs_marble" => Ok(Self::WorldlabsMarble),
"marble_0p1_mini" => Ok(Self::WorldlabsMarble0p1Mini),
"marble_0p1_plus" => Ok(Self::WorldlabsMarble0p1Plus),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
@@ -257,6 +265,8 @@ impl TaskModelType {
Self::Hunyuan3d2_1,
Self::Hunyuan3d3,
Self::WorldlabsMarble,
Self::WorldlabsMarble0p1Mini,
Self::WorldlabsMarble0p1Plus,
])
}
}
@@ -316,6 +326,8 @@ mod tests {
assert_serialization(TaskModelType::Hunyuan3d2_1, "hunyuan_3d_2.1");
assert_serialization(TaskModelType::Hunyuan3d3, "hunyuan_3d_3");
assert_serialization(TaskModelType::WorldlabsMarble, "worldlabs_marble");
assert_serialization(TaskModelType::WorldlabsMarble0p1Mini, "marble_0p1_mini");
assert_serialization(TaskModelType::WorldlabsMarble0p1Plus, "marble_0p1_plus");
}
#[test]
@@ -364,6 +376,8 @@ mod tests {
assert_eq!(TaskModelType::Hunyuan3d2_1.to_str(), "hunyuan_3d_2.1");
assert_eq!(TaskModelType::Hunyuan3d3.to_str(), "hunyuan_3d_3");
assert_eq!(TaskModelType::WorldlabsMarble.to_str(), "worldlabs_marble");
assert_eq!(TaskModelType::WorldlabsMarble0p1Mini.to_str(), "marble_0p1_mini");
assert_eq!(TaskModelType::WorldlabsMarble0p1Plus.to_str(), "marble_0p1_plus");
}
#[test]
@@ -412,6 +426,8 @@ mod tests {
assert_eq!(TaskModelType::from_str("hunyuan_3d_2.1").unwrap(), TaskModelType::Hunyuan3d2_1);
assert_eq!(TaskModelType::from_str("hunyuan_3d_3").unwrap(), TaskModelType::Hunyuan3d3);
assert_eq!(TaskModelType::from_str("worldlabs_marble").unwrap(), TaskModelType::WorldlabsMarble);
assert_eq!(TaskModelType::from_str("marble_0p1_mini").unwrap(), TaskModelType::WorldlabsMarble0p1Mini);
assert_eq!(TaskModelType::from_str("marble_0p1_plus").unwrap(), TaskModelType::WorldlabsMarble0p1Plus);
}
#[test]
@@ -428,7 +444,7 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = TaskModelType::all_variants();
assert_eq!(variants.len(), 41);
assert_eq!(variants.len(), 43);
// Image models
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell));
@@ -473,6 +489,8 @@ mod tests {
assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d2_1));
assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d3));
assert_eq!(variants.pop_first(), Some(TaskModelType::WorldlabsMarble));
assert_eq!(variants.pop_first(), Some(TaskModelType::WorldlabsMarble0p1Mini));
assert_eq!(variants.pop_first(), Some(TaskModelType::WorldlabsMarble0p1Plus));
assert_eq!(variants.pop_first(), None);
}
}
@@ -35,6 +35,7 @@ import {
IMAGE_TO_3D_WORLD_PAGE_MODEL_LIST,
ModelPage,
useSelectedModel,
useSelectedProviderForModel,
} from "@storyteller/ui-model-selector";
type Mode = "image" | "text";
@@ -91,6 +92,7 @@ export const ImageTo3DExperience = ({
EnqueueImageTo3dObjectModel.Hunyuan3d3,
);
const selectedWorldModel = useSelectedModel(WORLD_MODEL_PAGE);
const selectedWorldProvider = useSelectedProviderForModel(WORLD_MODEL_PAGE, selectedWorldModel?.id);
const [uploadedPreview, setUploadedPreview] = useState<string | null>(null);
const [uploadedName, setUploadedName] = useState<string | null>(null);
const [prompt, setPrompt] = useState("");
@@ -379,6 +381,7 @@ export const ImageTo3DExperience = ({
image_media_tokens: readyTokens,
prompt: worldPrompt.trim() || undefined,
model: selectedWorldModel ?? SPLAT_MODELS[0],
provider: selectedWorldProvider,
frontend_caller: "mini_app",
frontend_subscriber_id: subscriberId,
});
+1 -1
View File
@@ -1 +1 @@
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
@@ -4,8 +4,6 @@ import { GenerationProvider } from "@storyteller/api-enums";
// TODO: Some of the model configs, such as generation counts, are authoritatively controlled in `legacy/Models.ts`
const SPLAT_PROVIDERS = [GenerationProvider.Artcraft, GenerationProvider.WorldLabs];
export const SPLAT_MODELS : SplatModel [] = [
new SplatModel({
id: "marble_0p1_mini",
@@ -16,7 +14,10 @@ export const SPLAT_MODELS : SplatModel [] = [
selectorName: "Marble Mini",
selectorDescription: "Fast generation, good for quick drafts",
selectorBadges: ["~30 sec."],
providers: SPLAT_PROVIDERS,
providers: [
GenerationProvider.Artcraft,
GenerationProvider.WorldLabs,
],
progressBarTime: 45000,
}),
new SplatModel({
@@ -28,7 +29,10 @@ export const SPLAT_MODELS : SplatModel [] = [
selectorName: "Marble Plus",
selectorDescription: "High quality, best for final renders",
selectorBadges: ["~5 min."],
providers: SPLAT_PROVIDERS,
providers: [
GenerationProvider.Artcraft,
GenerationProvider.WorldLabs,
],
progressBarTime: 300000,
}),
];
@@ -1,6 +1,7 @@
import { invoke } from "@tauri-apps/api/core";
import { CommandResult } from "../common/CommandStatus";
import { Model } from "@storyteller/model-list";
import { GenerationProvider } from "@storyteller/api-enums";
export enum EnqueueImageToGaussianErrorType {
/// Caller didn't specify a model
@@ -17,6 +18,9 @@ export interface EnqueueImageToGaussianRequest {
// Required. The model to use.
model?: Model;
// The provider to use.
provider?: GenerationProvider;
// Optional. Text prompt to direct the gaussian.
prompt?: string;
@@ -32,6 +36,7 @@ export interface EnqueueImageToGaussianRequest {
interface RawEnqueueImageToGaussianRequest {
model?: string;
provider?: GenerationProvider;
prompt?: string;
image_media_tokens?: string[];
frontend_caller?: string;
@@ -61,6 +66,10 @@ export const EnqueueImageToGaussian = async (
image_media_tokens: request.image_media_tokens,
};
if (request.provider) {
mutableRequest.provider = request.provider;
}
if (request.prompt) {
mutableRequest.prompt = request.prompt;
}
@@ -0,0 +1 @@
{"fileNames":[],"fileInfos":[],"root":[],"options":{"composite":true,"declarationMap":true,"emitDeclarationOnly":true,"importHelpers":true,"module":99,"noEmitOnError":true,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUnusedLocals":true,"skipLibCheck":true,"strict":true,"target":9},"version":"5.8.3"}