mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 15:37:49 +00:00
worldlabs marble 1.1 client library (#1379)
This commit is contained in:
1 parent
d51c001835
commit
49e49efa3c
6 files changed
+364
-2
No files matched your search
Generated
+1
@@ -14793,6 +14793,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"test_data",
|
||||
"tokio",
|
||||
"uuid 1.22.0",
|
||||
"workspace-build-acceleration",
|
||||
|
||||
@@ -34,3 +34,4 @@ env_logger.workspace = true
|
||||
env.workspace = true
|
||||
chrono.workspace = true
|
||||
strum.workspace = true
|
||||
test_data.workspace = true
|
||||
@@ -4,12 +4,26 @@ pub enum WorldLabsModel {
|
||||
/// `Marble 0.1-mini` is good for quick drafts
|
||||
/// Generation time: 30-45 seconds
|
||||
/// Cost: 150-330 credits
|
||||
#[deprecated(note="Marble 0.1-mini is deprecated. Use `marble-1.0-draft` instead.")]
|
||||
Marble0p1Mini,
|
||||
|
||||
/// `Marble 0.1-plus` is best for final renders
|
||||
/// Generation time: ~5 minutes,
|
||||
/// Cost: 1500-1600 credits
|
||||
#[deprecated(note="Marble 0.1-plus is deprecated. Use `marble-1.0` instead.")]
|
||||
Marble0p1Plus,
|
||||
|
||||
/// marble-1.0
|
||||
Marble1p0,
|
||||
|
||||
/// marble-1.0-draft
|
||||
Marble1p0Draft,
|
||||
|
||||
/// marble-1.1
|
||||
Marble1p1,
|
||||
|
||||
/// marble-1.1-plus
|
||||
Marble1p1Plus,
|
||||
}
|
||||
|
||||
impl WorldLabsModel {
|
||||
@@ -18,6 +32,159 @@ impl WorldLabsModel {
|
||||
match self {
|
||||
Self::Marble0p1Mini => "Marble 0.1-mini",
|
||||
Self::Marble0p1Plus => "Marble 0.1-plus",
|
||||
Self::Marble1p0 => "marble-1.0",
|
||||
Self::Marble1p0Draft => "marble-1.0-draft",
|
||||
Self::Marble1p1 => "marble-1.1",
|
||||
Self::Marble1p1Plus => "marble-1.1-plus",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_deprecated(&self) -> bool {
|
||||
match self {
|
||||
Self::Marble0p1Mini | Self::Marble0p1Plus => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_new_value(self) -> Self {
|
||||
match self {
|
||||
Self::Marble0p1Mini => Self::Marble1p0Draft,
|
||||
Self::Marble0p1Plus => Self::Marble1p0,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod api_name {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_mini() {
|
||||
assert_eq!(WorldLabsModel::Marble0p1Mini.get_model_api_name_str(), "Marble 0.1-mini");
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_plus() {
|
||||
assert_eq!(WorldLabsModel::Marble0p1Plus.get_model_api_name_str(), "Marble 0.1-plus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0() {
|
||||
assert_eq!(WorldLabsModel::Marble1p0.get_model_api_name_str(), "marble-1.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0_draft() {
|
||||
assert_eq!(WorldLabsModel::Marble1p0Draft.get_model_api_name_str(), "marble-1.0-draft");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1() {
|
||||
assert_eq!(WorldLabsModel::Marble1p1.get_model_api_name_str(), "marble-1.1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1_plus() {
|
||||
assert_eq!(WorldLabsModel::Marble1p1Plus.get_model_api_name_str(), "marble-1.1-plus");
|
||||
}
|
||||
}
|
||||
|
||||
mod is_deprecated {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_mini_is_deprecated() {
|
||||
assert!(WorldLabsModel::Marble0p1Mini.is_deprecated());
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_plus_is_deprecated() {
|
||||
assert!(WorldLabsModel::Marble0p1Plus.is_deprecated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0_is_not_deprecated() {
|
||||
assert!(!WorldLabsModel::Marble1p0.is_deprecated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0_draft_is_not_deprecated() {
|
||||
assert!(!WorldLabsModel::Marble1p0Draft.is_deprecated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1_is_not_deprecated() {
|
||||
assert!(!WorldLabsModel::Marble1p1.is_deprecated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1_plus_is_not_deprecated() {
|
||||
assert!(!WorldLabsModel::Marble1p1Plus.is_deprecated());
|
||||
}
|
||||
}
|
||||
|
||||
mod to_new_value {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_mini_maps_to_1p0_draft() {
|
||||
let new = WorldLabsModel::Marble0p1Mini.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.0-draft");
|
||||
assert!(!new.is_deprecated());
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn marble_0p1_plus_maps_to_1p0() {
|
||||
let new = WorldLabsModel::Marble0p1Plus.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.0");
|
||||
assert!(!new.is_deprecated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0_stays_as_1p0() {
|
||||
let new = WorldLabsModel::Marble1p0.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p0_draft_stays_as_1p0_draft() {
|
||||
let new = WorldLabsModel::Marble1p0Draft.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.0-draft");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1_stays_as_1p1() {
|
||||
let new = WorldLabsModel::Marble1p1.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marble_1p1_plus_stays_as_1p1_plus() {
|
||||
let new = WorldLabsModel::Marble1p1Plus.to_new_value();
|
||||
assert_eq!(new.get_model_api_name_str(), "marble-1.1-plus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_models_are_not_deprecated_after_conversion() {
|
||||
for model in [
|
||||
WorldLabsModel::Marble1p0,
|
||||
WorldLabsModel::Marble1p0Draft,
|
||||
WorldLabsModel::Marble1p1,
|
||||
WorldLabsModel::Marble1p1Plus,
|
||||
] {
|
||||
let new = model.to_new_value();
|
||||
assert!(!new.is_deprecated(), "{:?} should not be deprecated after to_new_value", model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
-1
@@ -33,10 +33,15 @@ pub struct GenerateWorldResponse {
|
||||
pub async fn generate_world(args: GenerateWorldArgs<'_>) -> Result<GenerateWorldResponse, WorldLabsError> {
|
||||
let client = Client::new();
|
||||
|
||||
let model_name = args.model
|
||||
.to_new_value()
|
||||
.get_model_api_name_str()
|
||||
.to_string();
|
||||
|
||||
let payload = RawRequest {
|
||||
world_prompt: args.world_prompt,
|
||||
display_name: args.display_name,
|
||||
model: Some(args.model.get_model_api_name_str().to_string()),
|
||||
model: Some(model_name),
|
||||
seed: args.seed,
|
||||
tags: args.tags,
|
||||
permission: args.permission,
|
||||
@@ -347,4 +352,98 @@ mod tests {
|
||||
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
/// Marble 1.1: generate world from a mountain/tree image via URI.
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_generate_world_marble_1p1_image_uri() {
|
||||
setup_test_logging(LevelFilter::Debug);
|
||||
|
||||
let creds = get_test_api_key().unwrap();
|
||||
|
||||
let image_url = test_data::web::image_urls::MOUNTAIN_TREE_IMAGE_URL;
|
||||
|
||||
println!("=== generate_world with Marble 1.1 ===");
|
||||
println!("Image URL: {}", image_url);
|
||||
|
||||
let result = generate_world(GenerateWorldArgs {
|
||||
creds: &creds,
|
||||
world_prompt: WorldPrompt::Image {
|
||||
image_prompt: ContentReference::Uri {
|
||||
uri: image_url.to_string(),
|
||||
},
|
||||
text_prompt: Some("Mountain landscape with a tree".to_string()),
|
||||
is_pano: None,
|
||||
disable_recaption: None,
|
||||
},
|
||||
display_name: None,
|
||||
model: WorldLabsModel::Marble1p1,
|
||||
seed: None,
|
||||
tags: None,
|
||||
permission: None,
|
||||
request_timeout: None,
|
||||
}).await;
|
||||
|
||||
match &result {
|
||||
Ok(r) => {
|
||||
println!("SUCCESS — Marble 1.1");
|
||||
println!(" Operation ID: {}", r.operation_id.as_str());
|
||||
println!(" Done: {}", r.done);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("ERROR — Marble 1.1");
|
||||
println!(" Error (Debug): {:?}", err);
|
||||
println!(" Error (Display): {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
/// Marble 1.1-plus: generate world from a wide fall mountains image via URI.
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_generate_world_marble_1p1_plus_image_uri() {
|
||||
setup_test_logging(LevelFilter::Debug);
|
||||
|
||||
let creds = get_test_api_key().unwrap();
|
||||
|
||||
let image_url = test_data::web::image_urls::SUPER_WIDE_FALL_MOUNTAINS_IMAGE_URL;
|
||||
|
||||
println!("=== generate_world with Marble 1.1-plus ===");
|
||||
println!("Image URL: {}", image_url);
|
||||
|
||||
let result = generate_world(GenerateWorldArgs {
|
||||
creds: &creds,
|
||||
world_prompt: WorldPrompt::Image {
|
||||
image_prompt: ContentReference::Uri {
|
||||
uri: image_url.to_string(),
|
||||
},
|
||||
text_prompt: Some("Autumn mountain panorama".to_string()),
|
||||
is_pano: None,
|
||||
disable_recaption: None,
|
||||
},
|
||||
display_name: None,
|
||||
model: WorldLabsModel::Marble1p1Plus,
|
||||
seed: None,
|
||||
tags: None,
|
||||
permission: None,
|
||||
request_timeout: None,
|
||||
}).await;
|
||||
|
||||
match &result {
|
||||
Ok(r) => {
|
||||
println!("SUCCESS — Marble 1.1-plus");
|
||||
println!(" Operation ID: {}", r.operation_id.as_str());
|
||||
println!(" Done: {}", r.done);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("ERROR — Marble 1.1-plus");
|
||||
println!(" Error (Debug): {:?}", err);
|
||||
println!(" Error (Display): {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -195,7 +195,8 @@ mod tests {
|
||||
|
||||
let creds = get_test_api_key().unwrap();
|
||||
|
||||
let operation_id = OperationId("1fab3bf1-05a1-4929-907e-c6df07c539e2".to_string());
|
||||
//let operation_id = OperationId("1fab3bf1-05a1-4929-907e-c6df07c539e2".to_string());
|
||||
let operation_id = OperationId("a0894fa6-594d-4680-8f3a-5109f22664b1".to_string());
|
||||
|
||||
let response = get_operation(GetOperationArgs {
|
||||
creds: &creds,
|
||||
|
||||
@@ -42,6 +42,38 @@ fn credits_for(model: WorldLabsModel, input_type: InputType) -> u32 {
|
||||
(WorldLabsModel::Marble0p1Plus, InputType::ImageNonPanorama) => 1580,
|
||||
(WorldLabsModel::Marble0p1Plus, InputType::MultiImage) => 1600,
|
||||
(WorldLabsModel::Marble0p1Plus, InputType::Video) => 1600,
|
||||
|
||||
// Marble 1.0 (successor to 0.1-plus)
|
||||
(WorldLabsModel::Marble1p0, InputType::ImagePanorama) => 1500,
|
||||
(WorldLabsModel::Marble1p0, InputType::Text) => 1580,
|
||||
(WorldLabsModel::Marble1p0, InputType::ImageNonPanorama) => 1580,
|
||||
(WorldLabsModel::Marble1p0, InputType::MultiImage) => 1600,
|
||||
(WorldLabsModel::Marble1p0, InputType::Video) => 1600,
|
||||
|
||||
// Marble 1.0-draft (successor to 0.1-mini)
|
||||
(WorldLabsModel::Marble1p0Draft, InputType::ImagePanorama) => 150,
|
||||
(WorldLabsModel::Marble1p0Draft, InputType::Text) => 230,
|
||||
(WorldLabsModel::Marble1p0Draft, InputType::ImageNonPanorama) => 230,
|
||||
(WorldLabsModel::Marble1p0Draft, InputType::MultiImage) => 250,
|
||||
(WorldLabsModel::Marble1p0Draft, InputType::Video) => 250,
|
||||
|
||||
// Marble 1.1
|
||||
(WorldLabsModel::Marble1p1, InputType::ImagePanorama) => 1500,
|
||||
(WorldLabsModel::Marble1p1, InputType::Text) => 1580,
|
||||
(WorldLabsModel::Marble1p1, InputType::ImageNonPanorama) => 1580,
|
||||
(WorldLabsModel::Marble1p1, InputType::MultiImage) => 1600,
|
||||
(WorldLabsModel::Marble1p1, InputType::Video) => 1600,
|
||||
|
||||
// TODO: Looks like there's no way for us to know what the price will be
|
||||
// for us to bill this accurately, we'll have to bill this after the fact.
|
||||
// also - they don't have an API for us to ascertain this??! Wat.
|
||||
// Marble 1.1-plus (range pricing — we use the max to charge enough upfront)
|
||||
// Actual range: 1500–3000 / 1580–3080 / 1600–3100
|
||||
(WorldLabsModel::Marble1p1Plus, InputType::ImagePanorama) => 3000,
|
||||
(WorldLabsModel::Marble1p1Plus, InputType::Text) => 3080,
|
||||
(WorldLabsModel::Marble1p1Plus, InputType::ImageNonPanorama) => 3080,
|
||||
(WorldLabsModel::Marble1p1Plus, InputType::MultiImage) => 3100,
|
||||
(WorldLabsModel::Marble1p1Plus, InputType::Video) => 3100,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +96,10 @@ mod tests {
|
||||
let models = [
|
||||
WorldLabsModel::Marble0p1Mini,
|
||||
WorldLabsModel::Marble0p1Plus,
|
||||
WorldLabsModel::Marble1p0,
|
||||
WorldLabsModel::Marble1p0Draft,
|
||||
WorldLabsModel::Marble1p1,
|
||||
WorldLabsModel::Marble1p1Plus,
|
||||
];
|
||||
|
||||
println!("\n{:<20} {:<20} {:>10} {:>12}", "Model", "Input Type", "Credits", "USD Cents");
|
||||
@@ -101,6 +137,59 @@ mod tests {
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble0p1Plus, InputType::Video).worldlabs_credits, 1600);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marble_1p0_draft_credits() {
|
||||
// Same as Marble 0.1-mini (its successor)
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0Draft, InputType::Text).worldlabs_credits, 230);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0Draft, InputType::ImageNonPanorama).worldlabs_credits, 230);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0Draft, InputType::ImagePanorama).worldlabs_credits, 150);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0Draft, InputType::MultiImage).worldlabs_credits, 250);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0Draft, InputType::Video).worldlabs_credits, 250);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marble_1p0_credits() {
|
||||
// Standard model, same pricing as Marble 0.1-plus
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0, InputType::Text).worldlabs_credits, 1580);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0, InputType::ImageNonPanorama).worldlabs_credits, 1580);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0, InputType::ImagePanorama).worldlabs_credits, 1500);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0, InputType::MultiImage).worldlabs_credits, 1600);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p0, InputType::Video).worldlabs_credits, 1600);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marble_1p1_credits() {
|
||||
// Standard model, same pricing tier as 1.0
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1, InputType::Text).worldlabs_credits, 1580);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1, InputType::ImageNonPanorama).worldlabs_credits, 1580);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1, InputType::ImagePanorama).worldlabs_credits, 1500);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1, InputType::MultiImage).worldlabs_credits, 1600);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1, InputType::Video).worldlabs_credits, 1600);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marble_1p1_plus_credits() {
|
||||
// Plus model uses max of the range pricing (1500–3000, 1580–3080, 1600–3100)
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1Plus, InputType::Text).worldlabs_credits, 3080);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1Plus, InputType::ImageNonPanorama).worldlabs_credits, 3080);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1Plus, InputType::ImagePanorama).worldlabs_credits, 3000);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1Plus, InputType::MultiImage).worldlabs_credits, 3100);
|
||||
assert_eq!(calculate_cost(WorldLabsModel::Marble1p1Plus, InputType::Video).worldlabs_credits, 3100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marble_1p1_plus_is_more_expensive_than_standard() {
|
||||
for input_type in InputType::iter() {
|
||||
let standard = calculate_cost(WorldLabsModel::Marble1p1, input_type);
|
||||
let plus = calculate_cost(WorldLabsModel::Marble1p1Plus, input_type);
|
||||
assert!(
|
||||
plus.worldlabs_credits > standard.worldlabs_credits,
|
||||
"1.1-plus should cost more than 1.1 for {:?}: {} vs {}",
|
||||
input_type, plus.worldlabs_credits, standard.worldlabs_credits,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dollar_conversion() {
|
||||
// 1250 credits = $1.00 = 100 cents
|
||||
@@ -120,6 +209,10 @@ mod tests {
|
||||
let models = [
|
||||
WorldLabsModel::Marble0p1Mini,
|
||||
WorldLabsModel::Marble0p1Plus,
|
||||
WorldLabsModel::Marble1p0,
|
||||
WorldLabsModel::Marble1p0Draft,
|
||||
WorldLabsModel::Marble1p1,
|
||||
WorldLabsModel::Marble1p1Plus,
|
||||
];
|
||||
|
||||
for model in &models {
|
||||
|
||||
Reference in new issue
Block a user