mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 07:45:41 -05:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e89846000b | ||
|
|
6d2dafb244 | ||
|
|
ce73e1f988 | ||
|
|
a50304d506 | ||
|
|
73e65da3bf | ||
|
|
9961c52ba0 | ||
|
|
b54ef6cab3 | ||
|
|
2a05e6e660 |
+131
@@ -0,0 +1,131 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonAspectRatio` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientAspectRatio {
|
||||
Auto,
|
||||
Square,
|
||||
WideThreeByTwo,
|
||||
WideFourByThree,
|
||||
WideFiveByFour,
|
||||
WideSixteenByNine,
|
||||
WideTwentyOneByNine,
|
||||
TallTwoByThree,
|
||||
TallThreeByFour,
|
||||
TallFourByFive,
|
||||
TallNineBySixteen,
|
||||
TallNineByTwentyOne,
|
||||
Wide,
|
||||
Tall,
|
||||
Auto2k,
|
||||
Auto3k,
|
||||
Auto4k,
|
||||
SquareHd,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientAspectRatio {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"auto" => Self::Auto,
|
||||
"square" => Self::Square,
|
||||
"wide_three_by_two" => Self::WideThreeByTwo,
|
||||
"wide_four_by_three" => Self::WideFourByThree,
|
||||
"wide_five_by_four" => Self::WideFiveByFour,
|
||||
"wide_sixteen_by_nine" => Self::WideSixteenByNine,
|
||||
"wide_twenty_one_by_nine" => Self::WideTwentyOneByNine,
|
||||
"tall_two_by_three" => Self::TallTwoByThree,
|
||||
"tall_three_by_four" => Self::TallThreeByFour,
|
||||
"tall_four_by_five" => Self::TallFourByFive,
|
||||
"tall_nine_by_sixteen" => Self::TallNineBySixteen,
|
||||
"tall_nine_by_twenty_one" => Self::TallNineByTwentyOne,
|
||||
"wide" => Self::Wide,
|
||||
"tall" => Self::Tall,
|
||||
"auto_2k" => Self::Auto2k,
|
||||
"auto_3k" => Self::Auto3k,
|
||||
"auto_4k" => Self::Auto4k,
|
||||
"square_hd" => Self::SquareHd,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientAspectRatio> for String {
|
||||
fn from(value: ApiClientAspectRatio) -> Self {
|
||||
match value {
|
||||
ApiClientAspectRatio::Auto => "auto".to_string(),
|
||||
ApiClientAspectRatio::Square => "square".to_string(),
|
||||
ApiClientAspectRatio::WideThreeByTwo => "wide_three_by_two".to_string(),
|
||||
ApiClientAspectRatio::WideFourByThree => "wide_four_by_three".to_string(),
|
||||
ApiClientAspectRatio::WideFiveByFour => "wide_five_by_four".to_string(),
|
||||
ApiClientAspectRatio::WideSixteenByNine => "wide_sixteen_by_nine".to_string(),
|
||||
ApiClientAspectRatio::WideTwentyOneByNine => "wide_twenty_one_by_nine".to_string(),
|
||||
ApiClientAspectRatio::TallTwoByThree => "tall_two_by_three".to_string(),
|
||||
ApiClientAspectRatio::TallThreeByFour => "tall_three_by_four".to_string(),
|
||||
ApiClientAspectRatio::TallFourByFive => "tall_four_by_five".to_string(),
|
||||
ApiClientAspectRatio::TallNineBySixteen => "tall_nine_by_sixteen".to_string(),
|
||||
ApiClientAspectRatio::TallNineByTwentyOne => "tall_nine_by_twenty_one".to_string(),
|
||||
ApiClientAspectRatio::Wide => "wide".to_string(),
|
||||
ApiClientAspectRatio::Tall => "tall".to_string(),
|
||||
ApiClientAspectRatio::Auto2k => "auto_2k".to_string(),
|
||||
ApiClientAspectRatio::Auto3k => "auto_3k".to_string(),
|
||||
ApiClientAspectRatio::Auto4k => "auto_4k".to_string(),
|
||||
ApiClientAspectRatio::SquareHd => "square_hd".to_string(),
|
||||
ApiClientAspectRatio::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientAspectRatio = serde_json::from_str("\"auto\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientAspectRatio::Auto);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"auto\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientAspectRatio = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientAspectRatio::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientAspectRatio::Auto, "auto"),
|
||||
(ApiClientAspectRatio::Square, "square"),
|
||||
(ApiClientAspectRatio::WideThreeByTwo, "wide_three_by_two"),
|
||||
(ApiClientAspectRatio::WideFourByThree, "wide_four_by_three"),
|
||||
(ApiClientAspectRatio::WideFiveByFour, "wide_five_by_four"),
|
||||
(ApiClientAspectRatio::WideSixteenByNine, "wide_sixteen_by_nine"),
|
||||
(ApiClientAspectRatio::WideTwentyOneByNine, "wide_twenty_one_by_nine"),
|
||||
(ApiClientAspectRatio::TallTwoByThree, "tall_two_by_three"),
|
||||
(ApiClientAspectRatio::TallThreeByFour, "tall_three_by_four"),
|
||||
(ApiClientAspectRatio::TallFourByFive, "tall_four_by_five"),
|
||||
(ApiClientAspectRatio::TallNineBySixteen, "tall_nine_by_sixteen"),
|
||||
(ApiClientAspectRatio::TallNineByTwentyOne, "tall_nine_by_twenty_one"),
|
||||
(ApiClientAspectRatio::Wide, "wide"),
|
||||
(ApiClientAspectRatio::Tall, "tall"),
|
||||
(ApiClientAspectRatio::Auto2k, "auto_2k"),
|
||||
(ApiClientAspectRatio::Auto3k, "auto_3k"),
|
||||
(ApiClientAspectRatio::Auto4k, "auto_4k"),
|
||||
(ApiClientAspectRatio::SquareHd, "square_hd"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientAspectRatio = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonBitrate` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientBitrate {
|
||||
Normal,
|
||||
High,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientBitrate {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"normal" => Self::Normal,
|
||||
"high" => Self::High,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientBitrate> for String {
|
||||
fn from(value: ApiClientBitrate) -> Self {
|
||||
match value {
|
||||
ApiClientBitrate::Normal => "normal".to_string(),
|
||||
ApiClientBitrate::High => "high".to_string(),
|
||||
ApiClientBitrate::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientBitrate = serde_json::from_str("\"normal\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientBitrate::Normal);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"normal\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientBitrate = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientBitrate::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientBitrate::Normal, "normal"),
|
||||
(ApiClientBitrate::High, "high"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientBitrate = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `GenerationProvider` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientGenerationProvider {
|
||||
Artcraft,
|
||||
Fal,
|
||||
Grok,
|
||||
Midjourney,
|
||||
Sora,
|
||||
WorldLabs,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientGenerationProvider {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"artcraft" => Self::Artcraft,
|
||||
"fal" => Self::Fal,
|
||||
"grok" => Self::Grok,
|
||||
"midjourney" => Self::Midjourney,
|
||||
"sora" => Self::Sora,
|
||||
"world_labs" => Self::WorldLabs,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientGenerationProvider> for String {
|
||||
fn from(value: ApiClientGenerationProvider) -> Self {
|
||||
match value {
|
||||
ApiClientGenerationProvider::Artcraft => "artcraft".to_string(),
|
||||
ApiClientGenerationProvider::Fal => "fal".to_string(),
|
||||
ApiClientGenerationProvider::Grok => "grok".to_string(),
|
||||
ApiClientGenerationProvider::Midjourney => "midjourney".to_string(),
|
||||
ApiClientGenerationProvider::Sora => "sora".to_string(),
|
||||
ApiClientGenerationProvider::WorldLabs => "world_labs".to_string(),
|
||||
ApiClientGenerationProvider::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientGenerationProvider = serde_json::from_str("\"artcraft\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientGenerationProvider::Artcraft);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"artcraft\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientGenerationProvider = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientGenerationProvider::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientGenerationProvider::Artcraft, "artcraft"),
|
||||
(ApiClientGenerationProvider::Fal, "fal"),
|
||||
(ApiClientGenerationProvider::Grok, "grok"),
|
||||
(ApiClientGenerationProvider::Midjourney, "midjourney"),
|
||||
(ApiClientGenerationProvider::Sora, "sora"),
|
||||
(ApiClientGenerationProvider::WorldLabs, "world_labs"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientGenerationProvider = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonImageModel` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientImageModel {
|
||||
Flux1Dev,
|
||||
Flux1Schnell,
|
||||
FluxPro11,
|
||||
FluxPro11Ultra,
|
||||
GptImage1,
|
||||
GptImage1p5,
|
||||
GptImage2,
|
||||
GrokImagineImage,
|
||||
GrokImagineImageQuality,
|
||||
Midjourney7,
|
||||
Midjourney7Niji,
|
||||
Midjourney8,
|
||||
NanoBanana,
|
||||
NanoBanana2,
|
||||
NanoBananaPro,
|
||||
Seedream4,
|
||||
Seedream4p5,
|
||||
Seedream5Lite,
|
||||
QwenEdit2511Angles,
|
||||
Flux2LoraAngles,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientImageModel {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"flux_1_dev" => Self::Flux1Dev,
|
||||
"flux_1_schnell" => Self::Flux1Schnell,
|
||||
"flux_pro_1p1" => Self::FluxPro11,
|
||||
"flux_pro_1p1_ultra" => Self::FluxPro11Ultra,
|
||||
"gpt_image_1" => Self::GptImage1,
|
||||
"gpt_image_1p5" => Self::GptImage1p5,
|
||||
"gpt_image_2" => Self::GptImage2,
|
||||
"grok_imagine_image" => Self::GrokImagineImage,
|
||||
"grok_imagine_image_q" => Self::GrokImagineImageQuality,
|
||||
"midjourney_7" => Self::Midjourney7,
|
||||
"midjourney_7_niji" => Self::Midjourney7Niji,
|
||||
"midjourney_8" => Self::Midjourney8,
|
||||
"nano_banana" => Self::NanoBanana,
|
||||
"nano_banana_2" => Self::NanoBanana2,
|
||||
"nano_banana_pro" => Self::NanoBananaPro,
|
||||
"seedream_4" => Self::Seedream4,
|
||||
"seedream_4p5" => Self::Seedream4p5,
|
||||
"seedream_5_lite" => Self::Seedream5Lite,
|
||||
"qwen_edit_2511_angles" => Self::QwenEdit2511Angles,
|
||||
"flux_2_lora_angles" => Self::Flux2LoraAngles,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientImageModel> for String {
|
||||
fn from(value: ApiClientImageModel) -> Self {
|
||||
match value {
|
||||
ApiClientImageModel::Flux1Dev => "flux_1_dev".to_string(),
|
||||
ApiClientImageModel::Flux1Schnell => "flux_1_schnell".to_string(),
|
||||
ApiClientImageModel::FluxPro11 => "flux_pro_1p1".to_string(),
|
||||
ApiClientImageModel::FluxPro11Ultra => "flux_pro_1p1_ultra".to_string(),
|
||||
ApiClientImageModel::GptImage1 => "gpt_image_1".to_string(),
|
||||
ApiClientImageModel::GptImage1p5 => "gpt_image_1p5".to_string(),
|
||||
ApiClientImageModel::GptImage2 => "gpt_image_2".to_string(),
|
||||
ApiClientImageModel::GrokImagineImage => "grok_imagine_image".to_string(),
|
||||
ApiClientImageModel::GrokImagineImageQuality => "grok_imagine_image_q".to_string(),
|
||||
ApiClientImageModel::Midjourney7 => "midjourney_7".to_string(),
|
||||
ApiClientImageModel::Midjourney7Niji => "midjourney_7_niji".to_string(),
|
||||
ApiClientImageModel::Midjourney8 => "midjourney_8".to_string(),
|
||||
ApiClientImageModel::NanoBanana => "nano_banana".to_string(),
|
||||
ApiClientImageModel::NanoBanana2 => "nano_banana_2".to_string(),
|
||||
ApiClientImageModel::NanoBananaPro => "nano_banana_pro".to_string(),
|
||||
ApiClientImageModel::Seedream4 => "seedream_4".to_string(),
|
||||
ApiClientImageModel::Seedream4p5 => "seedream_4p5".to_string(),
|
||||
ApiClientImageModel::Seedream5Lite => "seedream_5_lite".to_string(),
|
||||
ApiClientImageModel::QwenEdit2511Angles => "qwen_edit_2511_angles".to_string(),
|
||||
ApiClientImageModel::Flux2LoraAngles => "flux_2_lora_angles".to_string(),
|
||||
ApiClientImageModel::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientImageModel = serde_json::from_str("\"flux_1_dev\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientImageModel::Flux1Dev);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"flux_1_dev\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientImageModel = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientImageModel::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientImageModel::Flux1Dev, "flux_1_dev"),
|
||||
(ApiClientImageModel::Flux1Schnell, "flux_1_schnell"),
|
||||
(ApiClientImageModel::FluxPro11, "flux_pro_1p1"),
|
||||
(ApiClientImageModel::FluxPro11Ultra, "flux_pro_1p1_ultra"),
|
||||
(ApiClientImageModel::GptImage1, "gpt_image_1"),
|
||||
(ApiClientImageModel::GptImage1p5, "gpt_image_1p5"),
|
||||
(ApiClientImageModel::GptImage2, "gpt_image_2"),
|
||||
(ApiClientImageModel::GrokImagineImage, "grok_imagine_image"),
|
||||
(ApiClientImageModel::GrokImagineImageQuality, "grok_imagine_image_q"),
|
||||
(ApiClientImageModel::Midjourney7, "midjourney_7"),
|
||||
(ApiClientImageModel::Midjourney7Niji, "midjourney_7_niji"),
|
||||
(ApiClientImageModel::Midjourney8, "midjourney_8"),
|
||||
(ApiClientImageModel::NanoBanana, "nano_banana"),
|
||||
(ApiClientImageModel::NanoBanana2, "nano_banana_2"),
|
||||
(ApiClientImageModel::NanoBananaPro, "nano_banana_pro"),
|
||||
(ApiClientImageModel::Seedream4, "seedream_4"),
|
||||
(ApiClientImageModel::Seedream4p5, "seedream_4p5"),
|
||||
(ApiClientImageModel::Seedream5Lite, "seedream_5_lite"),
|
||||
(ApiClientImageModel::QwenEdit2511Angles, "qwen_edit_2511_angles"),
|
||||
(ApiClientImageModel::Flux2LoraAngles, "flux_2_lora_angles"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientImageModel = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `ModelCreator` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientModelCreator {
|
||||
Alibaba,
|
||||
ArtCraft,
|
||||
BlackForestLabs,
|
||||
Bytedance,
|
||||
Fal,
|
||||
Google,
|
||||
Grok,
|
||||
Hailuo,
|
||||
Higgsfield,
|
||||
Kling,
|
||||
Krea,
|
||||
Midjourney,
|
||||
OpenAi,
|
||||
OpenArt,
|
||||
Recraft,
|
||||
Replicate,
|
||||
Runway,
|
||||
Stability,
|
||||
Tencent,
|
||||
TensorArt,
|
||||
Vidu,
|
||||
WorldLabs,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientModelCreator {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"alibaba" => Self::Alibaba,
|
||||
"artcraft" => Self::ArtCraft,
|
||||
"black_forest_labs" => Self::BlackForestLabs,
|
||||
"bytedance" => Self::Bytedance,
|
||||
"fal" => Self::Fal,
|
||||
"google" => Self::Google,
|
||||
"grok" => Self::Grok,
|
||||
"hailuo" => Self::Hailuo,
|
||||
"higgsfield" => Self::Higgsfield,
|
||||
"kling" => Self::Kling,
|
||||
"krea" => Self::Krea,
|
||||
"midjourney" => Self::Midjourney,
|
||||
"open_ai" => Self::OpenAi,
|
||||
"open_art" => Self::OpenArt,
|
||||
"recraft" => Self::Recraft,
|
||||
"replicate" => Self::Replicate,
|
||||
"runway" => Self::Runway,
|
||||
"stability" => Self::Stability,
|
||||
"tencent" => Self::Tencent,
|
||||
"tensor_art" => Self::TensorArt,
|
||||
"vidu" => Self::Vidu,
|
||||
"world_labs" => Self::WorldLabs,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientModelCreator> for String {
|
||||
fn from(value: ApiClientModelCreator) -> Self {
|
||||
match value {
|
||||
ApiClientModelCreator::Alibaba => "alibaba".to_string(),
|
||||
ApiClientModelCreator::ArtCraft => "artcraft".to_string(),
|
||||
ApiClientModelCreator::BlackForestLabs => "black_forest_labs".to_string(),
|
||||
ApiClientModelCreator::Bytedance => "bytedance".to_string(),
|
||||
ApiClientModelCreator::Fal => "fal".to_string(),
|
||||
ApiClientModelCreator::Google => "google".to_string(),
|
||||
ApiClientModelCreator::Grok => "grok".to_string(),
|
||||
ApiClientModelCreator::Hailuo => "hailuo".to_string(),
|
||||
ApiClientModelCreator::Higgsfield => "higgsfield".to_string(),
|
||||
ApiClientModelCreator::Kling => "kling".to_string(),
|
||||
ApiClientModelCreator::Krea => "krea".to_string(),
|
||||
ApiClientModelCreator::Midjourney => "midjourney".to_string(),
|
||||
ApiClientModelCreator::OpenAi => "open_ai".to_string(),
|
||||
ApiClientModelCreator::OpenArt => "open_art".to_string(),
|
||||
ApiClientModelCreator::Recraft => "recraft".to_string(),
|
||||
ApiClientModelCreator::Replicate => "replicate".to_string(),
|
||||
ApiClientModelCreator::Runway => "runway".to_string(),
|
||||
ApiClientModelCreator::Stability => "stability".to_string(),
|
||||
ApiClientModelCreator::Tencent => "tencent".to_string(),
|
||||
ApiClientModelCreator::TensorArt => "tensor_art".to_string(),
|
||||
ApiClientModelCreator::Vidu => "vidu".to_string(),
|
||||
ApiClientModelCreator::WorldLabs => "world_labs".to_string(),
|
||||
ApiClientModelCreator::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientModelCreator = serde_json::from_str("\"alibaba\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientModelCreator::Alibaba);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"alibaba\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientModelCreator = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientModelCreator::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientModelCreator::Alibaba, "alibaba"),
|
||||
(ApiClientModelCreator::ArtCraft, "artcraft"),
|
||||
(ApiClientModelCreator::BlackForestLabs, "black_forest_labs"),
|
||||
(ApiClientModelCreator::Bytedance, "bytedance"),
|
||||
(ApiClientModelCreator::Fal, "fal"),
|
||||
(ApiClientModelCreator::Google, "google"),
|
||||
(ApiClientModelCreator::Grok, "grok"),
|
||||
(ApiClientModelCreator::Hailuo, "hailuo"),
|
||||
(ApiClientModelCreator::Higgsfield, "higgsfield"),
|
||||
(ApiClientModelCreator::Kling, "kling"),
|
||||
(ApiClientModelCreator::Krea, "krea"),
|
||||
(ApiClientModelCreator::Midjourney, "midjourney"),
|
||||
(ApiClientModelCreator::OpenAi, "open_ai"),
|
||||
(ApiClientModelCreator::OpenArt, "open_art"),
|
||||
(ApiClientModelCreator::Recraft, "recraft"),
|
||||
(ApiClientModelCreator::Replicate, "replicate"),
|
||||
(ApiClientModelCreator::Runway, "runway"),
|
||||
(ApiClientModelCreator::Stability, "stability"),
|
||||
(ApiClientModelCreator::Tencent, "tencent"),
|
||||
(ApiClientModelCreator::TensorArt, "tensor_art"),
|
||||
(ApiClientModelCreator::Vidu, "vidu"),
|
||||
(ApiClientModelCreator::WorldLabs, "world_labs"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientModelCreator = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonQuality` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientQuality {
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientQuality {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"high" => Self::High,
|
||||
"medium" => Self::Medium,
|
||||
"low" => Self::Low,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientQuality> for String {
|
||||
fn from(value: ApiClientQuality) -> Self {
|
||||
match value {
|
||||
ApiClientQuality::High => "high".to_string(),
|
||||
ApiClientQuality::Medium => "medium".to_string(),
|
||||
ApiClientQuality::Low => "low".to_string(),
|
||||
ApiClientQuality::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientQuality = serde_json::from_str("\"high\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientQuality::High);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"high\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientQuality = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientQuality::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientQuality::High, "high"),
|
||||
(ApiClientQuality::Medium, "medium"),
|
||||
(ApiClientQuality::Low, "low"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientQuality = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonResolution` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientResolution {
|
||||
OneK,
|
||||
TwoK,
|
||||
ThreeK,
|
||||
FourK,
|
||||
HalfK,
|
||||
FourEightyP,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientResolution {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"one_k" => Self::OneK,
|
||||
"two_k" => Self::TwoK,
|
||||
"three_k" => Self::ThreeK,
|
||||
"four_k" => Self::FourK,
|
||||
"half_k" => Self::HalfK,
|
||||
"four_eighty_p" => Self::FourEightyP,
|
||||
"seven_twenty_p" => Self::SevenTwentyP,
|
||||
"ten_eighty_p" => Self::TenEightyP,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientResolution> for String {
|
||||
fn from(value: ApiClientResolution) -> Self {
|
||||
match value {
|
||||
ApiClientResolution::OneK => "one_k".to_string(),
|
||||
ApiClientResolution::TwoK => "two_k".to_string(),
|
||||
ApiClientResolution::ThreeK => "three_k".to_string(),
|
||||
ApiClientResolution::FourK => "four_k".to_string(),
|
||||
ApiClientResolution::HalfK => "half_k".to_string(),
|
||||
ApiClientResolution::FourEightyP => "four_eighty_p".to_string(),
|
||||
ApiClientResolution::SevenTwentyP => "seven_twenty_p".to_string(),
|
||||
ApiClientResolution::TenEightyP => "ten_eighty_p".to_string(),
|
||||
ApiClientResolution::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientResolution = serde_json::from_str("\"one_k\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientResolution::OneK);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"one_k\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientResolution = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientResolution::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientResolution::OneK, "one_k"),
|
||||
(ApiClientResolution::TwoK, "two_k"),
|
||||
(ApiClientResolution::ThreeK, "three_k"),
|
||||
(ApiClientResolution::FourK, "four_k"),
|
||||
(ApiClientResolution::HalfK, "half_k"),
|
||||
(ApiClientResolution::FourEightyP, "four_eighty_p"),
|
||||
(ApiClientResolution::SevenTwentyP, "seven_twenty_p"),
|
||||
(ApiClientResolution::TenEightyP, "ten_eighty_p"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientResolution = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Forward-compatible, self-contained API-client copy of the server's `CommonVideoModel` enum.
|
||||
///
|
||||
/// Serialized as a string. Any value this client build does not recognize is preserved
|
||||
/// verbatim in [`Unknown`], so newer server variants never break deserialization and
|
||||
/// still round-trip back to the original string.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "String", into = "String")]
|
||||
pub enum ApiClientVideoModel {
|
||||
GrokVideo,
|
||||
GrokImagineVideo,
|
||||
GrokImagineVideo1p5,
|
||||
Kling16Pro,
|
||||
Kling21Pro,
|
||||
Kling21Master,
|
||||
Kling2p5TurboPro,
|
||||
Kling2p6Pro,
|
||||
Kling3p0Standard,
|
||||
Kling3p0Pro,
|
||||
HappyHorse1p0,
|
||||
Seedance10Lite,
|
||||
Seedance1p5Pro,
|
||||
Seedance2p0,
|
||||
Seedance2p0Fast,
|
||||
Seedance2p0BytePlus,
|
||||
Seedance2p0BytePlusFast,
|
||||
Seedance2p0Ultra,
|
||||
Seedance2p0UltraFast,
|
||||
Seedance2p0BytePlusUltra,
|
||||
Seedance2p0BytePlusUltraFast,
|
||||
Seedance2p0Mini,
|
||||
Seedance2p0BytePlusMini,
|
||||
Seedance2p0BytePlusUltraMini,
|
||||
Sora2,
|
||||
Sora2Pro,
|
||||
Veo2,
|
||||
Veo3,
|
||||
Veo3Fast,
|
||||
Veo3p1,
|
||||
Veo3p1Fast,
|
||||
PreviewModel,
|
||||
PreviewModelFast,
|
||||
/// A value not known to this client build (preserved as-is).
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<String> for ApiClientVideoModel {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"grok_video" => Self::GrokVideo,
|
||||
"grok_imagine_video" => Self::GrokImagineVideo,
|
||||
"grok_imagine_video_1p5" => Self::GrokImagineVideo1p5,
|
||||
"kling_1p6_pro" => Self::Kling16Pro,
|
||||
"kling_2p1_pro" => Self::Kling21Pro,
|
||||
"kling_2p1_master" => Self::Kling21Master,
|
||||
"kling_2p5_turbo_pro" => Self::Kling2p5TurboPro,
|
||||
"kling_2p6_pro" => Self::Kling2p6Pro,
|
||||
"kling_3p0_standard" => Self::Kling3p0Standard,
|
||||
"kling_3p0_pro" => Self::Kling3p0Pro,
|
||||
"happy_horse_1p0" => Self::HappyHorse1p0,
|
||||
"seedance_1p0_lite" => Self::Seedance10Lite,
|
||||
"seedance_1p5_pro" => Self::Seedance1p5Pro,
|
||||
"seedance_2p0" => Self::Seedance2p0,
|
||||
"seedance_2p0_fast" => Self::Seedance2p0Fast,
|
||||
"seedance_2p0_bp" => Self::Seedance2p0BytePlus,
|
||||
"seedance_2p0_bp_fast" => Self::Seedance2p0BytePlusFast,
|
||||
"seedance_2p0_u" => Self::Seedance2p0Ultra,
|
||||
"seedance_2p0_u_fast" => Self::Seedance2p0UltraFast,
|
||||
"seedance_2p0_bpu" => Self::Seedance2p0BytePlusUltra,
|
||||
"seedance_2p0_bpu_fast" => Self::Seedance2p0BytePlusUltraFast,
|
||||
"seedance_2p0_mini" => Self::Seedance2p0Mini,
|
||||
"seedance_2p0_bp_mini" => Self::Seedance2p0BytePlusMini,
|
||||
"seedance_2p0_bpu_mini" => Self::Seedance2p0BytePlusUltraMini,
|
||||
"sora_2" => Self::Sora2,
|
||||
"sora_2_pro" => Self::Sora2Pro,
|
||||
"veo_2" => Self::Veo2,
|
||||
"veo_3" => Self::Veo3,
|
||||
"veo_3_fast" => Self::Veo3Fast,
|
||||
"veo_3p1" => Self::Veo3p1,
|
||||
"veo_3p1_fast" => Self::Veo3p1Fast,
|
||||
"preview_model" => Self::PreviewModel,
|
||||
"preview_model_fast" => Self::PreviewModelFast,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApiClientVideoModel> for String {
|
||||
fn from(value: ApiClientVideoModel) -> Self {
|
||||
match value {
|
||||
ApiClientVideoModel::GrokVideo => "grok_video".to_string(),
|
||||
ApiClientVideoModel::GrokImagineVideo => "grok_imagine_video".to_string(),
|
||||
ApiClientVideoModel::GrokImagineVideo1p5 => "grok_imagine_video_1p5".to_string(),
|
||||
ApiClientVideoModel::Kling16Pro => "kling_1p6_pro".to_string(),
|
||||
ApiClientVideoModel::Kling21Pro => "kling_2p1_pro".to_string(),
|
||||
ApiClientVideoModel::Kling21Master => "kling_2p1_master".to_string(),
|
||||
ApiClientVideoModel::Kling2p5TurboPro => "kling_2p5_turbo_pro".to_string(),
|
||||
ApiClientVideoModel::Kling2p6Pro => "kling_2p6_pro".to_string(),
|
||||
ApiClientVideoModel::Kling3p0Standard => "kling_3p0_standard".to_string(),
|
||||
ApiClientVideoModel::Kling3p0Pro => "kling_3p0_pro".to_string(),
|
||||
ApiClientVideoModel::HappyHorse1p0 => "happy_horse_1p0".to_string(),
|
||||
ApiClientVideoModel::Seedance10Lite => "seedance_1p0_lite".to_string(),
|
||||
ApiClientVideoModel::Seedance1p5Pro => "seedance_1p5_pro".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0 => "seedance_2p0".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0Fast => "seedance_2p0_fast".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlus => "seedance_2p0_bp".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlusFast => "seedance_2p0_bp_fast".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0Ultra => "seedance_2p0_u".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0UltraFast => "seedance_2p0_u_fast".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltra => "seedance_2p0_bpu".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltraFast => "seedance_2p0_bpu_fast".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0Mini => "seedance_2p0_mini".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlusMini => "seedance_2p0_bp_mini".to_string(),
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltraMini => "seedance_2p0_bpu_mini".to_string(),
|
||||
ApiClientVideoModel::Sora2 => "sora_2".to_string(),
|
||||
ApiClientVideoModel::Sora2Pro => "sora_2_pro".to_string(),
|
||||
ApiClientVideoModel::Veo2 => "veo_2".to_string(),
|
||||
ApiClientVideoModel::Veo3 => "veo_3".to_string(),
|
||||
ApiClientVideoModel::Veo3Fast => "veo_3_fast".to_string(),
|
||||
ApiClientVideoModel::Veo3p1 => "veo_3p1".to_string(),
|
||||
ApiClientVideoModel::Veo3p1Fast => "veo_3p1_fast".to_string(),
|
||||
ApiClientVideoModel::PreviewModel => "preview_model".to_string(),
|
||||
ApiClientVideoModel::PreviewModelFast => "preview_model_fast".to_string(),
|
||||
ApiClientVideoModel::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn known_variant_round_trips() {
|
||||
let parsed: ApiClientVideoModel = serde_json::from_str("\"grok_video\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientVideoModel::GrokVideo);
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"grok_video\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_value_is_preserved_and_round_trips() {
|
||||
let parsed: ApiClientVideoModel = serde_json::from_str("\"zzz_not_a_real_value\"").unwrap();
|
||||
assert_eq!(parsed, ApiClientVideoModel::Unknown("zzz_not_a_real_value".to_string()));
|
||||
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"zzz_not_a_real_value\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_known_variant_round_trips() {
|
||||
let all = [
|
||||
(ApiClientVideoModel::GrokVideo, "grok_video"),
|
||||
(ApiClientVideoModel::GrokImagineVideo, "grok_imagine_video"),
|
||||
(ApiClientVideoModel::GrokImagineVideo1p5, "grok_imagine_video_1p5"),
|
||||
(ApiClientVideoModel::Kling16Pro, "kling_1p6_pro"),
|
||||
(ApiClientVideoModel::Kling21Pro, "kling_2p1_pro"),
|
||||
(ApiClientVideoModel::Kling21Master, "kling_2p1_master"),
|
||||
(ApiClientVideoModel::Kling2p5TurboPro, "kling_2p5_turbo_pro"),
|
||||
(ApiClientVideoModel::Kling2p6Pro, "kling_2p6_pro"),
|
||||
(ApiClientVideoModel::Kling3p0Standard, "kling_3p0_standard"),
|
||||
(ApiClientVideoModel::Kling3p0Pro, "kling_3p0_pro"),
|
||||
(ApiClientVideoModel::HappyHorse1p0, "happy_horse_1p0"),
|
||||
(ApiClientVideoModel::Seedance10Lite, "seedance_1p0_lite"),
|
||||
(ApiClientVideoModel::Seedance1p5Pro, "seedance_1p5_pro"),
|
||||
(ApiClientVideoModel::Seedance2p0, "seedance_2p0"),
|
||||
(ApiClientVideoModel::Seedance2p0Fast, "seedance_2p0_fast"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlus, "seedance_2p0_bp"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlusFast, "seedance_2p0_bp_fast"),
|
||||
(ApiClientVideoModel::Seedance2p0Ultra, "seedance_2p0_u"),
|
||||
(ApiClientVideoModel::Seedance2p0UltraFast, "seedance_2p0_u_fast"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlusUltra, "seedance_2p0_bpu"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlusUltraFast, "seedance_2p0_bpu_fast"),
|
||||
(ApiClientVideoModel::Seedance2p0Mini, "seedance_2p0_mini"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlusMini, "seedance_2p0_bp_mini"),
|
||||
(ApiClientVideoModel::Seedance2p0BytePlusUltraMini, "seedance_2p0_bpu_mini"),
|
||||
(ApiClientVideoModel::Sora2, "sora_2"),
|
||||
(ApiClientVideoModel::Sora2Pro, "sora_2_pro"),
|
||||
(ApiClientVideoModel::Veo2, "veo_2"),
|
||||
(ApiClientVideoModel::Veo3, "veo_3"),
|
||||
(ApiClientVideoModel::Veo3Fast, "veo_3_fast"),
|
||||
(ApiClientVideoModel::Veo3p1, "veo_3p1"),
|
||||
(ApiClientVideoModel::Veo3p1Fast, "veo_3p1_fast"),
|
||||
(ApiClientVideoModel::PreviewModel, "preview_model"),
|
||||
(ApiClientVideoModel::PreviewModelFast, "preview_model_fast"),
|
||||
];
|
||||
for (variant, s) in all {
|
||||
assert_eq!(String::from(variant.clone()), s, "serialize mismatch for {:?}", variant);
|
||||
let parsed: ApiClientVideoModel = serde_json::from_str(&format!("\"{}\"", s)).unwrap();
|
||||
assert_eq!(parsed, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
pub mod api_client_aspect_ratio;
|
||||
pub mod api_client_bitrate;
|
||||
pub mod api_client_generation_provider;
|
||||
pub mod api_client_image_model;
|
||||
pub mod api_client_model_creator;
|
||||
pub mod api_client_quality;
|
||||
pub mod api_client_resolution;
|
||||
pub mod api_client_video_model;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod generation;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod common_enums;
|
||||
@@ -1 +1,2 @@
|
||||
pub mod generate;
|
||||
pub mod models;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod omni_gen_list_image_models;
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
//! Self-contained, forward-compatible binding for `GET /v1/omni_gen/models/image`.
|
||||
//!
|
||||
//! IMPORTANT: This module deliberately does NOT reuse any types from
|
||||
//! `artcraft_api_defs`. Client builds may be deployed in the wild long after the
|
||||
//! server's models, enums, and response shapes have changed, so every request /
|
||||
//! response type is copied here and made permissive:
|
||||
//! - Unknown JSON fields are ignored (serde default behavior).
|
||||
//! - Missing collection/flag fields default rather than fail (`serde(default)`).
|
||||
//! - Every string-valued enum has an `Unknown(String)` catch-all so new server
|
||||
//! variants deserialize instead of erroring.
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::datatypes::common_enums::generation::api_client_aspect_ratio::ApiClientAspectRatio;
|
||||
use crate::datatypes::common_enums::generation::api_client_generation_provider::ApiClientGenerationProvider;
|
||||
use crate::datatypes::common_enums::generation::api_client_image_model::ApiClientImageModel;
|
||||
use crate::datatypes::common_enums::generation::api_client_model_creator::ApiClientModelCreator;
|
||||
use crate::datatypes::common_enums::generation::api_client_quality::ApiClientQuality;
|
||||
use crate::datatypes::common_enums::generation::api_client_resolution::ApiClientResolution;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_get_request::basic_json_get_request;
|
||||
|
||||
pub const OMNI_GEN_IMAGE_MODELS_PATH: &str = "/v1/omni_gen/models/image";
|
||||
|
||||
/// Arguments for [`omni_gen_list_image_models`].
|
||||
pub struct OmniGenListImageModelsArgs<'a> {
|
||||
pub api_host: &'a ApiHost,
|
||||
pub maybe_creds: Option<&'a StorytellerCredentialSet>,
|
||||
/// Which provider's models to list. `None` lets the server default (artcraft).
|
||||
pub provider: Option<OmniGenImageModelsProvider>,
|
||||
}
|
||||
|
||||
/// List available image models.
|
||||
pub async fn omni_gen_list_image_models(
|
||||
args: OmniGenListImageModelsArgs<'_>,
|
||||
) -> Result<OmniGenImageModelsResponse, StorytellerError> {
|
||||
let path = match args.provider {
|
||||
Some(provider) => format!("{}?provider={}", OMNI_GEN_IMAGE_MODELS_PATH, provider.as_query_value()),
|
||||
None => OMNI_GEN_IMAGE_MODELS_PATH.to_string(),
|
||||
};
|
||||
|
||||
Ok(basic_json_get_request(args.api_host, &path, args.maybe_creds).await?)
|
||||
}
|
||||
|
||||
/// The provider filter for the models endpoint (a client-supplied request value).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OmniGenImageModelsProvider {
|
||||
/// Only models available through ArtCraft.
|
||||
Artcraft,
|
||||
/// All known models across all providers.
|
||||
All,
|
||||
}
|
||||
|
||||
impl OmniGenImageModelsProvider {
|
||||
pub fn as_query_value(self) -> &'static str {
|
||||
match self {
|
||||
Self::Artcraft => "artcraft",
|
||||
Self::All => "all",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OmniGenImageModelsProvider {
|
||||
fn default() -> Self {
|
||||
Self::Artcraft
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Response types ============================
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenImageModelsResponse {
|
||||
#[serde(default)]
|
||||
pub success: bool,
|
||||
#[serde(default)]
|
||||
pub models: Vec<OmniGenImageModelDetails>,
|
||||
#[serde(default)]
|
||||
pub providers: Vec<OmniGenImageModelProviderDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenImageModelProviderDetails {
|
||||
pub provider: ApiClientGenerationProvider,
|
||||
#[serde(default)]
|
||||
pub models: Vec<OmniGenImageProviderModelDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenImageProviderModelDetails {
|
||||
pub model: ApiClientImageModel,
|
||||
#[serde(default)]
|
||||
pub overrides: Option<OmniGenImageModelDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenImageModelDetails {
|
||||
pub model: ApiClientImageModel,
|
||||
#[serde(default)]
|
||||
pub model_creator: Option<ApiClientModelCreator>,
|
||||
#[serde(default)]
|
||||
pub full_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub text_prompt_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub text_prompt_max_length: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub negative_text_prompt_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub negative_text_prompt_max_length: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub image_refs_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub image_refs_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub has_fixed_editing_aspect_ratio: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub aspect_ratio_options: Option<Vec<ApiClientAspectRatio>>,
|
||||
#[serde(default)]
|
||||
pub aspect_ratio_default: Option<ApiClientAspectRatio>,
|
||||
#[serde(default)]
|
||||
pub aspect_ratio_default_when_editing: Option<ApiClientAspectRatio>,
|
||||
#[serde(default)]
|
||||
pub resolution_options: Option<Vec<ApiClientResolution>>,
|
||||
#[serde(default)]
|
||||
pub resolution_default: Option<ApiClientResolution>,
|
||||
#[serde(default)]
|
||||
pub quality_options: Option<Vec<ApiClientQuality>>,
|
||||
#[serde(default)]
|
||||
pub default_quality: Option<ApiClientQuality>,
|
||||
#[serde(default)]
|
||||
pub batch_size_min: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub batch_size_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub batch_size_options: Option<Vec<u16>>,
|
||||
#[serde(default)]
|
||||
pub batch_size_default: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub is_disabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn response_tolerates_unknown_fields_and_variants() {
|
||||
let json = r#"{
|
||||
"success": true,
|
||||
"models": [
|
||||
{
|
||||
"model": "brand_new_image_model",
|
||||
"model_creator": "some_new_studio",
|
||||
"aspect_ratio_options": ["square", "some_new_ratio"],
|
||||
"surprise_new_field": 123
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{ "provider": "new_provider", "models": [ { "model": "nano_banana_pro" } ] }
|
||||
],
|
||||
"another_unexpected_top_level_field": "ignored"
|
||||
}"#;
|
||||
|
||||
let resp: OmniGenImageModelsResponse = serde_json::from_str(json).unwrap();
|
||||
assert!(resp.success);
|
||||
assert_eq!(resp.models.len(), 1);
|
||||
assert_eq!(resp.models[0].model, ApiClientImageModel::Unknown("brand_new_image_model".to_string()));
|
||||
assert_eq!(resp.models[0].model_creator, Some(ApiClientModelCreator::Unknown("some_new_studio".to_string())));
|
||||
let ratios = resp.models[0].aspect_ratio_options.as_ref().unwrap();
|
||||
assert_eq!(ratios[0], ApiClientAspectRatio::Square);
|
||||
assert_eq!(ratios[1], ApiClientAspectRatio::Unknown("some_new_ratio".to_string()));
|
||||
assert_eq!(resp.models[0].is_disabled, None);
|
||||
assert_eq!(resp.providers[0].provider, ApiClientGenerationProvider::Unknown("new_provider".to_string()));
|
||||
assert_eq!(resp.providers[0].models[0].model, ApiClientImageModel::NanoBananaPro);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_object_uses_defaults() {
|
||||
let resp: OmniGenImageModelsResponse = serde_json::from_str("{}").unwrap();
|
||||
assert!(!resp.success);
|
||||
assert!(resp.models.is_empty());
|
||||
assert!(resp.providers.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Live tests that hit real servers. All `#[ignore]` so they never run by default.
|
||||
/// Run explicitly, e.g.:
|
||||
/// cargo test -p artcraft_client omni_gen_list_image_models::live_tests -- --ignored --nocapture
|
||||
#[cfg(test)]
|
||||
mod live_tests {
|
||||
mod localhost {
|
||||
use super::super::{omni_gen_list_image_models, OmniGenListImageModelsArgs};
|
||||
use crate::utils::api_host::ApiHost;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // live: hits http://localhost:12345
|
||||
async fn list_image_models() {
|
||||
let api_host = ApiHost::Localhost { port: 12345 };
|
||||
let response = omni_gen_list_image_models(OmniGenListImageModelsArgs {
|
||||
api_host: &api_host,
|
||||
maybe_creds: None,
|
||||
provider: None,
|
||||
})
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
println!("[localhost] image models response: {:#?}", response);
|
||||
assert!(response.success);
|
||||
assert!(!response.models.is_empty(), "expected at least one image model");
|
||||
}
|
||||
}
|
||||
|
||||
mod production {
|
||||
use super::super::{omni_gen_list_image_models, OmniGenListImageModelsArgs};
|
||||
use crate::utils::api_host::ApiHost;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // live: hits https://api.storyteller.ai
|
||||
async fn list_image_models() {
|
||||
let api_host = ApiHost::Storyteller;
|
||||
let response = omni_gen_list_image_models(OmniGenListImageModelsArgs {
|
||||
api_host: &api_host,
|
||||
maybe_creds: None,
|
||||
provider: None,
|
||||
})
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
println!("[production] image models response: {:#?}", response);
|
||||
assert!(response.success);
|
||||
assert!(!response.models.is_empty(), "expected at least one image model");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod image;
|
||||
pub mod video;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod omni_gen_list_video_models;
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
//! Self-contained, forward-compatible binding for `GET /v1/omni_gen/models/video`.
|
||||
//!
|
||||
//! IMPORTANT: This module deliberately does NOT reuse any types from
|
||||
//! `artcraft_api_defs`. Client builds may be deployed in the wild long after the
|
||||
//! server's models, enums, and response shapes have changed, so every request /
|
||||
//! response type is copied here and made permissive:
|
||||
//! - Unknown JSON fields are ignored (serde default behavior).
|
||||
//! - Missing collection/flag fields default rather than fail (`serde(default)`).
|
||||
//! - Every string-valued enum has an `Unknown(String)` catch-all so new server
|
||||
//! variants deserialize instead of erroring.
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_get_request::basic_json_get_request;
|
||||
use crate::datatypes::common_enums::generation::api_client_aspect_ratio::ApiClientAspectRatio;
|
||||
use crate::datatypes::common_enums::generation::api_client_bitrate::ApiClientBitrate;
|
||||
use crate::datatypes::common_enums::generation::api_client_generation_provider::ApiClientGenerationProvider;
|
||||
use crate::datatypes::common_enums::generation::api_client_model_creator::ApiClientModelCreator;
|
||||
use crate::datatypes::common_enums::generation::api_client_quality::ApiClientQuality;
|
||||
use crate::datatypes::common_enums::generation::api_client_resolution::ApiClientResolution;
|
||||
use crate::datatypes::common_enums::generation::api_client_video_model::ApiClientVideoModel;
|
||||
|
||||
pub const OMNI_GEN_VIDEO_MODELS_PATH: &str = "/v1/omni_gen/models/video";
|
||||
|
||||
/// Arguments for [`omni_gen_list_video_models`].
|
||||
pub struct OmniGenListVideoModelsArgs<'a> {
|
||||
pub api_host: &'a ApiHost,
|
||||
pub maybe_creds: Option<&'a StorytellerCredentialSet>,
|
||||
/// Which provider's models to list. `None` lets the server default (artcraft).
|
||||
pub provider: Option<OmniGenVideoModelsProvider>,
|
||||
}
|
||||
|
||||
/// List available video models.
|
||||
pub async fn omni_gen_list_video_models(
|
||||
args: OmniGenListVideoModelsArgs<'_>,
|
||||
) -> Result<OmniGenVideoModelsResponse, StorytellerError> {
|
||||
let path = match args.provider {
|
||||
Some(provider) => format!("{}?provider={}", OMNI_GEN_VIDEO_MODELS_PATH, provider.as_query_value()),
|
||||
None => OMNI_GEN_VIDEO_MODELS_PATH.to_string(),
|
||||
};
|
||||
|
||||
Ok(basic_json_get_request(args.api_host, &path, args.maybe_creds).await?)
|
||||
}
|
||||
|
||||
/// The provider filter for the models endpoint (a client-supplied request value).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OmniGenVideoModelsProvider {
|
||||
/// Only models available through ArtCraft.
|
||||
Artcraft,
|
||||
/// All known models across all providers.
|
||||
All,
|
||||
}
|
||||
|
||||
impl OmniGenVideoModelsProvider {
|
||||
pub fn as_query_value(self) -> &'static str {
|
||||
match self {
|
||||
Self::Artcraft => "artcraft",
|
||||
Self::All => "all",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OmniGenVideoModelsProvider {
|
||||
fn default() -> Self {
|
||||
Self::Artcraft
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Response types ============================
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenVideoModelsResponse {
|
||||
#[serde(default)]
|
||||
pub success: bool,
|
||||
#[serde(default)]
|
||||
pub models: Vec<OmniGenVideoModelDetails>,
|
||||
#[serde(default)]
|
||||
pub providers: Vec<OmniGenVideoModelProviderDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenVideoModelProviderDetails {
|
||||
pub provider: ApiClientGenerationProvider,
|
||||
#[serde(default)]
|
||||
pub models: Vec<OmniGenVideoProviderModelDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenVideoProviderModelDetails {
|
||||
pub model: ApiClientVideoModel,
|
||||
#[serde(default)]
|
||||
pub overrides: Option<OmniGenVideoModelDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OmniGenVideoModelDetails {
|
||||
pub model: ApiClientVideoModel,
|
||||
#[serde(default)]
|
||||
pub model_creator: Option<ApiClientModelCreator>,
|
||||
#[serde(default)]
|
||||
pub full_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub extra_info: Option<String>,
|
||||
#[serde(default)]
|
||||
pub extra_info_short: Option<String>,
|
||||
#[serde(default)]
|
||||
pub text_to_video_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub text_prompt_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub text_prompt_max_length: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub negative_text_prompt_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub negative_text_prompt_max_length: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub starting_keyframe_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub starting_keyframe_required: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub ending_keyframe_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub image_references_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub image_references_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub video_references_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub video_references_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub video_references_max_total_duration_seconds: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub audio_references_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub audio_references_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub audio_references_max_total_duration_seconds: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub character_references_supported: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub character_references_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub show_generate_with_sound_toggle: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub aspect_ratio_options: Option<Vec<ApiClientAspectRatio>>,
|
||||
#[serde(default)]
|
||||
pub aspect_ratio_default: Option<ApiClientAspectRatio>,
|
||||
#[serde(default)]
|
||||
pub resolution_options: Option<Vec<ApiClientResolution>>,
|
||||
#[serde(default)]
|
||||
pub resolution_default: Option<ApiClientResolution>,
|
||||
#[serde(default)]
|
||||
pub bitrate_options: Option<Vec<ApiClientBitrate>>,
|
||||
#[serde(default)]
|
||||
pub bitrate_default: Option<ApiClientBitrate>,
|
||||
#[serde(default)]
|
||||
pub quality_options: Option<Vec<ApiClientQuality>>,
|
||||
#[serde(default)]
|
||||
pub default_quality: Option<ApiClientQuality>,
|
||||
#[serde(default)]
|
||||
pub duration_seconds_min: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub duration_seconds_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub duration_seconds_max_with_image_references: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub duration_seconds_options: Option<Vec<u16>>,
|
||||
#[serde(default)]
|
||||
pub duration_seconds_default: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub batch_size_min: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub batch_size_max: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub batch_size_options: Option<Vec<u16>>,
|
||||
#[serde(default)]
|
||||
pub batch_size_default: Option<u16>,
|
||||
#[serde(default)]
|
||||
pub is_disabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unknown_enum_value_is_preserved_and_round_trips() {
|
||||
let known: ApiClientVideoModel = serde_json::from_str("\"seedance_2p0\"").unwrap();
|
||||
assert_eq!(known, ApiClientVideoModel::Seedance2p0);
|
||||
assert_eq!(serde_json::to_string(&known).unwrap(), "\"seedance_2p0\"");
|
||||
|
||||
// A value this build has never heard of does NOT fail — it is captured verbatim.
|
||||
let unknown: ApiClientVideoModel = serde_json::from_str("\"future_model_9000\"").unwrap();
|
||||
assert_eq!(unknown, ApiClientVideoModel::Unknown("future_model_9000".to_string()));
|
||||
// ...and serializes back to exactly what came in.
|
||||
assert_eq!(serde_json::to_string(&unknown).unwrap(), "\"future_model_9000\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_tolerates_unknown_fields_and_variants() {
|
||||
let json = r#"{
|
||||
"success": true,
|
||||
"models": [
|
||||
{
|
||||
"model": "brand_new_model",
|
||||
"model_creator": "some_new_studio",
|
||||
"aspect_ratio_options": ["wide_sixteen_by_nine", "some_new_ratio"],
|
||||
"surprise_new_field": 123
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{ "provider": "new_provider", "models": [ { "model": "seedance_2p0" } ] }
|
||||
],
|
||||
"another_unexpected_top_level_field": "ignored"
|
||||
}"#;
|
||||
|
||||
let resp: OmniGenVideoModelsResponse = serde_json::from_str(json).unwrap();
|
||||
assert!(resp.success);
|
||||
assert_eq!(resp.models.len(), 1);
|
||||
assert_eq!(resp.models[0].model, ApiClientVideoModel::Unknown("brand_new_model".to_string()));
|
||||
assert_eq!(resp.models[0].model_creator, Some(ApiClientModelCreator::Unknown("some_new_studio".to_string())));
|
||||
let ratios = resp.models[0].aspect_ratio_options.as_ref().unwrap();
|
||||
assert_eq!(ratios[0], ApiClientAspectRatio::WideSixteenByNine);
|
||||
assert_eq!(ratios[1], ApiClientAspectRatio::Unknown("some_new_ratio".to_string()));
|
||||
assert_eq!(resp.models[0].is_disabled, None); // missing optional -> None
|
||||
assert_eq!(resp.providers[0].provider, ApiClientGenerationProvider::Unknown("new_provider".to_string()));
|
||||
assert_eq!(resp.providers[0].models[0].model, ApiClientVideoModel::Seedance2p0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_object_uses_defaults() {
|
||||
let resp: OmniGenVideoModelsResponse = serde_json::from_str("{}").unwrap();
|
||||
assert!(!resp.success);
|
||||
assert!(resp.models.is_empty());
|
||||
assert!(resp.providers.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Live tests that hit real servers. All `#[ignore]` so they never run by default.
|
||||
/// Run explicitly, e.g.:
|
||||
/// cargo test -p artcraft_client omni_gen_list_video_models::live_tests -- --ignored --nocapture
|
||||
#[cfg(test)]
|
||||
mod live_tests {
|
||||
mod localhost {
|
||||
use super::super::{omni_gen_list_video_models, OmniGenListVideoModelsArgs};
|
||||
use crate::utils::api_host::ApiHost;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // live: hits http://localhost:12345
|
||||
async fn list_video_models() {
|
||||
let api_host = ApiHost::Localhost { port: 12345 };
|
||||
let response = omni_gen_list_video_models(OmniGenListVideoModelsArgs {
|
||||
api_host: &api_host,
|
||||
maybe_creds: None,
|
||||
provider: None,
|
||||
})
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
println!("[localhost] video models response: {:#?}", response);
|
||||
assert!(response.success);
|
||||
assert!(!response.models.is_empty(), "expected at least one video model");
|
||||
}
|
||||
}
|
||||
|
||||
mod production {
|
||||
use super::super::{omni_gen_list_video_models, OmniGenListVideoModelsArgs};
|
||||
use crate::utils::api_host::ApiHost;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // live: hits https://api.storyteller.ai
|
||||
async fn list_video_models() {
|
||||
let api_host = ApiHost::Storyteller;
|
||||
let response = omni_gen_list_video_models(OmniGenListVideoModelsArgs {
|
||||
api_host: &api_host,
|
||||
maybe_creds: None,
|
||||
provider: None,
|
||||
})
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
println!("[production] video models response: {:#?}", response);
|
||||
assert!(response.success);
|
||||
assert!(!response.models.is_empty(), "expected at least one video model");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! An API client for Artcraft / Storyteller / FakeYou
|
||||
|
||||
pub mod credentials;
|
||||
pub mod datatypes;
|
||||
pub mod endpoints;
|
||||
pub mod error;
|
||||
pub mod recipes;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod generate_image;
|
||||
pub mod generate_video;
|
||||
pub mod common;
|
||||
pub mod models;
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
//! Tauri command: list available image models.
|
||||
//!
|
||||
//! Maps the API-client response into command-specific types (see `response_types`),
|
||||
//! retries the request up to `MAX_ATTEMPTS` times, and caches successful responses
|
||||
//! in-memory for `CACHE_TTL`.
|
||||
|
||||
use std::sync::RwLock;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, warn};
|
||||
use once_cell::sync::Lazy;
|
||||
use tauri::State;
|
||||
|
||||
use artcraft_client::endpoints::omni_gen::models::image::omni_gen_list_image_models::{omni_gen_list_image_models, OmniGenListImageModelsArgs, OmniGenImageModelsResponse};
|
||||
use artcraft_client::utils::api_host::ApiHost;
|
||||
|
||||
use crate::core::commands::generate::models::image::response_types::ListImageModelsResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
const MAX_ATTEMPTS: u32 = 3;
|
||||
const RETRY_BACKOFF: Duration = Duration::from_millis(250);
|
||||
const CACHE_TTL: Duration = Duration::from_secs(60);
|
||||
|
||||
struct CacheEntry {
|
||||
response: ListImageModelsResponse,
|
||||
loaded_at: Instant,
|
||||
}
|
||||
|
||||
static CACHE: Lazy<RwLock<Option<CacheEntry>>> = Lazy::new(|| RwLock::new(None));
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_image_models_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
) -> ResponseOrErrorMessage<ListImageModelsResponse> {
|
||||
if let Some(cached) = cached_response() {
|
||||
debug!("list_image_models_command: serving cached response");
|
||||
return Ok(cached.into());
|
||||
}
|
||||
|
||||
match fetch_with_retry(&app_env_configs.storyteller_host).await {
|
||||
Ok(api_response) => {
|
||||
let response: ListImageModelsResponse = api_response.into();
|
||||
store_response(response.clone());
|
||||
Ok(response.into())
|
||||
}
|
||||
Err(error_message) => {
|
||||
warn!("list_image_models_command failed after {} attempts: {}", MAX_ATTEMPTS, error_message);
|
||||
if let Some(stale) = any_cached_response() {
|
||||
warn!("list_image_models_command: refresh failed; serving stale cached response");
|
||||
return Ok(stale.into());
|
||||
}
|
||||
Err(error_message.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cached_response() -> Option<ListImageModelsResponse> {
|
||||
let guard = CACHE.read().ok()?;
|
||||
let entry = guard.as_ref()?;
|
||||
if entry.loaded_at.elapsed() < CACHE_TTL {
|
||||
Some(entry.response.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The cached response regardless of age. Used as a fallback so that a failed
|
||||
/// refresh still returns the last-known-good data instead of an error.
|
||||
fn any_cached_response() -> Option<ListImageModelsResponse> {
|
||||
let guard = CACHE.read().ok()?;
|
||||
guard.as_ref().map(|entry| entry.response.clone())
|
||||
}
|
||||
|
||||
fn store_response(response: ListImageModelsResponse) {
|
||||
if let Ok(mut guard) = CACHE.write() {
|
||||
*guard = Some(CacheEntry { response, loaded_at: Instant::now() });
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the models list, retrying up to `MAX_ATTEMPTS` times on failure.
|
||||
async fn fetch_with_retry(api_host: &ApiHost) -> Result<OmniGenImageModelsResponse, String> {
|
||||
let mut last_error = "unknown error".to_string();
|
||||
for attempt in 1..=MAX_ATTEMPTS {
|
||||
match omni_gen_list_image_models(OmniGenListImageModelsArgs { api_host, maybe_creds: None, provider: None }).await {
|
||||
Ok(response) => return Ok(response),
|
||||
Err(err) => {
|
||||
last_error = err.to_string();
|
||||
warn!("list_image_models attempt {}/{} failed: {}", attempt, MAX_ATTEMPTS, last_error);
|
||||
if attempt < MAX_ATTEMPTS {
|
||||
tokio::time::sleep(RETRY_BACKOFF).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(last_error)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod list_image_models_command;
|
||||
pub mod response_types;
|
||||
@@ -0,0 +1,524 @@
|
||||
//! Command-specific response types for `list_image_models_command`.
|
||||
//!
|
||||
//! These are duplicated (never `artcraft_client` types), and every enum has an
|
||||
//! `Unknown(String)` catch-all so newer server variants are preserved instead of
|
||||
//! breaking an older desktop build. `From` impls map the API-client response over.
|
||||
|
||||
use serde_derive::Serialize;
|
||||
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_generation_provider::ApiClientGenerationProvider;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_model_creator::ApiClientModelCreator;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_image_model::ApiClientImageModel;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_aspect_ratio::ApiClientAspectRatio;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_resolution::ApiClientResolution;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_quality::ApiClientQuality;
|
||||
use artcraft_client::endpoints::omni_gen::models::image::omni_gen_list_image_models::{
|
||||
OmniGenImageModelDetails, OmniGenImageModelProviderDetails, OmniGenImageProviderModelDetails, OmniGenImageModelsResponse,
|
||||
};
|
||||
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
|
||||
impl SerializeMarker for ListImageModelsResponse {}
|
||||
|
||||
// ============================ Enums ============================
|
||||
|
||||
/// Command-specific copy of `ApiClientGenerationProvider` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsProvider {
|
||||
Artcraft,
|
||||
Fal,
|
||||
Grok,
|
||||
Midjourney,
|
||||
Sora,
|
||||
WorldLabs,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientGenerationProvider> for ListImageModelsProvider {
|
||||
fn from(value: ApiClientGenerationProvider) -> Self {
|
||||
match value {
|
||||
ApiClientGenerationProvider::Artcraft => Self::Artcraft,
|
||||
ApiClientGenerationProvider::Fal => Self::Fal,
|
||||
ApiClientGenerationProvider::Grok => Self::Grok,
|
||||
ApiClientGenerationProvider::Midjourney => Self::Midjourney,
|
||||
ApiClientGenerationProvider::Sora => Self::Sora,
|
||||
ApiClientGenerationProvider::WorldLabs => Self::WorldLabs,
|
||||
ApiClientGenerationProvider::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsProvider> for String {
|
||||
fn from(value: ListImageModelsProvider) -> Self {
|
||||
match value {
|
||||
ListImageModelsProvider::Artcraft => "artcraft".to_string(),
|
||||
ListImageModelsProvider::Fal => "fal".to_string(),
|
||||
ListImageModelsProvider::Grok => "grok".to_string(),
|
||||
ListImageModelsProvider::Midjourney => "midjourney".to_string(),
|
||||
ListImageModelsProvider::Sora => "sora".to_string(),
|
||||
ListImageModelsProvider::WorldLabs => "world_labs".to_string(),
|
||||
ListImageModelsProvider::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientModelCreator` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsModelCreator {
|
||||
Alibaba,
|
||||
ArtCraft,
|
||||
BlackForestLabs,
|
||||
Bytedance,
|
||||
Fal,
|
||||
Google,
|
||||
Grok,
|
||||
Hailuo,
|
||||
Higgsfield,
|
||||
Kling,
|
||||
Krea,
|
||||
Midjourney,
|
||||
OpenAi,
|
||||
OpenArt,
|
||||
Recraft,
|
||||
Replicate,
|
||||
Runway,
|
||||
Stability,
|
||||
Tencent,
|
||||
TensorArt,
|
||||
Vidu,
|
||||
WorldLabs,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientModelCreator> for ListImageModelsModelCreator {
|
||||
fn from(value: ApiClientModelCreator) -> Self {
|
||||
match value {
|
||||
ApiClientModelCreator::Alibaba => Self::Alibaba,
|
||||
ApiClientModelCreator::ArtCraft => Self::ArtCraft,
|
||||
ApiClientModelCreator::BlackForestLabs => Self::BlackForestLabs,
|
||||
ApiClientModelCreator::Bytedance => Self::Bytedance,
|
||||
ApiClientModelCreator::Fal => Self::Fal,
|
||||
ApiClientModelCreator::Google => Self::Google,
|
||||
ApiClientModelCreator::Grok => Self::Grok,
|
||||
ApiClientModelCreator::Hailuo => Self::Hailuo,
|
||||
ApiClientModelCreator::Higgsfield => Self::Higgsfield,
|
||||
ApiClientModelCreator::Kling => Self::Kling,
|
||||
ApiClientModelCreator::Krea => Self::Krea,
|
||||
ApiClientModelCreator::Midjourney => Self::Midjourney,
|
||||
ApiClientModelCreator::OpenAi => Self::OpenAi,
|
||||
ApiClientModelCreator::OpenArt => Self::OpenArt,
|
||||
ApiClientModelCreator::Recraft => Self::Recraft,
|
||||
ApiClientModelCreator::Replicate => Self::Replicate,
|
||||
ApiClientModelCreator::Runway => Self::Runway,
|
||||
ApiClientModelCreator::Stability => Self::Stability,
|
||||
ApiClientModelCreator::Tencent => Self::Tencent,
|
||||
ApiClientModelCreator::TensorArt => Self::TensorArt,
|
||||
ApiClientModelCreator::Vidu => Self::Vidu,
|
||||
ApiClientModelCreator::WorldLabs => Self::WorldLabs,
|
||||
ApiClientModelCreator::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsModelCreator> for String {
|
||||
fn from(value: ListImageModelsModelCreator) -> Self {
|
||||
match value {
|
||||
ListImageModelsModelCreator::Alibaba => "alibaba".to_string(),
|
||||
ListImageModelsModelCreator::ArtCraft => "artcraft".to_string(),
|
||||
ListImageModelsModelCreator::BlackForestLabs => "black_forest_labs".to_string(),
|
||||
ListImageModelsModelCreator::Bytedance => "bytedance".to_string(),
|
||||
ListImageModelsModelCreator::Fal => "fal".to_string(),
|
||||
ListImageModelsModelCreator::Google => "google".to_string(),
|
||||
ListImageModelsModelCreator::Grok => "grok".to_string(),
|
||||
ListImageModelsModelCreator::Hailuo => "hailuo".to_string(),
|
||||
ListImageModelsModelCreator::Higgsfield => "higgsfield".to_string(),
|
||||
ListImageModelsModelCreator::Kling => "kling".to_string(),
|
||||
ListImageModelsModelCreator::Krea => "krea".to_string(),
|
||||
ListImageModelsModelCreator::Midjourney => "midjourney".to_string(),
|
||||
ListImageModelsModelCreator::OpenAi => "open_ai".to_string(),
|
||||
ListImageModelsModelCreator::OpenArt => "open_art".to_string(),
|
||||
ListImageModelsModelCreator::Recraft => "recraft".to_string(),
|
||||
ListImageModelsModelCreator::Replicate => "replicate".to_string(),
|
||||
ListImageModelsModelCreator::Runway => "runway".to_string(),
|
||||
ListImageModelsModelCreator::Stability => "stability".to_string(),
|
||||
ListImageModelsModelCreator::Tencent => "tencent".to_string(),
|
||||
ListImageModelsModelCreator::TensorArt => "tensor_art".to_string(),
|
||||
ListImageModelsModelCreator::Vidu => "vidu".to_string(),
|
||||
ListImageModelsModelCreator::WorldLabs => "world_labs".to_string(),
|
||||
ListImageModelsModelCreator::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientImageModel` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsImageModel {
|
||||
Flux1Dev,
|
||||
Flux1Schnell,
|
||||
FluxPro11,
|
||||
FluxPro11Ultra,
|
||||
GptImage1,
|
||||
GptImage1p5,
|
||||
GptImage2,
|
||||
GrokImagineImage,
|
||||
GrokImagineImageQuality,
|
||||
Midjourney7,
|
||||
Midjourney7Niji,
|
||||
Midjourney8,
|
||||
NanoBanana,
|
||||
NanoBanana2,
|
||||
NanoBananaPro,
|
||||
Seedream4,
|
||||
Seedream4p5,
|
||||
Seedream5Lite,
|
||||
QwenEdit2511Angles,
|
||||
Flux2LoraAngles,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientImageModel> for ListImageModelsImageModel {
|
||||
fn from(value: ApiClientImageModel) -> Self {
|
||||
match value {
|
||||
ApiClientImageModel::Flux1Dev => Self::Flux1Dev,
|
||||
ApiClientImageModel::Flux1Schnell => Self::Flux1Schnell,
|
||||
ApiClientImageModel::FluxPro11 => Self::FluxPro11,
|
||||
ApiClientImageModel::FluxPro11Ultra => Self::FluxPro11Ultra,
|
||||
ApiClientImageModel::GptImage1 => Self::GptImage1,
|
||||
ApiClientImageModel::GptImage1p5 => Self::GptImage1p5,
|
||||
ApiClientImageModel::GptImage2 => Self::GptImage2,
|
||||
ApiClientImageModel::GrokImagineImage => Self::GrokImagineImage,
|
||||
ApiClientImageModel::GrokImagineImageQuality => Self::GrokImagineImageQuality,
|
||||
ApiClientImageModel::Midjourney7 => Self::Midjourney7,
|
||||
ApiClientImageModel::Midjourney7Niji => Self::Midjourney7Niji,
|
||||
ApiClientImageModel::Midjourney8 => Self::Midjourney8,
|
||||
ApiClientImageModel::NanoBanana => Self::NanoBanana,
|
||||
ApiClientImageModel::NanoBanana2 => Self::NanoBanana2,
|
||||
ApiClientImageModel::NanoBananaPro => Self::NanoBananaPro,
|
||||
ApiClientImageModel::Seedream4 => Self::Seedream4,
|
||||
ApiClientImageModel::Seedream4p5 => Self::Seedream4p5,
|
||||
ApiClientImageModel::Seedream5Lite => Self::Seedream5Lite,
|
||||
ApiClientImageModel::QwenEdit2511Angles => Self::QwenEdit2511Angles,
|
||||
ApiClientImageModel::Flux2LoraAngles => Self::Flux2LoraAngles,
|
||||
ApiClientImageModel::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsImageModel> for String {
|
||||
fn from(value: ListImageModelsImageModel) -> Self {
|
||||
match value {
|
||||
ListImageModelsImageModel::Flux1Dev => "flux_1_dev".to_string(),
|
||||
ListImageModelsImageModel::Flux1Schnell => "flux_1_schnell".to_string(),
|
||||
ListImageModelsImageModel::FluxPro11 => "flux_pro_1p1".to_string(),
|
||||
ListImageModelsImageModel::FluxPro11Ultra => "flux_pro_1p1_ultra".to_string(),
|
||||
ListImageModelsImageModel::GptImage1 => "gpt_image_1".to_string(),
|
||||
ListImageModelsImageModel::GptImage1p5 => "gpt_image_1p5".to_string(),
|
||||
ListImageModelsImageModel::GptImage2 => "gpt_image_2".to_string(),
|
||||
ListImageModelsImageModel::GrokImagineImage => "grok_imagine_image".to_string(),
|
||||
ListImageModelsImageModel::GrokImagineImageQuality => "grok_imagine_image_q".to_string(),
|
||||
ListImageModelsImageModel::Midjourney7 => "midjourney_7".to_string(),
|
||||
ListImageModelsImageModel::Midjourney7Niji => "midjourney_7_niji".to_string(),
|
||||
ListImageModelsImageModel::Midjourney8 => "midjourney_8".to_string(),
|
||||
ListImageModelsImageModel::NanoBanana => "nano_banana".to_string(),
|
||||
ListImageModelsImageModel::NanoBanana2 => "nano_banana_2".to_string(),
|
||||
ListImageModelsImageModel::NanoBananaPro => "nano_banana_pro".to_string(),
|
||||
ListImageModelsImageModel::Seedream4 => "seedream_4".to_string(),
|
||||
ListImageModelsImageModel::Seedream4p5 => "seedream_4p5".to_string(),
|
||||
ListImageModelsImageModel::Seedream5Lite => "seedream_5_lite".to_string(),
|
||||
ListImageModelsImageModel::QwenEdit2511Angles => "qwen_edit_2511_angles".to_string(),
|
||||
ListImageModelsImageModel::Flux2LoraAngles => "flux_2_lora_angles".to_string(),
|
||||
ListImageModelsImageModel::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientAspectRatio` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsAspectRatio {
|
||||
Auto,
|
||||
Square,
|
||||
WideThreeByTwo,
|
||||
WideFourByThree,
|
||||
WideFiveByFour,
|
||||
WideSixteenByNine,
|
||||
WideTwentyOneByNine,
|
||||
TallTwoByThree,
|
||||
TallThreeByFour,
|
||||
TallFourByFive,
|
||||
TallNineBySixteen,
|
||||
TallNineByTwentyOne,
|
||||
Wide,
|
||||
Tall,
|
||||
Auto2k,
|
||||
Auto3k,
|
||||
Auto4k,
|
||||
SquareHd,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientAspectRatio> for ListImageModelsAspectRatio {
|
||||
fn from(value: ApiClientAspectRatio) -> Self {
|
||||
match value {
|
||||
ApiClientAspectRatio::Auto => Self::Auto,
|
||||
ApiClientAspectRatio::Square => Self::Square,
|
||||
ApiClientAspectRatio::WideThreeByTwo => Self::WideThreeByTwo,
|
||||
ApiClientAspectRatio::WideFourByThree => Self::WideFourByThree,
|
||||
ApiClientAspectRatio::WideFiveByFour => Self::WideFiveByFour,
|
||||
ApiClientAspectRatio::WideSixteenByNine => Self::WideSixteenByNine,
|
||||
ApiClientAspectRatio::WideTwentyOneByNine => Self::WideTwentyOneByNine,
|
||||
ApiClientAspectRatio::TallTwoByThree => Self::TallTwoByThree,
|
||||
ApiClientAspectRatio::TallThreeByFour => Self::TallThreeByFour,
|
||||
ApiClientAspectRatio::TallFourByFive => Self::TallFourByFive,
|
||||
ApiClientAspectRatio::TallNineBySixteen => Self::TallNineBySixteen,
|
||||
ApiClientAspectRatio::TallNineByTwentyOne => Self::TallNineByTwentyOne,
|
||||
ApiClientAspectRatio::Wide => Self::Wide,
|
||||
ApiClientAspectRatio::Tall => Self::Tall,
|
||||
ApiClientAspectRatio::Auto2k => Self::Auto2k,
|
||||
ApiClientAspectRatio::Auto3k => Self::Auto3k,
|
||||
ApiClientAspectRatio::Auto4k => Self::Auto4k,
|
||||
ApiClientAspectRatio::SquareHd => Self::SquareHd,
|
||||
ApiClientAspectRatio::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsAspectRatio> for String {
|
||||
fn from(value: ListImageModelsAspectRatio) -> Self {
|
||||
match value {
|
||||
ListImageModelsAspectRatio::Auto => "auto".to_string(),
|
||||
ListImageModelsAspectRatio::Square => "square".to_string(),
|
||||
ListImageModelsAspectRatio::WideThreeByTwo => "wide_three_by_two".to_string(),
|
||||
ListImageModelsAspectRatio::WideFourByThree => "wide_four_by_three".to_string(),
|
||||
ListImageModelsAspectRatio::WideFiveByFour => "wide_five_by_four".to_string(),
|
||||
ListImageModelsAspectRatio::WideSixteenByNine => "wide_sixteen_by_nine".to_string(),
|
||||
ListImageModelsAspectRatio::WideTwentyOneByNine => "wide_twenty_one_by_nine".to_string(),
|
||||
ListImageModelsAspectRatio::TallTwoByThree => "tall_two_by_three".to_string(),
|
||||
ListImageModelsAspectRatio::TallThreeByFour => "tall_three_by_four".to_string(),
|
||||
ListImageModelsAspectRatio::TallFourByFive => "tall_four_by_five".to_string(),
|
||||
ListImageModelsAspectRatio::TallNineBySixteen => "tall_nine_by_sixteen".to_string(),
|
||||
ListImageModelsAspectRatio::TallNineByTwentyOne => "tall_nine_by_twenty_one".to_string(),
|
||||
ListImageModelsAspectRatio::Wide => "wide".to_string(),
|
||||
ListImageModelsAspectRatio::Tall => "tall".to_string(),
|
||||
ListImageModelsAspectRatio::Auto2k => "auto_2k".to_string(),
|
||||
ListImageModelsAspectRatio::Auto3k => "auto_3k".to_string(),
|
||||
ListImageModelsAspectRatio::Auto4k => "auto_4k".to_string(),
|
||||
ListImageModelsAspectRatio::SquareHd => "square_hd".to_string(),
|
||||
ListImageModelsAspectRatio::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientResolution` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsResolution {
|
||||
OneK,
|
||||
TwoK,
|
||||
ThreeK,
|
||||
FourK,
|
||||
HalfK,
|
||||
FourEightyP,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientResolution> for ListImageModelsResolution {
|
||||
fn from(value: ApiClientResolution) -> Self {
|
||||
match value {
|
||||
ApiClientResolution::OneK => Self::OneK,
|
||||
ApiClientResolution::TwoK => Self::TwoK,
|
||||
ApiClientResolution::ThreeK => Self::ThreeK,
|
||||
ApiClientResolution::FourK => Self::FourK,
|
||||
ApiClientResolution::HalfK => Self::HalfK,
|
||||
ApiClientResolution::FourEightyP => Self::FourEightyP,
|
||||
ApiClientResolution::SevenTwentyP => Self::SevenTwentyP,
|
||||
ApiClientResolution::TenEightyP => Self::TenEightyP,
|
||||
ApiClientResolution::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsResolution> for String {
|
||||
fn from(value: ListImageModelsResolution) -> Self {
|
||||
match value {
|
||||
ListImageModelsResolution::OneK => "one_k".to_string(),
|
||||
ListImageModelsResolution::TwoK => "two_k".to_string(),
|
||||
ListImageModelsResolution::ThreeK => "three_k".to_string(),
|
||||
ListImageModelsResolution::FourK => "four_k".to_string(),
|
||||
ListImageModelsResolution::HalfK => "half_k".to_string(),
|
||||
ListImageModelsResolution::FourEightyP => "four_eighty_p".to_string(),
|
||||
ListImageModelsResolution::SevenTwentyP => "seven_twenty_p".to_string(),
|
||||
ListImageModelsResolution::TenEightyP => "ten_eighty_p".to_string(),
|
||||
ListImageModelsResolution::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientQuality` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListImageModelsQuality {
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientQuality> for ListImageModelsQuality {
|
||||
fn from(value: ApiClientQuality) -> Self {
|
||||
match value {
|
||||
ApiClientQuality::High => Self::High,
|
||||
ApiClientQuality::Medium => Self::Medium,
|
||||
ApiClientQuality::Low => Self::Low,
|
||||
ApiClientQuality::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListImageModelsQuality> for String {
|
||||
fn from(value: ListImageModelsQuality) -> Self {
|
||||
match value {
|
||||
ListImageModelsQuality::High => "high".to_string(),
|
||||
ListImageModelsQuality::Medium => "medium".to_string(),
|
||||
ListImageModelsQuality::Low => "low".to_string(),
|
||||
ListImageModelsQuality::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Structs ============================
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListImageModelsResponse {
|
||||
pub success: bool,
|
||||
pub models: Vec<ListImageModelsModelDetails>,
|
||||
pub providers: Vec<ListImageModelsProviderDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenImageModelsResponse> for ListImageModelsResponse {
|
||||
fn from(v: OmniGenImageModelsResponse) -> Self {
|
||||
Self {
|
||||
success: v.success,
|
||||
models: v.models.into_iter().map(Into::into).collect(),
|
||||
providers: v.providers.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListImageModelsProviderDetails {
|
||||
pub provider: ListImageModelsProvider,
|
||||
pub models: Vec<ListImageModelsProviderModelDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenImageModelProviderDetails> for ListImageModelsProviderDetails {
|
||||
fn from(v: OmniGenImageModelProviderDetails) -> Self {
|
||||
Self {
|
||||
provider: v.provider.into(),
|
||||
models: v.models.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListImageModelsProviderModelDetails {
|
||||
pub model: ListImageModelsImageModel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub overrides: Option<ListImageModelsModelDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenImageProviderModelDetails> for ListImageModelsProviderModelDetails {
|
||||
fn from(v: OmniGenImageProviderModelDetails) -> Self {
|
||||
Self {
|
||||
model: v.model.into(),
|
||||
overrides: v.overrides.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListImageModelsModelDetails {
|
||||
pub model: ListImageModelsImageModel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub model_creator: Option<ListImageModelsModelCreator>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub full_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub text_prompt_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub text_prompt_max_length: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub negative_text_prompt_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub negative_text_prompt_max_length: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_refs_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_refs_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub has_fixed_editing_aspect_ratio: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio_options: Option<Vec<ListImageModelsAspectRatio>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio_default: Option<ListImageModelsAspectRatio>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio_default_when_editing: Option<ListImageModelsAspectRatio>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution_options: Option<Vec<ListImageModelsResolution>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution_default: Option<ListImageModelsResolution>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub quality_options: Option<Vec<ListImageModelsQuality>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_quality: Option<ListImageModelsQuality>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_min: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_options: Option<Vec<u16>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_default: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_disabled: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<OmniGenImageModelDetails> for ListImageModelsModelDetails {
|
||||
fn from(v: OmniGenImageModelDetails) -> Self {
|
||||
Self {
|
||||
model: v.model.into(),
|
||||
model_creator: v.model_creator.map(Into::into),
|
||||
full_name: v.full_name,
|
||||
text_prompt_supported: v.text_prompt_supported,
|
||||
text_prompt_max_length: v.text_prompt_max_length,
|
||||
negative_text_prompt_supported: v.negative_text_prompt_supported,
|
||||
negative_text_prompt_max_length: v.negative_text_prompt_max_length,
|
||||
image_refs_supported: v.image_refs_supported,
|
||||
image_refs_max: v.image_refs_max,
|
||||
has_fixed_editing_aspect_ratio: v.has_fixed_editing_aspect_ratio,
|
||||
aspect_ratio_options: v.aspect_ratio_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
aspect_ratio_default: v.aspect_ratio_default.map(Into::into),
|
||||
aspect_ratio_default_when_editing: v.aspect_ratio_default_when_editing.map(Into::into),
|
||||
resolution_options: v.resolution_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
resolution_default: v.resolution_default.map(Into::into),
|
||||
quality_options: v.quality_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
default_quality: v.default_quality.map(Into::into),
|
||||
batch_size_min: v.batch_size_min,
|
||||
batch_size_max: v.batch_size_max,
|
||||
batch_size_options: v.batch_size_options,
|
||||
batch_size_default: v.batch_size_default,
|
||||
is_disabled: v.is_disabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod image;
|
||||
pub mod video;
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
//! Tauri command: list available video models.
|
||||
//!
|
||||
//! Maps the API-client response into command-specific types (see `response_types`),
|
||||
//! retries the request up to `MAX_ATTEMPTS` times, and caches successful responses
|
||||
//! in-memory for `CACHE_TTL`.
|
||||
|
||||
use std::sync::RwLock;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, warn};
|
||||
use once_cell::sync::Lazy;
|
||||
use tauri::State;
|
||||
|
||||
use artcraft_client::endpoints::omni_gen::models::video::omni_gen_list_video_models::{omni_gen_list_video_models, OmniGenListVideoModelsArgs, OmniGenVideoModelsResponse};
|
||||
use artcraft_client::utils::api_host::ApiHost;
|
||||
|
||||
use crate::core::commands::generate::models::video::response_types::ListVideoModelsResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
|
||||
const MAX_ATTEMPTS: u32 = 3;
|
||||
const RETRY_BACKOFF: Duration = Duration::from_millis(250);
|
||||
const CACHE_TTL: Duration = Duration::from_secs(60);
|
||||
|
||||
struct CacheEntry {
|
||||
response: ListVideoModelsResponse,
|
||||
loaded_at: Instant,
|
||||
}
|
||||
|
||||
static CACHE: Lazy<RwLock<Option<CacheEntry>>> = Lazy::new(|| RwLock::new(None));
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_video_models_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
) -> ResponseOrErrorMessage<ListVideoModelsResponse> {
|
||||
if let Some(cached) = cached_response() {
|
||||
debug!("list_video_models_command: serving cached response");
|
||||
return Ok(cached.into());
|
||||
}
|
||||
|
||||
match fetch_with_retry(&app_env_configs.storyteller_host).await {
|
||||
Ok(api_response) => {
|
||||
let response: ListVideoModelsResponse = api_response.into();
|
||||
store_response(response.clone());
|
||||
Ok(response.into())
|
||||
}
|
||||
Err(error_message) => {
|
||||
warn!("list_video_models_command failed after {} attempts: {}", MAX_ATTEMPTS, error_message);
|
||||
if let Some(stale) = any_cached_response() {
|
||||
warn!("list_video_models_command: refresh failed; serving stale cached response");
|
||||
return Ok(stale.into());
|
||||
}
|
||||
Err(error_message.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cached_response() -> Option<ListVideoModelsResponse> {
|
||||
let guard = CACHE.read().ok()?;
|
||||
let entry = guard.as_ref()?;
|
||||
if entry.loaded_at.elapsed() < CACHE_TTL {
|
||||
Some(entry.response.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The cached response regardless of age. Used as a fallback so that a failed
|
||||
/// refresh still returns the last-known-good data instead of an error.
|
||||
fn any_cached_response() -> Option<ListVideoModelsResponse> {
|
||||
let guard = CACHE.read().ok()?;
|
||||
guard.as_ref().map(|entry| entry.response.clone())
|
||||
}
|
||||
|
||||
fn store_response(response: ListVideoModelsResponse) {
|
||||
if let Ok(mut guard) = CACHE.write() {
|
||||
*guard = Some(CacheEntry { response, loaded_at: Instant::now() });
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the models list, retrying up to `MAX_ATTEMPTS` times on failure.
|
||||
async fn fetch_with_retry(api_host: &ApiHost) -> Result<OmniGenVideoModelsResponse, String> {
|
||||
let mut last_error = "unknown error".to_string();
|
||||
for attempt in 1..=MAX_ATTEMPTS {
|
||||
match omni_gen_list_video_models(OmniGenListVideoModelsArgs { api_host, maybe_creds: None, provider: None }).await {
|
||||
Ok(response) => return Ok(response),
|
||||
Err(err) => {
|
||||
last_error = err.to_string();
|
||||
warn!("list_video_models attempt {}/{} failed: {}", attempt, MAX_ATTEMPTS, last_error);
|
||||
if attempt < MAX_ATTEMPTS {
|
||||
tokio::time::sleep(RETRY_BACKOFF).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(last_error)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod list_video_models_command;
|
||||
pub mod response_types;
|
||||
@@ -0,0 +1,654 @@
|
||||
//! Command-specific response types for `list_video_models_command`.
|
||||
//!
|
||||
//! These are duplicated (never `artcraft_client` types), and every enum has an
|
||||
//! `Unknown(String)` catch-all so newer server variants are preserved instead of
|
||||
//! breaking an older desktop build. `From` impls map the API-client response over.
|
||||
|
||||
use serde_derive::Serialize;
|
||||
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_generation_provider::ApiClientGenerationProvider;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_model_creator::ApiClientModelCreator;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_video_model::ApiClientVideoModel;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_aspect_ratio::ApiClientAspectRatio;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_resolution::ApiClientResolution;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_bitrate::ApiClientBitrate;
|
||||
use artcraft_client::datatypes::common_enums::generation::api_client_quality::ApiClientQuality;
|
||||
use artcraft_client::endpoints::omni_gen::models::video::omni_gen_list_video_models::{
|
||||
OmniGenVideoModelDetails, OmniGenVideoModelProviderDetails, OmniGenVideoProviderModelDetails, OmniGenVideoModelsResponse,
|
||||
};
|
||||
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
|
||||
impl SerializeMarker for ListVideoModelsResponse {}
|
||||
|
||||
// ============================ Enums ============================
|
||||
|
||||
/// Command-specific copy of `ApiClientGenerationProvider` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsProvider {
|
||||
Artcraft,
|
||||
Fal,
|
||||
Grok,
|
||||
Midjourney,
|
||||
Sora,
|
||||
WorldLabs,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientGenerationProvider> for ListVideoModelsProvider {
|
||||
fn from(value: ApiClientGenerationProvider) -> Self {
|
||||
match value {
|
||||
ApiClientGenerationProvider::Artcraft => Self::Artcraft,
|
||||
ApiClientGenerationProvider::Fal => Self::Fal,
|
||||
ApiClientGenerationProvider::Grok => Self::Grok,
|
||||
ApiClientGenerationProvider::Midjourney => Self::Midjourney,
|
||||
ApiClientGenerationProvider::Sora => Self::Sora,
|
||||
ApiClientGenerationProvider::WorldLabs => Self::WorldLabs,
|
||||
ApiClientGenerationProvider::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsProvider> for String {
|
||||
fn from(value: ListVideoModelsProvider) -> Self {
|
||||
match value {
|
||||
ListVideoModelsProvider::Artcraft => "artcraft".to_string(),
|
||||
ListVideoModelsProvider::Fal => "fal".to_string(),
|
||||
ListVideoModelsProvider::Grok => "grok".to_string(),
|
||||
ListVideoModelsProvider::Midjourney => "midjourney".to_string(),
|
||||
ListVideoModelsProvider::Sora => "sora".to_string(),
|
||||
ListVideoModelsProvider::WorldLabs => "world_labs".to_string(),
|
||||
ListVideoModelsProvider::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientModelCreator` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsModelCreator {
|
||||
Alibaba,
|
||||
ArtCraft,
|
||||
BlackForestLabs,
|
||||
Bytedance,
|
||||
Fal,
|
||||
Google,
|
||||
Grok,
|
||||
Hailuo,
|
||||
Higgsfield,
|
||||
Kling,
|
||||
Krea,
|
||||
Midjourney,
|
||||
OpenAi,
|
||||
OpenArt,
|
||||
Recraft,
|
||||
Replicate,
|
||||
Runway,
|
||||
Stability,
|
||||
Tencent,
|
||||
TensorArt,
|
||||
Vidu,
|
||||
WorldLabs,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientModelCreator> for ListVideoModelsModelCreator {
|
||||
fn from(value: ApiClientModelCreator) -> Self {
|
||||
match value {
|
||||
ApiClientModelCreator::Alibaba => Self::Alibaba,
|
||||
ApiClientModelCreator::ArtCraft => Self::ArtCraft,
|
||||
ApiClientModelCreator::BlackForestLabs => Self::BlackForestLabs,
|
||||
ApiClientModelCreator::Bytedance => Self::Bytedance,
|
||||
ApiClientModelCreator::Fal => Self::Fal,
|
||||
ApiClientModelCreator::Google => Self::Google,
|
||||
ApiClientModelCreator::Grok => Self::Grok,
|
||||
ApiClientModelCreator::Hailuo => Self::Hailuo,
|
||||
ApiClientModelCreator::Higgsfield => Self::Higgsfield,
|
||||
ApiClientModelCreator::Kling => Self::Kling,
|
||||
ApiClientModelCreator::Krea => Self::Krea,
|
||||
ApiClientModelCreator::Midjourney => Self::Midjourney,
|
||||
ApiClientModelCreator::OpenAi => Self::OpenAi,
|
||||
ApiClientModelCreator::OpenArt => Self::OpenArt,
|
||||
ApiClientModelCreator::Recraft => Self::Recraft,
|
||||
ApiClientModelCreator::Replicate => Self::Replicate,
|
||||
ApiClientModelCreator::Runway => Self::Runway,
|
||||
ApiClientModelCreator::Stability => Self::Stability,
|
||||
ApiClientModelCreator::Tencent => Self::Tencent,
|
||||
ApiClientModelCreator::TensorArt => Self::TensorArt,
|
||||
ApiClientModelCreator::Vidu => Self::Vidu,
|
||||
ApiClientModelCreator::WorldLabs => Self::WorldLabs,
|
||||
ApiClientModelCreator::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsModelCreator> for String {
|
||||
fn from(value: ListVideoModelsModelCreator) -> Self {
|
||||
match value {
|
||||
ListVideoModelsModelCreator::Alibaba => "alibaba".to_string(),
|
||||
ListVideoModelsModelCreator::ArtCraft => "artcraft".to_string(),
|
||||
ListVideoModelsModelCreator::BlackForestLabs => "black_forest_labs".to_string(),
|
||||
ListVideoModelsModelCreator::Bytedance => "bytedance".to_string(),
|
||||
ListVideoModelsModelCreator::Fal => "fal".to_string(),
|
||||
ListVideoModelsModelCreator::Google => "google".to_string(),
|
||||
ListVideoModelsModelCreator::Grok => "grok".to_string(),
|
||||
ListVideoModelsModelCreator::Hailuo => "hailuo".to_string(),
|
||||
ListVideoModelsModelCreator::Higgsfield => "higgsfield".to_string(),
|
||||
ListVideoModelsModelCreator::Kling => "kling".to_string(),
|
||||
ListVideoModelsModelCreator::Krea => "krea".to_string(),
|
||||
ListVideoModelsModelCreator::Midjourney => "midjourney".to_string(),
|
||||
ListVideoModelsModelCreator::OpenAi => "open_ai".to_string(),
|
||||
ListVideoModelsModelCreator::OpenArt => "open_art".to_string(),
|
||||
ListVideoModelsModelCreator::Recraft => "recraft".to_string(),
|
||||
ListVideoModelsModelCreator::Replicate => "replicate".to_string(),
|
||||
ListVideoModelsModelCreator::Runway => "runway".to_string(),
|
||||
ListVideoModelsModelCreator::Stability => "stability".to_string(),
|
||||
ListVideoModelsModelCreator::Tencent => "tencent".to_string(),
|
||||
ListVideoModelsModelCreator::TensorArt => "tensor_art".to_string(),
|
||||
ListVideoModelsModelCreator::Vidu => "vidu".to_string(),
|
||||
ListVideoModelsModelCreator::WorldLabs => "world_labs".to_string(),
|
||||
ListVideoModelsModelCreator::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientVideoModel` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsVideoModel {
|
||||
GrokVideo,
|
||||
GrokImagineVideo,
|
||||
GrokImagineVideo1p5,
|
||||
Kling16Pro,
|
||||
Kling21Pro,
|
||||
Kling21Master,
|
||||
Kling2p5TurboPro,
|
||||
Kling2p6Pro,
|
||||
Kling3p0Standard,
|
||||
Kling3p0Pro,
|
||||
HappyHorse1p0,
|
||||
Seedance10Lite,
|
||||
Seedance1p5Pro,
|
||||
Seedance2p0,
|
||||
Seedance2p0Fast,
|
||||
Seedance2p0BytePlus,
|
||||
Seedance2p0BytePlusFast,
|
||||
Seedance2p0Ultra,
|
||||
Seedance2p0UltraFast,
|
||||
Seedance2p0BytePlusUltra,
|
||||
Seedance2p0BytePlusUltraFast,
|
||||
Seedance2p0Mini,
|
||||
Seedance2p0BytePlusMini,
|
||||
Seedance2p0BytePlusUltraMini,
|
||||
Sora2,
|
||||
Sora2Pro,
|
||||
Veo2,
|
||||
Veo3,
|
||||
Veo3Fast,
|
||||
Veo3p1,
|
||||
Veo3p1Fast,
|
||||
PreviewModel,
|
||||
PreviewModelFast,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientVideoModel> for ListVideoModelsVideoModel {
|
||||
fn from(value: ApiClientVideoModel) -> Self {
|
||||
match value {
|
||||
ApiClientVideoModel::GrokVideo => Self::GrokVideo,
|
||||
ApiClientVideoModel::GrokImagineVideo => Self::GrokImagineVideo,
|
||||
ApiClientVideoModel::GrokImagineVideo1p5 => Self::GrokImagineVideo1p5,
|
||||
ApiClientVideoModel::Kling16Pro => Self::Kling16Pro,
|
||||
ApiClientVideoModel::Kling21Pro => Self::Kling21Pro,
|
||||
ApiClientVideoModel::Kling21Master => Self::Kling21Master,
|
||||
ApiClientVideoModel::Kling2p5TurboPro => Self::Kling2p5TurboPro,
|
||||
ApiClientVideoModel::Kling2p6Pro => Self::Kling2p6Pro,
|
||||
ApiClientVideoModel::Kling3p0Standard => Self::Kling3p0Standard,
|
||||
ApiClientVideoModel::Kling3p0Pro => Self::Kling3p0Pro,
|
||||
ApiClientVideoModel::HappyHorse1p0 => Self::HappyHorse1p0,
|
||||
ApiClientVideoModel::Seedance10Lite => Self::Seedance10Lite,
|
||||
ApiClientVideoModel::Seedance1p5Pro => Self::Seedance1p5Pro,
|
||||
ApiClientVideoModel::Seedance2p0 => Self::Seedance2p0,
|
||||
ApiClientVideoModel::Seedance2p0Fast => Self::Seedance2p0Fast,
|
||||
ApiClientVideoModel::Seedance2p0BytePlus => Self::Seedance2p0BytePlus,
|
||||
ApiClientVideoModel::Seedance2p0BytePlusFast => Self::Seedance2p0BytePlusFast,
|
||||
ApiClientVideoModel::Seedance2p0Ultra => Self::Seedance2p0Ultra,
|
||||
ApiClientVideoModel::Seedance2p0UltraFast => Self::Seedance2p0UltraFast,
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltra => Self::Seedance2p0BytePlusUltra,
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltraFast => Self::Seedance2p0BytePlusUltraFast,
|
||||
ApiClientVideoModel::Seedance2p0Mini => Self::Seedance2p0Mini,
|
||||
ApiClientVideoModel::Seedance2p0BytePlusMini => Self::Seedance2p0BytePlusMini,
|
||||
ApiClientVideoModel::Seedance2p0BytePlusUltraMini => Self::Seedance2p0BytePlusUltraMini,
|
||||
ApiClientVideoModel::Sora2 => Self::Sora2,
|
||||
ApiClientVideoModel::Sora2Pro => Self::Sora2Pro,
|
||||
ApiClientVideoModel::Veo2 => Self::Veo2,
|
||||
ApiClientVideoModel::Veo3 => Self::Veo3,
|
||||
ApiClientVideoModel::Veo3Fast => Self::Veo3Fast,
|
||||
ApiClientVideoModel::Veo3p1 => Self::Veo3p1,
|
||||
ApiClientVideoModel::Veo3p1Fast => Self::Veo3p1Fast,
|
||||
ApiClientVideoModel::PreviewModel => Self::PreviewModel,
|
||||
ApiClientVideoModel::PreviewModelFast => Self::PreviewModelFast,
|
||||
ApiClientVideoModel::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsVideoModel> for String {
|
||||
fn from(value: ListVideoModelsVideoModel) -> Self {
|
||||
match value {
|
||||
ListVideoModelsVideoModel::GrokVideo => "grok_video".to_string(),
|
||||
ListVideoModelsVideoModel::GrokImagineVideo => "grok_imagine_video".to_string(),
|
||||
ListVideoModelsVideoModel::GrokImagineVideo1p5 => "grok_imagine_video_1p5".to_string(),
|
||||
ListVideoModelsVideoModel::Kling16Pro => "kling_1p6_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Kling21Pro => "kling_2p1_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Kling21Master => "kling_2p1_master".to_string(),
|
||||
ListVideoModelsVideoModel::Kling2p5TurboPro => "kling_2p5_turbo_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Kling2p6Pro => "kling_2p6_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Kling3p0Standard => "kling_3p0_standard".to_string(),
|
||||
ListVideoModelsVideoModel::Kling3p0Pro => "kling_3p0_pro".to_string(),
|
||||
ListVideoModelsVideoModel::HappyHorse1p0 => "happy_horse_1p0".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance10Lite => "seedance_1p0_lite".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance1p5Pro => "seedance_1p5_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0 => "seedance_2p0".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0Fast => "seedance_2p0_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlus => "seedance_2p0_bp".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlusFast => "seedance_2p0_bp_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0Ultra => "seedance_2p0_u".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0UltraFast => "seedance_2p0_u_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlusUltra => "seedance_2p0_bpu".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlusUltraFast => "seedance_2p0_bpu_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0Mini => "seedance_2p0_mini".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlusMini => "seedance_2p0_bp_mini".to_string(),
|
||||
ListVideoModelsVideoModel::Seedance2p0BytePlusUltraMini => "seedance_2p0_bpu_mini".to_string(),
|
||||
ListVideoModelsVideoModel::Sora2 => "sora_2".to_string(),
|
||||
ListVideoModelsVideoModel::Sora2Pro => "sora_2_pro".to_string(),
|
||||
ListVideoModelsVideoModel::Veo2 => "veo_2".to_string(),
|
||||
ListVideoModelsVideoModel::Veo3 => "veo_3".to_string(),
|
||||
ListVideoModelsVideoModel::Veo3Fast => "veo_3_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Veo3p1 => "veo_3p1".to_string(),
|
||||
ListVideoModelsVideoModel::Veo3p1Fast => "veo_3p1_fast".to_string(),
|
||||
ListVideoModelsVideoModel::PreviewModel => "preview_model".to_string(),
|
||||
ListVideoModelsVideoModel::PreviewModelFast => "preview_model_fast".to_string(),
|
||||
ListVideoModelsVideoModel::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientAspectRatio` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsAspectRatio {
|
||||
Auto,
|
||||
Square,
|
||||
WideThreeByTwo,
|
||||
WideFourByThree,
|
||||
WideFiveByFour,
|
||||
WideSixteenByNine,
|
||||
WideTwentyOneByNine,
|
||||
TallTwoByThree,
|
||||
TallThreeByFour,
|
||||
TallFourByFive,
|
||||
TallNineBySixteen,
|
||||
TallNineByTwentyOne,
|
||||
Wide,
|
||||
Tall,
|
||||
Auto2k,
|
||||
Auto3k,
|
||||
Auto4k,
|
||||
SquareHd,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientAspectRatio> for ListVideoModelsAspectRatio {
|
||||
fn from(value: ApiClientAspectRatio) -> Self {
|
||||
match value {
|
||||
ApiClientAspectRatio::Auto => Self::Auto,
|
||||
ApiClientAspectRatio::Square => Self::Square,
|
||||
ApiClientAspectRatio::WideThreeByTwo => Self::WideThreeByTwo,
|
||||
ApiClientAspectRatio::WideFourByThree => Self::WideFourByThree,
|
||||
ApiClientAspectRatio::WideFiveByFour => Self::WideFiveByFour,
|
||||
ApiClientAspectRatio::WideSixteenByNine => Self::WideSixteenByNine,
|
||||
ApiClientAspectRatio::WideTwentyOneByNine => Self::WideTwentyOneByNine,
|
||||
ApiClientAspectRatio::TallTwoByThree => Self::TallTwoByThree,
|
||||
ApiClientAspectRatio::TallThreeByFour => Self::TallThreeByFour,
|
||||
ApiClientAspectRatio::TallFourByFive => Self::TallFourByFive,
|
||||
ApiClientAspectRatio::TallNineBySixteen => Self::TallNineBySixteen,
|
||||
ApiClientAspectRatio::TallNineByTwentyOne => Self::TallNineByTwentyOne,
|
||||
ApiClientAspectRatio::Wide => Self::Wide,
|
||||
ApiClientAspectRatio::Tall => Self::Tall,
|
||||
ApiClientAspectRatio::Auto2k => Self::Auto2k,
|
||||
ApiClientAspectRatio::Auto3k => Self::Auto3k,
|
||||
ApiClientAspectRatio::Auto4k => Self::Auto4k,
|
||||
ApiClientAspectRatio::SquareHd => Self::SquareHd,
|
||||
ApiClientAspectRatio::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsAspectRatio> for String {
|
||||
fn from(value: ListVideoModelsAspectRatio) -> Self {
|
||||
match value {
|
||||
ListVideoModelsAspectRatio::Auto => "auto".to_string(),
|
||||
ListVideoModelsAspectRatio::Square => "square".to_string(),
|
||||
ListVideoModelsAspectRatio::WideThreeByTwo => "wide_three_by_two".to_string(),
|
||||
ListVideoModelsAspectRatio::WideFourByThree => "wide_four_by_three".to_string(),
|
||||
ListVideoModelsAspectRatio::WideFiveByFour => "wide_five_by_four".to_string(),
|
||||
ListVideoModelsAspectRatio::WideSixteenByNine => "wide_sixteen_by_nine".to_string(),
|
||||
ListVideoModelsAspectRatio::WideTwentyOneByNine => "wide_twenty_one_by_nine".to_string(),
|
||||
ListVideoModelsAspectRatio::TallTwoByThree => "tall_two_by_three".to_string(),
|
||||
ListVideoModelsAspectRatio::TallThreeByFour => "tall_three_by_four".to_string(),
|
||||
ListVideoModelsAspectRatio::TallFourByFive => "tall_four_by_five".to_string(),
|
||||
ListVideoModelsAspectRatio::TallNineBySixteen => "tall_nine_by_sixteen".to_string(),
|
||||
ListVideoModelsAspectRatio::TallNineByTwentyOne => "tall_nine_by_twenty_one".to_string(),
|
||||
ListVideoModelsAspectRatio::Wide => "wide".to_string(),
|
||||
ListVideoModelsAspectRatio::Tall => "tall".to_string(),
|
||||
ListVideoModelsAspectRatio::Auto2k => "auto_2k".to_string(),
|
||||
ListVideoModelsAspectRatio::Auto3k => "auto_3k".to_string(),
|
||||
ListVideoModelsAspectRatio::Auto4k => "auto_4k".to_string(),
|
||||
ListVideoModelsAspectRatio::SquareHd => "square_hd".to_string(),
|
||||
ListVideoModelsAspectRatio::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientResolution` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsResolution {
|
||||
OneK,
|
||||
TwoK,
|
||||
ThreeK,
|
||||
FourK,
|
||||
HalfK,
|
||||
FourEightyP,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientResolution> for ListVideoModelsResolution {
|
||||
fn from(value: ApiClientResolution) -> Self {
|
||||
match value {
|
||||
ApiClientResolution::OneK => Self::OneK,
|
||||
ApiClientResolution::TwoK => Self::TwoK,
|
||||
ApiClientResolution::ThreeK => Self::ThreeK,
|
||||
ApiClientResolution::FourK => Self::FourK,
|
||||
ApiClientResolution::HalfK => Self::HalfK,
|
||||
ApiClientResolution::FourEightyP => Self::FourEightyP,
|
||||
ApiClientResolution::SevenTwentyP => Self::SevenTwentyP,
|
||||
ApiClientResolution::TenEightyP => Self::TenEightyP,
|
||||
ApiClientResolution::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsResolution> for String {
|
||||
fn from(value: ListVideoModelsResolution) -> Self {
|
||||
match value {
|
||||
ListVideoModelsResolution::OneK => "one_k".to_string(),
|
||||
ListVideoModelsResolution::TwoK => "two_k".to_string(),
|
||||
ListVideoModelsResolution::ThreeK => "three_k".to_string(),
|
||||
ListVideoModelsResolution::FourK => "four_k".to_string(),
|
||||
ListVideoModelsResolution::HalfK => "half_k".to_string(),
|
||||
ListVideoModelsResolution::FourEightyP => "four_eighty_p".to_string(),
|
||||
ListVideoModelsResolution::SevenTwentyP => "seven_twenty_p".to_string(),
|
||||
ListVideoModelsResolution::TenEightyP => "ten_eighty_p".to_string(),
|
||||
ListVideoModelsResolution::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientBitrate` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsBitrate {
|
||||
Normal,
|
||||
High,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientBitrate> for ListVideoModelsBitrate {
|
||||
fn from(value: ApiClientBitrate) -> Self {
|
||||
match value {
|
||||
ApiClientBitrate::Normal => Self::Normal,
|
||||
ApiClientBitrate::High => Self::High,
|
||||
ApiClientBitrate::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsBitrate> for String {
|
||||
fn from(value: ListVideoModelsBitrate) -> Self {
|
||||
match value {
|
||||
ListVideoModelsBitrate::Normal => "normal".to_string(),
|
||||
ListVideoModelsBitrate::High => "high".to_string(),
|
||||
ListVideoModelsBitrate::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command-specific copy of `ApiClientQuality` with an `Unknown` catch-all so newer
|
||||
/// server variants are preserved rather than dropped.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub enum ListVideoModelsQuality {
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<ApiClientQuality> for ListVideoModelsQuality {
|
||||
fn from(value: ApiClientQuality) -> Self {
|
||||
match value {
|
||||
ApiClientQuality::High => Self::High,
|
||||
ApiClientQuality::Medium => Self::Medium,
|
||||
ApiClientQuality::Low => Self::Low,
|
||||
ApiClientQuality::Unknown(other) => Self::Unknown(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListVideoModelsQuality> for String {
|
||||
fn from(value: ListVideoModelsQuality) -> Self {
|
||||
match value {
|
||||
ListVideoModelsQuality::High => "high".to_string(),
|
||||
ListVideoModelsQuality::Medium => "medium".to_string(),
|
||||
ListVideoModelsQuality::Low => "low".to_string(),
|
||||
ListVideoModelsQuality::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Structs ============================
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListVideoModelsResponse {
|
||||
pub success: bool,
|
||||
pub models: Vec<ListVideoModelsModelDetails>,
|
||||
pub providers: Vec<ListVideoModelsProviderDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenVideoModelsResponse> for ListVideoModelsResponse {
|
||||
fn from(v: OmniGenVideoModelsResponse) -> Self {
|
||||
Self {
|
||||
success: v.success,
|
||||
models: v.models.into_iter().map(Into::into).collect(),
|
||||
providers: v.providers.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListVideoModelsProviderDetails {
|
||||
pub provider: ListVideoModelsProvider,
|
||||
pub models: Vec<ListVideoModelsProviderModelDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenVideoModelProviderDetails> for ListVideoModelsProviderDetails {
|
||||
fn from(v: OmniGenVideoModelProviderDetails) -> Self {
|
||||
Self {
|
||||
provider: v.provider.into(),
|
||||
models: v.models.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListVideoModelsProviderModelDetails {
|
||||
pub model: ListVideoModelsVideoModel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub overrides: Option<ListVideoModelsModelDetails>,
|
||||
}
|
||||
|
||||
impl From<OmniGenVideoProviderModelDetails> for ListVideoModelsProviderModelDetails {
|
||||
fn from(v: OmniGenVideoProviderModelDetails) -> Self {
|
||||
Self {
|
||||
model: v.model.into(),
|
||||
overrides: v.overrides.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ListVideoModelsModelDetails {
|
||||
pub model: ListVideoModelsVideoModel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub model_creator: Option<ListVideoModelsModelCreator>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub full_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extra_info: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extra_info_short: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub text_to_video_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub text_prompt_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub text_prompt_max_length: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub negative_text_prompt_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub negative_text_prompt_max_length: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub starting_keyframe_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub starting_keyframe_required: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ending_keyframe_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_references_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_references_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub video_references_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub video_references_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub video_references_max_total_duration_seconds: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub audio_references_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub audio_references_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub audio_references_max_total_duration_seconds: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub character_references_supported: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub character_references_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub show_generate_with_sound_toggle: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio_options: Option<Vec<ListVideoModelsAspectRatio>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio_default: Option<ListVideoModelsAspectRatio>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution_options: Option<Vec<ListVideoModelsResolution>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution_default: Option<ListVideoModelsResolution>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bitrate_options: Option<Vec<ListVideoModelsBitrate>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bitrate_default: Option<ListVideoModelsBitrate>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub quality_options: Option<Vec<ListVideoModelsQuality>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_quality: Option<ListVideoModelsQuality>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration_seconds_min: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration_seconds_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration_seconds_max_with_image_references: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration_seconds_options: Option<Vec<u16>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration_seconds_default: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_min: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_max: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_options: Option<Vec<u16>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub batch_size_default: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_disabled: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<OmniGenVideoModelDetails> for ListVideoModelsModelDetails {
|
||||
fn from(v: OmniGenVideoModelDetails) -> Self {
|
||||
Self {
|
||||
model: v.model.into(),
|
||||
model_creator: v.model_creator.map(Into::into),
|
||||
full_name: v.full_name,
|
||||
extra_info: v.extra_info,
|
||||
extra_info_short: v.extra_info_short,
|
||||
text_to_video_supported: v.text_to_video_supported,
|
||||
text_prompt_supported: v.text_prompt_supported,
|
||||
text_prompt_max_length: v.text_prompt_max_length,
|
||||
negative_text_prompt_supported: v.negative_text_prompt_supported,
|
||||
negative_text_prompt_max_length: v.negative_text_prompt_max_length,
|
||||
starting_keyframe_supported: v.starting_keyframe_supported,
|
||||
starting_keyframe_required: v.starting_keyframe_required,
|
||||
ending_keyframe_supported: v.ending_keyframe_supported,
|
||||
image_references_supported: v.image_references_supported,
|
||||
image_references_max: v.image_references_max,
|
||||
video_references_supported: v.video_references_supported,
|
||||
video_references_max: v.video_references_max,
|
||||
video_references_max_total_duration_seconds: v.video_references_max_total_duration_seconds,
|
||||
audio_references_supported: v.audio_references_supported,
|
||||
audio_references_max: v.audio_references_max,
|
||||
audio_references_max_total_duration_seconds: v.audio_references_max_total_duration_seconds,
|
||||
character_references_supported: v.character_references_supported,
|
||||
character_references_max: v.character_references_max,
|
||||
show_generate_with_sound_toggle: v.show_generate_with_sound_toggle,
|
||||
aspect_ratio_options: v.aspect_ratio_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
aspect_ratio_default: v.aspect_ratio_default.map(Into::into),
|
||||
resolution_options: v.resolution_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
resolution_default: v.resolution_default.map(Into::into),
|
||||
bitrate_options: v.bitrate_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
bitrate_default: v.bitrate_default.map(Into::into),
|
||||
quality_options: v.quality_options.map(|items| items.into_iter().map(Into::into).collect()),
|
||||
default_quality: v.default_quality.map(Into::into),
|
||||
duration_seconds_min: v.duration_seconds_min,
|
||||
duration_seconds_max: v.duration_seconds_max,
|
||||
duration_seconds_max_with_image_references: v.duration_seconds_max_with_image_references,
|
||||
duration_seconds_options: v.duration_seconds_options,
|
||||
duration_seconds_default: v.duration_seconds_default,
|
||||
batch_size_min: v.batch_size_min,
|
||||
batch_size_max: v.batch_size_max,
|
||||
batch_size_options: v.batch_size_options,
|
||||
batch_size_default: v.batch_size_default,
|
||||
is_disabled: v.is_disabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ use crate::core::commands::app_preferences::update_app_preference_command::updat
|
||||
use crate::core::commands::cost_estimate::estimate_image_cost_command::estimate_image_cost_command;
|
||||
use crate::core::commands::cost_estimate::estimate_splat_cost_command::estimate_splat_cost_command;
|
||||
use crate::core::commands::cost_estimate::estimate_video_cost_command::estimate_video_cost_command;
|
||||
use crate::core::commands::generate::models::image::list_image_models_command::list_image_models_command;
|
||||
use crate::core::commands::generate::models::video::list_video_models_command::list_video_models_command;
|
||||
use crate::core::commands::download::download_directory_reveal_command::download_directory_reveal_command;
|
||||
use crate::core::commands::download::download_media_file_command::download_media_file_command;
|
||||
use crate::core::commands::download::download_url_command::download_url_command;
|
||||
@@ -219,6 +221,8 @@ pub fn run() {
|
||||
estimate_image_cost_command,
|
||||
estimate_splat_cost_command,
|
||||
estimate_video_cost_command,
|
||||
list_image_models_command,
|
||||
list_video_models_command,
|
||||
flip_image,
|
||||
generate_image_command,
|
||||
generate_video_command,
|
||||
|
||||
Reference in New Issue
Block a user