Initial commit
This commit is contained in:
63
src/controller/utils/agent.rs
Normal file
63
src/controller/utils/agent.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{
|
||||
agent::{
|
||||
utils::{get_effective_agent_for_purpose, AgentForPurposeDeterminationError},
|
||||
AgentInstance, AgentPurpose,
|
||||
},
|
||||
entity::MessageContext,
|
||||
strings, Bot,
|
||||
};
|
||||
|
||||
pub async fn get_effective_agent_for_purpose_or_complain<'a>(
|
||||
bot: &'a Bot,
|
||||
message_context: &MessageContext,
|
||||
agent_purpose: AgentPurpose,
|
||||
response_type: MessageResponseType,
|
||||
complain_when_purpose_unsupported: bool,
|
||||
) -> Option<AgentInstance> {
|
||||
let agent_info = get_effective_agent_for_purpose(
|
||||
bot.agent_manager(),
|
||||
message_context.room_config_context(),
|
||||
agent_purpose,
|
||||
)
|
||||
.await;
|
||||
|
||||
match agent_info {
|
||||
Ok(agent_info) => Some(agent_info.instance),
|
||||
Err(err) => {
|
||||
let error_message = match err {
|
||||
AgentForPurposeDeterminationError::Unknown(err_string) => Some(err_string),
|
||||
AgentForPurposeDeterminationError::NoneConfigured => None,
|
||||
AgentForPurposeDeterminationError::ConfiguredButMissing(agent_identifier) => Some(
|
||||
strings::room_config::configures_agent_for_purpose_but_does_not_exist(
|
||||
&agent_identifier,
|
||||
agent_purpose,
|
||||
),
|
||||
),
|
||||
AgentForPurposeDeterminationError::ConfiguredButLacksSupport(agent_identifier) => {
|
||||
if complain_when_purpose_unsupported {
|
||||
Some(strings::room_config::configures_agent_for_purpose_but_agent_does_not_support_it(
|
||||
&agent_identifier,
|
||||
agent_purpose,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(error_message) = error_message {
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&error_message,
|
||||
response_type,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/controller/utils/mod.rs
Normal file
29
src/controller/utils/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{
|
||||
entity::{MessageContext, MessagePayload},
|
||||
Bot,
|
||||
};
|
||||
|
||||
pub mod agent;
|
||||
pub mod text_to_speech;
|
||||
|
||||
pub async fn get_text_body_or_complain<'a>(
|
||||
bot: &Bot,
|
||||
message_context: &'a MessageContext,
|
||||
) -> Option<&'a str> {
|
||||
match &message_context.payload() {
|
||||
MessagePayload::Text(text_message_content) => Some(&text_message_content.body),
|
||||
_ => {
|
||||
bot.messaging()
|
||||
.send_text_markdown_no_fail(
|
||||
message_context.room(),
|
||||
"This command only works with text messages.".to_owned(),
|
||||
MessageResponseType::InThread(message_context.thread_info().clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
168
src/controller/utils/text_to_speech.rs
Normal file
168
src/controller/utils/text_to_speech.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use mxlink::matrix_sdk::ruma::OwnedEventId;
|
||||
use mxlink::{MatrixLink, MessageResponseType};
|
||||
|
||||
use tracing::Instrument;
|
||||
|
||||
use crate::{
|
||||
agent::{provider::TextToSpeechParams, AgentInstance, AgentPurpose, ControllerTrait},
|
||||
entity::MessageContext,
|
||||
strings, Bot,
|
||||
};
|
||||
|
||||
pub async fn generate_and_send_tts_for_message(
|
||||
bot: &Bot,
|
||||
matrix_link: MatrixLink,
|
||||
message_context: &MessageContext,
|
||||
response_type: MessageResponseType,
|
||||
speech_agent: &AgentInstance,
|
||||
text_message_event_id: &OwnedEventId,
|
||||
text_content: &str,
|
||||
) -> bool {
|
||||
_ = message_context.room().typing_notice(true).await;
|
||||
|
||||
let reaction_event_response = bot
|
||||
.reacting()
|
||||
.react_no_fail(
|
||||
message_context.room(),
|
||||
text_message_event_id.clone(),
|
||||
strings::PROGRESS_INDICATOR_EMOJI.to_owned(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let result = do_generate_and_send_tts_for_message(
|
||||
bot,
|
||||
matrix_link,
|
||||
message_context,
|
||||
response_type,
|
||||
speech_agent,
|
||||
text_content,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Some(reaction_event_response) = reaction_event_response {
|
||||
let redaction_reason = if result {
|
||||
strings::text_to_speech::redaction_reason_done()
|
||||
} else {
|
||||
strings::text_to_speech::redaction_reason_failed()
|
||||
};
|
||||
|
||||
bot.messaging()
|
||||
.redact_event_no_fail(
|
||||
message_context.room(),
|
||||
reaction_event_response.event_id,
|
||||
Some(redaction_reason.to_owned()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn do_generate_and_send_tts_for_message(
|
||||
bot: &Bot,
|
||||
matrix_link: MatrixLink,
|
||||
message_context: &MessageContext,
|
||||
response_type: MessageResponseType,
|
||||
speech_agent: &AgentInstance,
|
||||
text_content: &str,
|
||||
) -> bool {
|
||||
let params = TextToSpeechParams {
|
||||
speed_override: message_context
|
||||
.room_config_context()
|
||||
.text_to_speech_speed_override(),
|
||||
|
||||
voice_override: message_context
|
||||
.room_config_context()
|
||||
.text_to_speech_voice_override(),
|
||||
};
|
||||
|
||||
let text_content = if let Some(text_content) = text_content.strip_prefix(bot.command_prefix()) {
|
||||
text_content.trim()
|
||||
} else {
|
||||
text_content
|
||||
};
|
||||
|
||||
let span = tracing::debug_span!(
|
||||
"text_to_speech_generation",
|
||||
agent_id = speech_agent.identifier().as_string()
|
||||
);
|
||||
|
||||
let text_to_speech_result = speech_agent
|
||||
.controller()
|
||||
.text_to_speech(text_content, params)
|
||||
.instrument(span)
|
||||
.await;
|
||||
|
||||
let text_to_speech_result = match text_to_speech_result {
|
||||
Ok(text_to_speech_result) => text_to_speech_result,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
"Error in room {} while trying to generate TTS via agent {}: {:?}",
|
||||
message_context.room_id(),
|
||||
speech_agent.identifier(),
|
||||
err,
|
||||
);
|
||||
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&strings::agent::error_while_serving_purpose(
|
||||
speech_agent.identifier(),
|
||||
&AgentPurpose::SpeechToText,
|
||||
&err,
|
||||
),
|
||||
response_type,
|
||||
)
|
||||
.await;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let attachment_body_text = strings::text_to_speech::alternate_body_text();
|
||||
|
||||
let event_content = matrix_link
|
||||
.media()
|
||||
.upload_and_prepare_event_content(
|
||||
message_context.room(),
|
||||
&text_to_speech_result.mime_type,
|
||||
text_to_speech_result.bytes,
|
||||
&attachment_body_text,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut event_content = match event_content {
|
||||
Ok(event_content) => event_content,
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
?err,
|
||||
"Error in room {} while trying to upload TTS via agent {}",
|
||||
message_context.room_id(),
|
||||
speech_agent.identifier(),
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let result = matrix_link
|
||||
.messaging()
|
||||
.send_event(
|
||||
message_context.room(),
|
||||
&mut event_content,
|
||||
response_type.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let Err(err) = result else {
|
||||
return true;
|
||||
};
|
||||
|
||||
tracing::error!(
|
||||
?err,
|
||||
"Error in room {} while trying to send TTS payload",
|
||||
message_context.room_id(),
|
||||
);
|
||||
|
||||
false
|
||||
}
|
||||
Reference in New Issue
Block a user