mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
minimax feature (#1865)
This commit is contained in:
@@ -49,6 +49,14 @@ pub enum UserFeatureFlag {
|
||||
/// Access to API key creation
|
||||
#[serde(rename = "api_key")]
|
||||
ApiKey,
|
||||
|
||||
/// Access to Minimax
|
||||
#[serde(rename = "minimax")]
|
||||
Minimax,
|
||||
|
||||
/// Access to Minimax with debugging priority (admins)
|
||||
#[serde(rename = "minimax_p")]
|
||||
MinimaxPriority,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -69,6 +77,8 @@ impl UserFeatureFlag {
|
||||
Self::HappyHorseRateLimit => "hh_rl",
|
||||
Self::ReferralsProgram => "referrals",
|
||||
Self::ApiKey => "api_key",
|
||||
Self::Minimax => "minimax",
|
||||
Self::MinimaxPriority => "minimax_p",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +93,8 @@ impl UserFeatureFlag {
|
||||
"hh_rl" => Ok(Self::HappyHorseRateLimit),
|
||||
"referrals" => Ok(Self::ReferralsProgram),
|
||||
"api_key" => Ok(Self::ApiKey),
|
||||
"minimax" => Ok(Self::Minimax),
|
||||
"minimax_p" => Ok(Self::MinimaxPriority),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -98,6 +110,8 @@ impl UserFeatureFlag {
|
||||
Self::HappyHorseRateLimit => "Happy Horse (with Rate Limit)",
|
||||
Self::ReferralsProgram => "Referrals Program",
|
||||
Self::ApiKey => "API Keys",
|
||||
Self::Minimax => "Minimax",
|
||||
Self::MinimaxPriority => "Minimax (Debugging Priority)",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +126,8 @@ impl UserFeatureFlag {
|
||||
Self::HappyHorseRateLimit => "Access to Happy Horse (with a rate limit)",
|
||||
Self::ReferralsProgram => "Access to the referrals program",
|
||||
Self::ApiKey => "Access to API key creation",
|
||||
Self::Minimax => "Access to Minimax",
|
||||
Self::MinimaxPriority => "Access to Minimax with debugging priority (admins)",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +144,8 @@ impl UserFeatureFlag {
|
||||
Self::HappyHorseRateLimit,
|
||||
Self::ReferralsProgram,
|
||||
Self::ApiKey,
|
||||
Self::Minimax,
|
||||
Self::MinimaxPriority,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -151,6 +169,8 @@ mod tests {
|
||||
assert_serialization(UserFeatureFlag::HappyHorseRateLimit, "hh_rl");
|
||||
assert_serialization(UserFeatureFlag::ReferralsProgram, "referrals");
|
||||
assert_serialization(UserFeatureFlag::ApiKey, "api_key");
|
||||
assert_serialization(UserFeatureFlag::Minimax, "minimax");
|
||||
assert_serialization(UserFeatureFlag::MinimaxPriority, "minimax_p");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -164,6 +184,8 @@ mod tests {
|
||||
assert_eq!(UserFeatureFlag::HappyHorseRateLimit.to_str(), "hh_rl");
|
||||
assert_eq!(UserFeatureFlag::ReferralsProgram.to_str(), "referrals");
|
||||
assert_eq!(UserFeatureFlag::ApiKey.to_str(), "api_key");
|
||||
assert_eq!(UserFeatureFlag::Minimax.to_str(), "minimax");
|
||||
assert_eq!(UserFeatureFlag::MinimaxPriority.to_str(), "minimax_p");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -177,13 +199,15 @@ mod tests {
|
||||
assert_eq!(UserFeatureFlag::from_str("hh_rl").unwrap(), UserFeatureFlag::HappyHorseRateLimit);
|
||||
assert_eq!(UserFeatureFlag::from_str("referrals").unwrap(), UserFeatureFlag::ReferralsProgram);
|
||||
assert_eq!(UserFeatureFlag::from_str("api_key").unwrap(), UserFeatureFlag::ApiKey);
|
||||
assert_eq!(UserFeatureFlag::from_str("minimax").unwrap(), UserFeatureFlag::Minimax);
|
||||
assert_eq!(UserFeatureFlag::from_str("minimax_p").unwrap(), UserFeatureFlag::MinimaxPriority);
|
||||
assert!(UserFeatureFlag::from_str("foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = UserFeatureFlag::all_variants();
|
||||
assert_eq!(variants.len(), 9);
|
||||
assert_eq!(variants.len(), 11);
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::ExploreMedia));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::Studio));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::Upload3d));
|
||||
@@ -193,6 +217,8 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::HappyHorseRateLimit));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::ReferralsProgram));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::ApiKey));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::Minimax));
|
||||
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::MinimaxPriority));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
+76
@@ -46,5 +46,81 @@ pub fn minimax_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
models.push(OmniGenVideoModelDetails {
|
||||
model: CommonVideoModel::MinimaxH3Turbo,
|
||||
model_creator: Some(ModelCreator::Hailuo),
|
||||
full_name: Some("MiniMax H3 Turbo".to_string()),
|
||||
text_prompt_supported: Some(true),
|
||||
text_prompt_max_length: Some(7000),
|
||||
starting_keyframe_supported: Some(true),
|
||||
ending_keyframe_supported: Some(true),
|
||||
image_references_supported: Some(true),
|
||||
image_references_max: Some(9),
|
||||
video_references_supported: Some(true),
|
||||
video_references_max: Some(3),
|
||||
video_references_max_total_duration_seconds: Some(15),
|
||||
audio_references_supported: Some(true),
|
||||
audio_references_max: Some(3),
|
||||
audio_references_max_total_duration_seconds: Some(15),
|
||||
aspect_ratio_options: Some(vec![
|
||||
CommonAspectRatio::WideTwentyOneByNine,
|
||||
CommonAspectRatio::WideSixteenByNine,
|
||||
CommonAspectRatio::WideFourByThree,
|
||||
CommonAspectRatio::Square,
|
||||
CommonAspectRatio::TallThreeByFour,
|
||||
CommonAspectRatio::TallNineBySixteen,
|
||||
]),
|
||||
aspect_ratio_default: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
// The model renders 768P or 2K; 720p and below land on 768P, 1080p and
|
||||
// above land on 2K.
|
||||
resolution_options: Some(vec![
|
||||
CommonResolution::SevenTwentyP,
|
||||
CommonResolution::TwoK,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::TwoK),
|
||||
duration_seconds_min: Some(5),
|
||||
duration_seconds_max: Some(15),
|
||||
duration_seconds_default: Some(5),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
models.push(OmniGenVideoModelDetails {
|
||||
model: CommonVideoModel::MinimaxH3Ultra,
|
||||
model_creator: Some(ModelCreator::Hailuo),
|
||||
full_name: Some("MiniMax H3 Ultra".to_string()),
|
||||
text_prompt_supported: Some(true),
|
||||
text_prompt_max_length: Some(7000),
|
||||
starting_keyframe_supported: Some(true),
|
||||
ending_keyframe_supported: Some(true),
|
||||
image_references_supported: Some(true),
|
||||
image_references_max: Some(9),
|
||||
video_references_supported: Some(true),
|
||||
video_references_max: Some(3),
|
||||
video_references_max_total_duration_seconds: Some(15),
|
||||
audio_references_supported: Some(true),
|
||||
audio_references_max: Some(3),
|
||||
audio_references_max_total_duration_seconds: Some(15),
|
||||
aspect_ratio_options: Some(vec![
|
||||
CommonAspectRatio::WideTwentyOneByNine,
|
||||
CommonAspectRatio::WideSixteenByNine,
|
||||
CommonAspectRatio::WideFourByThree,
|
||||
CommonAspectRatio::Square,
|
||||
CommonAspectRatio::TallThreeByFour,
|
||||
CommonAspectRatio::TallNineBySixteen,
|
||||
]),
|
||||
aspect_ratio_default: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
// The model renders 768P or 2K; 720p and below land on 768P, 1080p and
|
||||
// above land on 2K.
|
||||
resolution_options: Some(vec![
|
||||
CommonResolution::SevenTwentyP,
|
||||
CommonResolution::TwoK,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::TwoK),
|
||||
duration_seconds_min: Some(5),
|
||||
duration_seconds_max: Some(15),
|
||||
duration_seconds_default: Some(5),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
models
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ fn build_omni_gen_video_model_providers() -> Vec<OmniGenVideoModelProviderDetail
|
||||
model: CommonVideoModel::MinimaxH3,
|
||||
overrides: None,
|
||||
},
|
||||
OmniGenVideoProviderModelDetails {
|
||||
model: CommonVideoModel::MinimaxH3Turbo,
|
||||
overrides: None,
|
||||
},
|
||||
OmniGenVideoProviderModelDetails {
|
||||
model: CommonVideoModel::MinimaxH3Ultra,
|
||||
overrides: None,
|
||||
},
|
||||
OmniGenVideoProviderModelDetails {
|
||||
model: CommonVideoModel::Flux3,
|
||||
overrides: None,
|
||||
|
||||
+55
-2
@@ -13,6 +13,13 @@ use artcraft_api_defs::omni_gen::models::omni_gen_video_models::{
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use log::warn;
|
||||
|
||||
/// Models hidden behind the Minimax feature flags. MinimaxH3 itself stays
|
||||
/// available to everyone.
|
||||
const MINIMAX_GATED_MODELS: [CommonVideoModel; 2] = [
|
||||
CommonVideoModel::MinimaxH3Turbo,
|
||||
CommonVideoModel::MinimaxH3Ultra,
|
||||
];
|
||||
|
||||
/// List available video models.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
@@ -25,14 +32,60 @@ use log::warn;
|
||||
),
|
||||
)]
|
||||
pub async fn omni_gen_video_models_handler(
|
||||
_http_request: HttpRequest,
|
||||
_server_state: web::Data<Arc<ServerState>>,
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
_query: Query<OmniGenVideoModelsQuery>,
|
||||
) -> Result<Json<OmniGenVideoModelsResponse>, CommonWebError> {
|
||||
let mut response = (*OMNI_GEN_VIDEO_MODELS_AND_PROVIDERS).clone();
|
||||
|
||||
if !can_see_minimax(&http_request, &server_state).await? {
|
||||
remove_models(&mut response, &MINIMAX_GATED_MODELS);
|
||||
}
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
/// Whether the (optional) logged-in user holds a Minimax feature flag.
|
||||
/// Anonymous requests see the ungated model list.
|
||||
pub async fn can_see_minimax(
|
||||
http_request: &HttpRequest,
|
||||
server_state: &ServerState,
|
||||
) -> Result<bool, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => return Ok(false),
|
||||
};
|
||||
|
||||
let user_feature_flags =
|
||||
UserSessionFeatureFlags::new(user_session.maybe_feature_flags.as_deref());
|
||||
|
||||
let can_use_minimax = user_feature_flags.can_use_minimax();
|
||||
let can_use_minimax_priority = user_feature_flags.can_use_minimax_priority();
|
||||
|
||||
Ok(can_use_minimax || can_use_minimax_priority)
|
||||
}
|
||||
|
||||
/// Strip the given models from both the top-level model list and every
|
||||
/// provider's model list.
|
||||
fn remove_models(response: &mut OmniGenVideoModelsResponse, hidden_models: &[CommonVideoModel]) {
|
||||
response.models.retain(|details| !hidden_models.contains(&details.model));
|
||||
for provider in &mut response.providers {
|
||||
provider.models.retain(|details| !hidden_models.contains(&details.model));
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Keeping this for future reference if we need to feature gate other models.
|
||||
pub async fn can_see_happy_horse(
|
||||
http_request: &HttpRequest,
|
||||
|
||||
+8
@@ -101,6 +101,14 @@ impl UserSessionFeatureFlags {
|
||||
pub fn can_use_referrals_program(&self) -> bool {
|
||||
self.feature_flags.contains(&UserFeatureFlag::ReferralsProgram)
|
||||
}
|
||||
|
||||
pub fn can_use_minimax(&self) -> bool {
|
||||
self.feature_flags.contains(&UserFeatureFlag::Minimax)
|
||||
}
|
||||
|
||||
pub fn can_use_minimax_priority(&self) -> bool {
|
||||
self.feature_flags.contains(&UserFeatureFlag::MinimaxPriority)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user