diff --git a/crates/lib/database_queries/src/queries/tts/tts_models/edit_tts_model_details.rs b/crates/lib/database_queries/src/queries/tts/tts_models/edit_tts_model_details.rs index 29df0438f3..ed539f2854 100644 --- a/crates/lib/database_queries/src/queries/tts/tts_models/edit_tts_model_details.rs +++ b/crates/lib/database_queries/src/queries/tts/tts_models/edit_tts_model_details.rs @@ -14,6 +14,7 @@ pub async fn edit_tts_model_details_as_author( ietf_primary_language_subtag: &str, creator_set_visibility: RecordVisibility, maybe_default_pretrained_vocoder: Option, + text_pipeline_type: Option<&str>, // Author fields author_ip_address: &str, ) -> Result { @@ -23,6 +24,7 @@ pub async fn edit_tts_model_details_as_author( UPDATE tts_models SET maybe_default_pretrained_vocoder = ?, + text_pipeline_type = ?, title = ?, description_markdown = ?, description_rendered_html = ?, @@ -35,6 +37,7 @@ WHERE token = ? LIMIT 1 "#, maybe_default_pretrained_vocoder.map(|v| v.to_str()), + text_pipeline_type, title, description_markdown, description_html, @@ -58,6 +61,7 @@ pub async fn edit_tts_model_details_as_mod( ietf_primary_language_subtag: &str, creator_set_visibility: RecordVisibility, maybe_default_pretrained_vocoder: Option, + text_pipeline_type: Option<&str>, // moderator fields moderator_user_token: &str, ) -> Result { @@ -67,6 +71,7 @@ pub async fn edit_tts_model_details_as_mod( UPDATE tts_models SET maybe_default_pretrained_vocoder = ?, + text_pipeline_type = ?, title = ?, description_markdown = ?, description_rendered_html = ?, @@ -79,6 +84,7 @@ WHERE token = ? LIMIT 1 "#, maybe_default_pretrained_vocoder.map(|v| v.to_str()), + text_pipeline_type, title, description_markdown, description_html, diff --git a/crates/lib/tts_common/src/text_pipelines/guess_pipeline.rs b/crates/lib/tts_common/src/text_pipelines/guess_pipeline.rs index 2b3e6257f3..28ba334063 100644 --- a/crates/lib/tts_common/src/text_pipelines/guess_pipeline.rs +++ b/crates/lib/tts_common/src/text_pipelines/guess_pipeline.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use chrono::{DateTime, Utc}; use once_cell::sync::Lazy; -use crate::text_pipelines::text_pipeline::TtsTextPipeline; +use crate::text_pipelines::text_pipeline_type::TextPipelineType; const ENGLISH_V1_EPOCH_STR : &'static str = "2022-05-01T00:00:00.00Z"; @@ -20,28 +20,28 @@ static ENGLISH_V1_EPOCH : Lazy> = Lazy::new(|| { /// There will always be a guess returned. /// /// maybe_model_created_at - `created_at` timestamp from database -pub fn guess_text_pipeline_heuristic(maybe_model_created_at: Option>) -> TtsTextPipeline { +pub fn guess_text_pipeline_heuristic(maybe_model_created_at: Option>) -> TextPipelineType { // TODO: Use language to infer as well. if let Some(created_at) = maybe_model_created_at { if created_at < *ENGLISH_V1_EPOCH { - return TtsTextPipeline::LegacyFakeYou; + return TextPipelineType::LegacyFakeYou; } } - TtsTextPipeline::EnglishV1 + TextPipelineType::EnglishV1 } #[cfg(test)] mod tests { use chrono::{DateTime, Utc}; use crate::text_pipelines::guess_pipeline::guess_text_pipeline_heuristic; - use crate::text_pipelines::text_pipeline::TtsTextPipeline; + use crate::text_pipelines::text_pipeline_type::TextPipelineType; #[test] fn new_models_just_created_with_no_date_use_english_v1() { - assert_eq!(guess_text_pipeline_heuristic(None), TtsTextPipeline::EnglishV1); + assert_eq!(guess_text_pipeline_heuristic(None), TextPipelineType::EnglishV1); } #[test] @@ -49,7 +49,7 @@ mod tests { let datetime = DateTime::parse_from_rfc3339("2022-07-01T00:00:00.00Z") .expect("should parse") .with_timezone(&Utc); - assert_eq!(guess_text_pipeline_heuristic(Some(datetime)), TtsTextPipeline::EnglishV1); + assert_eq!(guess_text_pipeline_heuristic(Some(datetime)), TextPipelineType::EnglishV1); } #[test] @@ -57,6 +57,6 @@ mod tests { let datetime = DateTime::parse_from_rfc3339("2022-01-01T00:00:00.00Z") .expect("should parse") .with_timezone(&Utc); - assert_eq!(guess_text_pipeline_heuristic(Some(datetime)), TtsTextPipeline::LegacyFakeYou); + assert_eq!(guess_text_pipeline_heuristic(Some(datetime)), TextPipelineType::LegacyFakeYou); } } diff --git a/crates/lib/tts_common/src/text_pipelines/mod.rs b/crates/lib/tts_common/src/text_pipelines/mod.rs index e118ebd584..512e01e45e 100644 --- a/crates/lib/tts_common/src/text_pipelines/mod.rs +++ b/crates/lib/tts_common/src/text_pipelines/mod.rs @@ -1,2 +1,2 @@ pub mod guess_pipeline; -pub mod text_pipeline; +pub mod text_pipeline_type; diff --git a/crates/lib/tts_common/src/text_pipelines/text_pipeline.rs b/crates/lib/tts_common/src/text_pipelines/text_pipeline_type.rs similarity index 69% rename from crates/lib/tts_common/src/text_pipelines/text_pipeline.rs rename to crates/lib/tts_common/src/text_pipelines/text_pipeline_type.rs index e89de46e2c..f07ce5be51 100644 --- a/crates/lib/tts_common/src/text_pipelines/text_pipeline.rs +++ b/crates/lib/tts_common/src/text_pipelines/text_pipeline_type.rs @@ -7,7 +7,7 @@ use container_common::anyhow_result::AnyhowResult; /// This is *not* a database enum, but the text serializations get stored in the database in a /// varchar field and communicated over the API. #[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] -pub enum TtsTextPipeline { +pub enum TextPipelineType { // TODO: Introduce old vocodes models. //#[serde(rename = "legacy_vocodes")] @@ -46,7 +46,7 @@ const ENGLISH_V1 : &'static str = "english_v1"; const SPANISH_V1 : &'static str = "spanish_v1"; const SPANISH_V2 : &'static str = "spanish_v2"; -impl TtsTextPipeline { +impl TextPipelineType { /// Check if the text pipeline name is valid and supported. pub fn is_valid_name(tts_text_pipeline: &str) -> bool { @@ -57,10 +57,10 @@ impl TtsTextPipeline { // `serde_variant` looks like a fix, but it's GPL3. pub fn to_str(&self) -> &'static str { match self { - TtsTextPipeline::LegacyFakeYou => LEGACY_FAKEYOU, - TtsTextPipeline::EnglishV1 => ENGLISH_V1, - TtsTextPipeline::SpanishV1 => SPANISH_V1, - TtsTextPipeline::SpanishV2 => SPANISH_V2, + TextPipelineType::LegacyFakeYou => LEGACY_FAKEYOU, + TextPipelineType::EnglishV1 => ENGLISH_V1, + TextPipelineType::SpanishV1 => SPANISH_V1, + TextPipelineType::SpanishV2 => SPANISH_V2, } } @@ -77,36 +77,36 @@ impl TtsTextPipeline { #[cfg(test)] mod tests { - use crate::text_pipelines::text_pipeline::TtsTextPipeline; + use crate::text_pipelines::text_pipeline_type::TextPipelineType; #[test] fn valid_text_pipeline_names() { - assert!(TtsTextPipeline::is_valid_name("legacy_fakeyou")); - assert!(TtsTextPipeline::is_valid_name("english_v1")); - assert!(TtsTextPipeline::is_valid_name("spanish_v1")); - assert!(TtsTextPipeline::is_valid_name("spanish_v2")); + assert!(TextPipelineType::is_valid_name("legacy_fakeyou")); + assert!(TextPipelineType::is_valid_name("english_v1")); + assert!(TextPipelineType::is_valid_name("spanish_v1")); + assert!(TextPipelineType::is_valid_name("spanish_v2")); } #[test] fn invalid_text_pipeline_names() { // Garbage - assert!(!TtsTextPipeline::is_valid_name("")); - assert!(!TtsTextPipeline::is_valid_name("asdf")); + assert!(!TextPipelineType::is_valid_name("")); + assert!(!TextPipelineType::is_valid_name("asdf")); // NB: Must be lower case - assert!(!TtsTextPipeline::is_valid_name("LEGACY_FAKEYOU")); - assert!(!TtsTextPipeline::is_valid_name("ENGLISH_V1")); - assert!(!TtsTextPipeline::is_valid_name("SPANISH_V1")); - assert!(!TtsTextPipeline::is_valid_name("SPANISH_V2")); + assert!(!TextPipelineType::is_valid_name("LEGACY_FAKEYOU")); + assert!(!TextPipelineType::is_valid_name("ENGLISH_V1")); + assert!(!TextPipelineType::is_valid_name("SPANISH_V1")); + assert!(!TextPipelineType::is_valid_name("SPANISH_V2")); // NB: Not yet supported - assert!(!TtsTextPipeline::is_valid_name("legacy_vocodes")); - assert!(!TtsTextPipeline::is_valid_name("spanish_v3")); - assert!(!TtsTextPipeline::is_valid_name("english_v2")); + assert!(!TextPipelineType::is_valid_name("legacy_vocodes")); + assert!(!TextPipelineType::is_valid_name("spanish_v3")); + assert!(!TextPipelineType::is_valid_name("english_v2")); // Wrong names - assert!(!TtsTextPipeline::is_valid_name("english")); - assert!(!TtsTextPipeline::is_valid_name("spanish")); - assert!(!TtsTextPipeline::is_valid_name("vocodes")); + assert!(!TextPipelineType::is_valid_name("english")); + assert!(!TextPipelineType::is_valid_name("spanish")); + assert!(!TextPipelineType::is_valid_name("vocodes")); } } diff --git a/crates/service/storyteller_web/src/http_server/endpoints/tts/edit_tts_model.rs b/crates/service/storyteller_web/src/http_server/endpoints/tts/edit_tts_model.rs index dacec55916..c851a95cb8 100644 --- a/crates/service/storyteller_web/src/http_server/endpoints/tts/edit_tts_model.rs +++ b/crates/service/storyteller_web/src/http_server/endpoints/tts/edit_tts_model.rs @@ -27,6 +27,7 @@ use std::fmt; use std::sync::Arc; use database_queries::queries::tts::tts_models::edit_tts_model_details::{edit_tts_model_details_as_author, edit_tts_model_details_as_mod}; use database_queries::queries::tts::tts_models::edit_tts_model_moderator_details::edit_tts_model_moderator_details; +use tts_common::text_pipelines::text_pipeline_type::TextPipelineType; use user_input_common::check_for_slurs::contains_slurs; const DEFAULT_IETF_LANGUAGE_TAG : &'static str = "en-US"; @@ -47,11 +48,13 @@ pub struct EditTtsModelRequest { pub creator_set_visibility: Option, pub maybe_default_pretrained_vocoder: Option, + // Text pipeline won't be set by default on model upload (for now), + // and a lot of existing models will have null values. + pub text_pipeline_type: Option, + // NB: We calculate 'ietf_primary_language_subtag' from this value. pub ietf_language_tag: Option, - pub text_pipeline_type: Option, - //pub updatable_slug: Option, //pub tts_model_type: Option, //pub vocoder_token: Option, @@ -245,6 +248,9 @@ pub async fn edit_tts_model_handler( maybe_default_pretrained_vocoder = Some(vocoder); } + let text_pipeline_type = request.text_pipeline_type + .map(|pipeline_type| pipeline_type.to_str()); + let ip_address = get_request_ip(&http_request); let query_result = if is_author { @@ -259,6 +265,7 @@ pub async fn edit_tts_model_handler( &ietf_primary_language_subtag, creator_set_visibility, maybe_default_pretrained_vocoder, + text_pipeline_type, &ip_address, ).await } else { @@ -273,6 +280,7 @@ pub async fn edit_tts_model_handler( &ietf_primary_language_subtag, creator_set_visibility, maybe_default_pretrained_vocoder, + text_pipeline_type, &user_session.user_token, ).await }; diff --git a/crates/service/storyteller_web/src/http_server/endpoints/tts/get_tts_model.rs b/crates/service/storyteller_web/src/http_server/endpoints/tts/get_tts_model.rs index 234518d8f1..912d2a8990 100644 --- a/crates/service/storyteller_web/src/http_server/endpoints/tts/get_tts_model.rs +++ b/crates/service/storyteller_web/src/http_server/endpoints/tts/get_tts_model.rs @@ -22,7 +22,7 @@ use sqlx::mysql::MySqlDatabaseError; use std::fmt; use std::sync::Arc; use tts_common::text_pipelines::guess_pipeline::guess_text_pipeline_heuristic; -use tts_common::text_pipelines::text_pipeline::TtsTextPipeline; +use tts_common::text_pipelines::text_pipeline_type::TextPipelineType; // =============== Request =============== @@ -50,8 +50,8 @@ pub struct TtsModelInfo { /// /// >> NB: text_pipeline_type may not always be present in the database, but if absent we'll /// inform the frontend (and inference pipeline) of our best guess according to a heuristic. - pub text_pipeline_type: Option, - pub text_pipeline_type_guess: TtsTextPipeline, + pub text_pipeline_type: Option, + pub text_pipeline_type_guess: TextPipelineType, pub maybe_default_pretrained_vocoder: Option, pub text_preprocessing_algorithm: String, @@ -187,7 +187,7 @@ pub async fn get_tts_model_handler( // If there's an error deserializing, turn it to None. let text_pipeline_type = model.text_pipeline_type .as_deref() - .and_then(|pipeline_type| TtsTextPipeline::from_str(pipeline_type).ok()); + .and_then(|pipeline_type| TextPipelineType::from_str(pipeline_type).ok()); // TODO: Use language to infer as well. let text_pipeline_type_guess = diff --git a/crates/service/tts_inference_job/src/job_steps/process_single_job.rs b/crates/service/tts_inference_job/src/job_steps/process_single_job.rs index 270a48756a..814e91cc4a 100644 --- a/crates/service/tts_inference_job/src/job_steps/process_single_job.rs +++ b/crates/service/tts_inference_job/src/job_steps/process_single_job.rs @@ -51,7 +51,7 @@ use storage_buckets_common::bucket_path_unifier::BucketPathUnifier; use tempdir::TempDir; use tts_common::clean_symbols::clean_symbols; use tts_common::text_pipelines::guess_pipeline::guess_text_pipeline_heuristic; -use tts_common::text_pipelines::text_pipeline::TtsTextPipeline; +use tts_common::text_pipelines::text_pipeline_type::TextPipelineType; #[derive(Deserialize, Default)] struct FileMetadata { @@ -327,7 +327,7 @@ pub async fn process_single_job( let text_pipeline_type_or_guess = model_record.text_pipeline_type .as_deref() .and_then(|pipeline_type| - TtsTextPipeline::from_str(pipeline_type).ok())// NB: If there's an error deserializing, turn it to None. + TextPipelineType::from_str(pipeline_type).ok())// NB: If there's an error deserializing, turn it to None. .unwrap_or_else(|| guess_text_pipeline_heuristic(Some(model_record.created_at)));