From a1bd292752bdd37a196788c73d00b5619e843a78 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Wed, 26 Feb 2025 13:39:43 +0200 Subject: [PATCH] Add support for making Text-To-Speech send regular text messages instead of notices When speech-to-text/flow-type = `only_transcribe`, the bot will now send text messages by default, not notices. While notice messages may be less desirable with other bots in the room, it's probably a better default for most people who enable "transcribe-only" mode. This is an improvement related to https://github.com/etkecc/baibot/issues/14 --- docs/configuration/speech-to-text.md | 13 ++++ docs/features.md | 2 + .../speech-to-text-transcribe-only-mode.webp | Bin 13290 -> 22534 bytes src/controller/cfg/controller_type.rs | 8 ++- .../cfg/determination/speech_to_text/mod.rs | 55 +++++++++++++++- .../cfg/dispatching/speech_to_text.rs | 38 ++++++++++- src/controller/cfg/help.rs | 43 +++++++++++- src/controller/cfg/status.rs | 29 +++++++++ src/controller/chat_completion/mod.rs | 61 ++++++++++++++---- src/controller/image/generation.rs | 12 +++- src/controller/utils/mime.rs | 3 +- src/controller/utils/mod.rs | 2 +- src/controller/utils/text_to_speech.rs | 5 +- src/conversation/llm/utils.rs | 2 +- src/entity/room_config_context.rs | 21 +++++- src/entity/roomconfig/defaults.rs | 8 ++- src/entity/roomconfig/entity/mod.rs | 4 +- .../roomconfig/entity/speech_to_text.rs | 50 ++++++++++++++ .../roomconfig/entity/text_generation.rs | 2 + src/entity/roomconfig/mod.rs | 5 +- src/strings/cfg.rs | 13 +++- src/strings/help/cfg.rs | 9 +++ src/utils/text_to_speech.rs | 2 +- 23 files changed, 355 insertions(+), 32 deletions(-) diff --git a/docs/configuration/speech-to-text.md b/docs/configuration/speech-to-text.md index 18a1d36..1c028b1 100644 --- a/docs/configuration/speech-to-text.md +++ b/docs/configuration/speech-to-text.md @@ -23,6 +23,19 @@ The following configuration values are recognized: Example: `!bai config room speech-to-text set-flow-type ignore` (this can also be set globally, see [🛠️ Room Settings](./README.md#room-settings)) +### 🪄 Message Type for non-threaded only-transcribed messages + +Controls how the transcribed text of voice messages is sent to the chat when Flow Type = `only_transcribe`. + +The following configuration values are recognized: + +- (default) `text`: the transcribed text is sent as a regular message. This is more convenient if you'd like to forward the transcribed message to other rooms. + +- `notice`: the transcribed text is sent as a notice message. This provides better compatibility with other bots in the room, as they are less likely to interact with messages of type notice. + +Example: `!bai config room speech-to-text set-msg-type-for-non-threaded-only-transcribed-messages notice` (this can also be set globally, see [🛠️ Room Settings](./README.md#room-settings)) + + ### 🔤 Language Lets you specify the language of the input voice messages, to avoid using auto-detection. diff --git a/docs/features.md b/docs/features.md index bc84062..00d260d 100644 --- a/docs/features.md +++ b/docs/features.md @@ -136,6 +136,8 @@ To operate in this mode, you can: - adjust the [🦻 Speech-to-Text / 🪄 Flow Type](./configuration/speech-to-text.md#-flow-type) setting to make the bot only transcribe (without doing [💬 Text Generation](#-text-generation)): `!bai config room speech-to-text set-flow-type only_transcribe` +- optionally adjust [🦻 Speech-to-Text / 🪄 Message Type for non-threaded only-transcribed messages](./configuration/speech-to-text.md#-message-type-for-non-threaded-only-transcribed-messages), if you'd like to bot to send messages of type `notice` (for better compatibility with other bots in the room) instead of sending regular `text` messages (default) + ### 🖌️ Image Generation diff --git a/docs/screenshots/speech-to-text-transcribe-only-mode.webp b/docs/screenshots/speech-to-text-transcribe-only-mode.webp index 785effe9d424dc396217afd6caaf99d3e9136902..b44d368ae167ffca87146b5eb76d1c8ee14150a0 100644 GIT binary patch literal 22534 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 diff --git a/src/controller/cfg/controller_type.rs b/src/controller/cfg/controller_type.rs index 659c35f..ae861b8 100644 --- a/src/controller/cfg/controller_type.rs +++ b/src/controller/cfg/controller_type.rs @@ -1,7 +1,8 @@ use crate::{ agent::{AgentPurpose, PublicIdentifier}, entity::roomconfig::{ - SpeechToTextFlowType, TextGenerationAutoUsage, TextGenerationPrefixRequirementType, + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, }, }; @@ -54,6 +55,11 @@ pub enum ConfigSpeechToTextSettingRelatedControllerType { GetFlowType, SetFlowType(Option), + GetMsgTypeForNonThreadedOnlyTranscribedMessages, + SetMsgTypeForNonThreadedOnlyTranscribedMessages( + Option, + ), + GetLanguage, SetLanguage(Option), } diff --git a/src/controller/cfg/determination/speech_to_text/mod.rs b/src/controller/cfg/determination/speech_to_text/mod.rs index 613ceae..1a5d2e5 100644 --- a/src/controller/cfg/determination/speech_to_text/mod.rs +++ b/src/controller/cfg/determination/speech_to_text/mod.rs @@ -1,7 +1,13 @@ #[cfg(test)] mod tests; -use crate::{controller::ControllerType, entity::roomconfig::SpeechToTextFlowType, strings}; +use crate::{ + controller::ControllerType, + entity::roomconfig::{ + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + }, + strings, +}; use super::super::controller_type::ConfigSpeechToTextSettingRelatedControllerType; @@ -48,6 +54,53 @@ pub(super) fn determine( )); } + // msg_type_for_non_threaded_only_transcribed_messages + + if let Some(remaining_text) = + text.strip_prefix("msg-type-for-non-threaded-only-transcribed-messages") + { + let remaining_text = remaining_text.trim(); + + if !remaining_text.is_empty() { + return Err(ControllerType::Error( + strings::cfg::configuration_getter_used_with_extra_text( + "msg-type-for-non-threaded-only-transcribed-messages", + remaining_text, + ) + .to_owned(), + )); + } + + return Ok(ConfigSpeechToTextSettingRelatedControllerType::GetMsgTypeForNonThreadedOnlyTranscribedMessages); + } + + if let Some(value_string) = + text.strip_prefix("set-msg-type-for-non-threaded-only-transcribed-messages") + { + let value_string = value_string.trim().to_owned(); + + let value_choice = if value_string.is_empty() { + None + } else { + let value_choice = + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::from_str( + &value_string.to_lowercase(), + ); + + if value_choice.is_none() { + return Err(ControllerType::Error( + strings::cfg::configuration_value_unrecognized(&value_string).to_owned(), + )); + } + + value_choice + }; + + return Ok(ConfigSpeechToTextSettingRelatedControllerType::SetMsgTypeForNonThreadedOnlyTranscribedMessages( + value_choice, + )); + } + // Language if let Some(remaining_text) = text.strip_prefix("language") { diff --git a/src/controller/cfg/dispatching/speech_to_text.rs b/src/controller/cfg/dispatching/speech_to_text.rs index 9a2d408..e1f90ec 100644 --- a/src/controller/cfg/dispatching/speech_to_text.rs +++ b/src/controller/cfg/dispatching/speech_to_text.rs @@ -1,4 +1,7 @@ -use crate::entity::roomconfig::{RoomSettings, SpeechToTextFlowType}; +use crate::entity::roomconfig::{ + RoomSettings, SpeechToTextFlowType, + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, +}; use crate::{entity::MessageContext, Bot}; use super::super::controller_type::{ @@ -52,6 +55,39 @@ pub(super) async fn dispatch( } } + ConfigSpeechToTextSettingRelatedControllerType::GetMsgTypeForNonThreadedOnlyTranscribedMessages => { + let value = &room_settings.speech_to_text.msg_type_for_non_threaded_only_transcribed_messages; + setting_get::(bot, message_context, value).await + } + ConfigSpeechToTextSettingRelatedControllerType::SetMsgTypeForNonThreadedOnlyTranscribedMessages(value) => { + let value = value.to_owned(); + + let setter_callback = Box::new(move |room_settings: &mut RoomSettings| { + room_settings.speech_to_text.msg_type_for_non_threaded_only_transcribed_messages = value; + }); + + match config_type { + SettingsStorageSource::Room => { + room_setting_set::( + bot, + message_context, + &value, + setter_callback, + ) + .await + } + SettingsStorageSource::Global => { + global_setting_set::( + bot, + message_context, + &value, + setter_callback, + ) + .await + } + } + } + ConfigSpeechToTextSettingRelatedControllerType::GetLanguage => { let value = &room_settings.speech_to_text.language; setting_get::(bot, message_context, value).await diff --git a/src/controller/cfg/help.rs b/src/controller/cfg/help.rs index 5a51ee3..2097df1 100644 --- a/src/controller/cfg/help.rs +++ b/src/controller/cfg/help.rs @@ -3,7 +3,8 @@ use mxlink::MessageResponseType; use crate::{ entity::{ roomconfig::{ - SpeechToTextFlowType, TextGenerationAutoUsage, TextGenerationPrefixRequirementType, + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, }, MessageContext, @@ -346,6 +347,46 @@ fn build_section_speech_to_text(command_prefix: &str) -> String { )); message.push_str("\n\n"); + // Msg Type For Non Threaded Only Transcribed Messages + + message.push_str(&format!( + "#### {}", + strings::help::cfg::speech_to_text_msg_type_for_non_threaded_only_transcribed_messages_heading() + )); + message.push_str("\n\n"); + message.push_str(strings::help::cfg::speech_to_text_msg_type_for_non_threaded_only_transcribed_messages_intro()); + message.push('\n'); + message.push_str( + &strings::help::cfg::the_following_configuration_values_are_recognized( + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::choices(), + ), + ); + message.push_str("\n\n"); + message.push_str(&format!( + "- {}", + &strings::help::cfg::current_setting_show( + command_prefix, + "speech-to-text msg-type-for-non-threaded-only-transcribed-messages" + ) + )); + message.push('\n'); + message.push_str(&format!( + "- {}", + &strings::help::cfg::current_setting_set( + command_prefix, + "speech-to-text set-msg-type-for-non-threaded-only-transcribed-messages VALUE" + ) + )); + message.push('\n'); + message.push_str(&format!( + "- {}", + &strings::help::cfg::current_setting_unset( + command_prefix, + "speech-to-text set-msg-type-for-non-threaded-only-transcribed-messages" + ) + )); + message.push_str("\n\n"); + // Language message.push_str(&format!( diff --git a/src/controller/cfg/status.rs b/src/controller/cfg/status.rs index cb354fe..933891e 100644 --- a/src/controller/cfg/status.rs +++ b/src/controller/cfg/status.rs @@ -507,6 +507,35 @@ async fn generate_speech_to_text_section( flow_type_set_where, )); + // Msg Type For Non Threaded Only Transcribed Messages + + let effective_msg_type_for_non_threaded_only_transcribed_messages = + room_config_context.speech_to_text_msg_type_for_non_threaded_only_transcribed_messages(); + let room_config_msg_type_for_non_threaded_only_transcribed_messages = room_config_context + .room_config + .settings + .speech_to_text + .msg_type_for_non_threaded_only_transcribed_messages; + let global_config_msg_type_for_non_threaded_only_transcribed_messages = room_config_context + .global_config + .fallback_room_settings + .speech_to_text + .msg_type_for_non_threaded_only_transcribed_messages; + + let msg_type_for_non_threaded_only_transcribed_messages_set_where = + if room_config_msg_type_for_non_threaded_only_transcribed_messages.is_some() { + strings::cfg::status_badge_set_in_room_config() + } else if global_config_msg_type_for_non_threaded_only_transcribed_messages.is_some() { + strings::cfg::status_badge_set_in_global_config() + } else { + strings::cfg::status_badge_using_hardcoded_default() + }; + + message.push_str(&strings::cfg::status_speech_to_text_entry_msg_type_for_non_threaded_only_transcribed_messages( + effective_msg_type_for_non_threaded_only_transcribed_messages, + msg_type_for_non_threaded_only_transcribed_messages_set_where, + )); + // Language let effective_language = room_config_context.speech_to_text_language(); diff --git a/src/controller/chat_completion/mod.rs b/src/controller/chat_completion/mod.rs index 5c2b307..58baaf7 100644 --- a/src/controller/chat_completion/mod.rs +++ b/src/controller/chat_completion/mod.rs @@ -13,7 +13,8 @@ use crate::agent::ControllerTrait; use crate::controller::utils::agent::get_effective_agent_for_purpose_or_complain; use crate::conversation::matrix::MatrixMessageProcessingParams; use crate::entity::roomconfig::{ - SpeechToTextFlowType, TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, }; use crate::entity::MessagePayload; use crate::strings; @@ -71,21 +72,35 @@ pub async fn handle( if let MessagePayload::Audio(audio_content) = &message_context.payload() { original_message_is_audio = true; - let response_type = match speech_to_text_flow_type { + let (response_type, msg_type) = match speech_to_text_flow_type { SpeechToTextFlowType::Ignore => { tracing::debug!("Intentionally ignoring audio message"); return Ok(()); } SpeechToTextFlowType::TranscribeAndGenerateText => { tracing::debug!("Will be transcribing and possibly generating text.."); - MessageResponseType::InThread(message_context.thread_info().clone()) + ( + MessageResponseType::InThread(message_context.thread_info().clone()), + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Notice, + ) } SpeechToTextFlowType::OnlyTranscribe => { tracing::debug!("Will only be transcribing audio to text.."); if message_context.thread_info().is_thread_root_only() { - MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()) + let msg_type = message_context + .room_config_context() + .speech_to_text_msg_type_for_non_threaded_only_transcribed_messages(); + ( + MessageResponseType::Reply( + message_context.thread_info().root_event_id.clone(), + ), + msg_type, + ) } else { - MessageResponseType::InThread(message_context.thread_info().clone()) + ( + MessageResponseType::InThread(message_context.thread_info().clone()), + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Notice, + ) } } }; @@ -94,8 +109,14 @@ pub async fn handle( _typing_notice_guard = Some(bot.start_typing_notice(message_context.room()).await); } - let Some(speech_to_text_created_event_id_result) = - handle_stage_speech_to_text(bot, message_context, audio_content, response_type).await + let Some(speech_to_text_created_event_id_result) = handle_stage_speech_to_text( + bot, + message_context, + audio_content, + response_type, + msg_type, + ) + .await else { return Ok(()); }; @@ -282,6 +303,7 @@ async fn handle_stage_speech_to_text( message_context: &MessageContext, audio_content: &AudioMessageEventContent, response_type: MessageResponseType, + msg_type: SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, ) -> Option { let agent = get_effective_agent_for_purpose_or_complain( bot, @@ -312,6 +334,7 @@ async fn handle_stage_speech_to_text( &agent, audio_content, response_type.clone(), + msg_type, ) .await; @@ -572,6 +595,7 @@ async fn handle_stage_speech_to_text_actual_transcribing( agent: &AgentInstance, audio_content: &AudioMessageEventContent, response_type: MessageResponseType, + msg_type: SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, ) -> anyhow::Result { let src = &audio_content.source; @@ -626,9 +650,6 @@ async fn handle_stage_speech_to_text_actual_transcribing( // // When sending a bare reply, we'd better annotate the message with a 🦻 reaction instead, // to make it clear to users that it's a transcription. - // - // Regardless of how we post this message, it will be posted as a notice, - // which can indicate to the bot (for potential future text-generation purposes) that this message is not a bot message. let (transcribed_text, annotate_message_with_reaction) = if let MessageResponseType::InThread(_) = response_type { ( @@ -639,10 +660,22 @@ async fn handle_stage_speech_to_text_actual_transcribing( (speech_to_text_result.text, true) }; - let result = bot - .messaging() - .send_notice_markdown_no_fail(message_context.room(), transcribed_text, response_type) - .await; + let result = match msg_type { + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Text => { + bot.messaging() + .send_text_markdown_no_fail(message_context.room(), transcribed_text, response_type) + .await + } + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Notice => { + bot.messaging() + .send_notice_markdown_no_fail( + message_context.room(), + transcribed_text, + response_type, + ) + .await + } + }; let event_id = result .map(|result| result.event_id) diff --git a/src/controller/image/generation.rs b/src/controller/image/generation.rs index 9bfb672..6a3a47f 100644 --- a/src/controller/image/generation.rs +++ b/src/controller/image/generation.rs @@ -6,11 +6,11 @@ use crate::agent::provider::ImageGenerationParams; use crate::agent::AgentPurpose; use crate::agent::ControllerTrait; use crate::controller::utils::agent::get_effective_agent_for_purpose_or_complain; +use crate::controller::utils::mime::get_file_extension; use crate::conversation::create_llm_conversation_for_matrix_thread; use crate::conversation::matrix::MatrixMessageProcessingParams; use crate::strings; use crate::{entity::MessageContext, Bot}; -use crate::controller::utils::mime::get_file_extension; // We may make this configurable (per room, etc.) in the future, but for now it's hardcoded. const STICKER_SIZE: &str = "256x256"; @@ -82,7 +82,10 @@ pub async fn handle_image( .await; } - let attachment_body_text = format!("generated-image.{}", get_file_extension(&response.mime_type)); + let attachment_body_text = format!( + "generated-image.{}", + get_file_extension(&response.mime_type) + ); let mut event_content = matrix_link .media() @@ -158,7 +161,10 @@ pub async fn handle_sticker( .instrument(span) .await?; - let attachment_body_text = format!("generated-sticker.{}", get_file_extension(&response.mime_type)); + let attachment_body_text = format!( + "generated-sticker.{}", + get_file_extension(&response.mime_type) + ); let mut event_content = matrix_link .media() diff --git a/src/controller/utils/mime.rs b/src/controller/utils/mime.rs index 8c9a8e7..9efe4c4 100644 --- a/src/controller/utils/mime.rs +++ b/src/controller/utils/mime.rs @@ -12,5 +12,6 @@ pub fn get_file_extension(mime_type: &mime::Mime) -> String { (mime::IMAGE, mime::PNG) => "png", (mime::IMAGE, mime::SVG) => "svg", _ => "bin", - }.to_string() + } + .to_string() } diff --git a/src/controller/utils/mod.rs b/src/controller/utils/mod.rs index f7b9a25..9ab3166 100644 --- a/src/controller/utils/mod.rs +++ b/src/controller/utils/mod.rs @@ -6,8 +6,8 @@ use crate::{ }; pub mod agent; -pub mod text_to_speech; pub(super) mod mime; +pub mod text_to_speech; pub async fn get_text_body_or_complain<'a>( bot: &Bot, diff --git a/src/controller/utils/text_to_speech.rs b/src/controller/utils/text_to_speech.rs index 0178b66..9f50110 100644 --- a/src/controller/utils/text_to_speech.rs +++ b/src/controller/utils/text_to_speech.rs @@ -118,7 +118,10 @@ async fn do_generate_and_send_tts_for_message( } }; - let attachment_body_text = format!("generated-speech.{}", get_file_extension(&text_to_speech_result.mime_type)); + let attachment_body_text = format!( + "generated-speech.{}", + get_file_extension(&text_to_speech_result.mime_type) + ); let event_content = matrix_link .media() diff --git a/src/conversation/llm/utils.rs b/src/conversation/llm/utils.rs index f8039db..3c071ad 100644 --- a/src/conversation/llm/utils.rs +++ b/src/conversation/llm/utils.rs @@ -45,7 +45,7 @@ fn convert_bot_notice_message( // Such transcriptions are prefixed with an emoji and blockquoted. // If we find a notice that doesn't match this pattern, we skip it. // - // It should be noted that transcriptions are sometimes posted as regular notice messages which do not include + // It should be noted that transcriptions are sometimes posted as regular notice (or even text) messages which do not include // the `> 🦻` formatting. This function will not handle these properly. if let Some(text) = text_to_speech_utils::parse_transcribed_message_text(text) { diff --git a/src/entity/room_config_context.rs b/src/entity/room_config_context.rs index be45ce9..a545827 100644 --- a/src/entity/room_config_context.rs +++ b/src/entity/room_config_context.rs @@ -4,7 +4,8 @@ use super::globalconfig::GlobalConfig; use super::roomconfig::RoomConfig; use crate::entity::roomconfig::{ - defaults as roomconfig_defaults, SpeechToTextFlowType, TextGenerationAutoUsage, + defaults as roomconfig_defaults, SpeechToTextFlowType, + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, }; @@ -37,6 +38,24 @@ impl RoomConfigContext { .unwrap_or(roomconfig_defaults::SPEECH_TO_TEXT_FLOW_TYPE) } + pub fn speech_to_text_msg_type_for_non_threaded_only_transcribed_messages( + &self, + ) -> SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages { + self.room_config + .settings + .speech_to_text + .msg_type_for_non_threaded_only_transcribed_messages + .or({ + self.global_config + .fallback_room_settings + .speech_to_text + .msg_type_for_non_threaded_only_transcribed_messages + }) + .unwrap_or( + roomconfig_defaults::SPEECH_TO_TEXT_ONLY_TRANSCRIBE_NON_THREADED_MESSAGE_TYPE, + ) + } + pub fn speech_to_text_language(&self) -> Option { self.room_config .settings diff --git a/src/entity/roomconfig/defaults.rs b/src/entity/roomconfig/defaults.rs index 89b5a91..87bdd36 100644 --- a/src/entity/roomconfig/defaults.rs +++ b/src/entity/roomconfig/defaults.rs @@ -1,4 +1,4 @@ -use super::SpeechToTextFlowType; +use super::{SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages}; use super::{TextGenerationAutoUsage, TextGenerationPrefixRequirementType}; use super::{TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType}; @@ -15,3 +15,9 @@ pub const TEXT_TO_SPEECH_USER_MESSAGES_FLOW_TYPE: TextToSpeechUserMessagesFlowTy pub const SPEECH_TO_TEXT_FLOW_TYPE: SpeechToTextFlowType = SpeechToTextFlowType::TranscribeAndGenerateText; + +// While notice messages may be less desirable with other bots in the room, +// it's probably a better default for most people who enable "transcribe-only" mode. +pub const SPEECH_TO_TEXT_ONLY_TRANSCRIBE_NON_THREADED_MESSAGE_TYPE: + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages = + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Text; diff --git a/src/entity/roomconfig/entity/mod.rs b/src/entity/roomconfig/entity/mod.rs index a9cc83b..543e6a0 100644 --- a/src/entity/roomconfig/entity/mod.rs +++ b/src/entity/roomconfig/entity/mod.rs @@ -13,7 +13,9 @@ mod text_generation; mod text_to_speech; pub use handler::RoomSettingsHandler; -pub use speech_to_text::SpeechToTextFlowType; +pub use speech_to_text::{ + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, +}; pub use text_generation::{TextGenerationAutoUsage, TextGenerationPrefixRequirementType}; pub use text_to_speech::{TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType}; diff --git a/src/entity/roomconfig/entity/speech_to_text.rs b/src/entity/roomconfig/entity/speech_to_text.rs index d960e48..9c5c109 100644 --- a/src/entity/roomconfig/entity/speech_to_text.rs +++ b/src/entity/roomconfig/entity/speech_to_text.rs @@ -4,6 +4,20 @@ use serde::{Deserialize, Serialize}; pub struct RoomSettingsSpeechToText { pub flow_type: Option, + /// Controls how the transcribed message is posted when dealing with: + /// - messages that only get transcribed (and do not trigger text-generation). + /// See `flow_type` and `SpeechToTextFlowType::OnlyTranscribe` for more details. + /// - incoming voice messages that are not part of a thread. + /// For messages that are part of a thread, we need to reply within the thread in a way (with a notice message) + /// that won't confuse the bot later, so we have no choice but to use a notice message. + /// + /// Text-generation may happen either as a direct result of the incoming voice message or as part of a threaded conversation. + /// Transcribed messages should not be attributed to the bot for the purposes of text-generation, + /// so any time there's a chance of text-generation happening, we should use `SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Notice` + /// and optionally prefix the message with `> 🦻`. + pub msg_type_for_non_threaded_only_transcribed_messages: + Option, + /// The language of the input audio. /// Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will improve accuracy and latency. pub language: Option, @@ -57,3 +71,39 @@ impl std::fmt::Display for SpeechToTextFlowType { } } } + +#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] +pub enum SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages { + /// Send the transcribed message text as a regular message + #[serde(rename = "text")] + Text, + + /// Send the transcribed message text as a notice message + #[serde(rename = "notice")] + Notice, +} + +impl SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages { + pub fn choices() -> Vec { + vec![Self::Text, Self::Notice] + } + + pub fn from_str(s: &str) -> Option { + match s { + "text" => Some(Self::Text), + "notice" => Some(Self::Notice), + _ => None, + } + } +} + +impl std::fmt::Display for SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Text => write!(f, "text"), + SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages::Notice => { + write!(f, "notice") + } + } + } +} diff --git a/src/entity/roomconfig/entity/text_generation.rs b/src/entity/roomconfig/entity/text_generation.rs index 7464f7f..a366aa2 100644 --- a/src/entity/roomconfig/entity/text_generation.rs +++ b/src/entity/roomconfig/entity/text_generation.rs @@ -70,6 +70,8 @@ pub enum TextGenerationAutoUsage { /// Text Generation is to be performed when the original message was sent as audio (voice). /// The voice message would be transcribed to text (subject to other configuration) /// and text generation would be triggered. + /// + /// Also see `SpeechToTextFlowType`. #[serde(rename = "only_for_voice")] OnlyForVoice, diff --git a/src/entity/roomconfig/mod.rs b/src/entity/roomconfig/mod.rs index 1a8df4b..3bfba2c 100644 --- a/src/entity/roomconfig/mod.rs +++ b/src/entity/roomconfig/mod.rs @@ -5,8 +5,9 @@ use mxlink::helpers::account_data_config::RoomConfigManager as AccountDataRoomCo pub use entity::{RoomConfig, RoomConfigCarrierContent, RoomSettings, RoomSettingsHandler}; pub use entity::{ - SpeechToTextFlowType, TextGenerationAutoUsage, TextGenerationPrefixRequirementType, - TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType, + TextToSpeechUserMessagesFlowType, }; pub type RoomConfigurationManager = diff --git a/src/strings/cfg.rs b/src/strings/cfg.rs index 1ccd166..ff26d93 100644 --- a/src/strings/cfg.rs +++ b/src/strings/cfg.rs @@ -4,7 +4,8 @@ use crate::{ PublicIdentifier, }, entity::roomconfig::{ - SpeechToTextFlowType, TextGenerationAutoUsage, TextGenerationPrefixRequirementType, + SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType, }, utils::text::block_quote, @@ -269,6 +270,16 @@ pub fn status_speech_to_text_entry_flow_type( format!("- 🪄 Flow type: `{}` ({})\n", value, set_where) } +pub fn status_speech_to_text_entry_msg_type_for_non_threaded_only_transcribed_messages( + value: SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages, + set_where: &str, +) -> String { + format!( + "- 🪄 Message type for non-threaded only-transcribed messages: `{}` ({})\n", + value, set_where + ) +} + pub fn status_speech_to_text_entry_language(value: Option, set_where: &str) -> String { let formatted = match value { Some(value) => format!("`{}` ({})", value, set_where), diff --git a/src/strings/help/cfg.rs b/src/strings/help/cfg.rs index 59a0b6c..fedd63e 100644 --- a/src/strings/help/cfg.rs +++ b/src/strings/help/cfg.rs @@ -200,6 +200,15 @@ pub fn speech_to_text_flow_type_intro() -> &'static str { "Controls how voice messages are handled." } +pub fn speech_to_text_msg_type_for_non_threaded_only_transcribed_messages_heading() -> &'static str +{ + "🪄 Message Type for non-threaded only-transcribed messages" +} + +pub fn speech_to_text_msg_type_for_non_threaded_only_transcribed_messages_intro() -> &'static str { + "Controls how the transcribed text of voice messages is sent to the chat when Flow Type = `only_transcribe`." +} + pub fn speech_to_text_language_heading() -> &'static str { "🔤 Language" } diff --git a/src/utils/text_to_speech.rs b/src/utils/text_to_speech.rs index 0da0787..4a0400c 100644 --- a/src/utils/text_to_speech.rs +++ b/src/utils/text_to_speech.rs @@ -16,7 +16,7 @@ pub fn create_transcribed_message_text(text: &str) -> String { /// If the provided text string does not match the expected format, None is returned. /// /// It should be noted that in certain cases (Transcribe-only mode), transcriptions are posted as regular notice messages which do not include -/// the `> 🦻` prefixing. This function will not handle these properly. +/// the `> 🦻` prefix. This function will not handle these properly. pub fn parse_transcribed_message_text(text: &str) -> Option { if !text.starts_with("> ") { return None;