Initial commit

This commit is contained in:
Slavi Pantaleev
2024-09-12 13:44:06 +03:00
commit 946aa9d9e9
220 changed files with 26033 additions and 0 deletions

View 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
}
}
}

View 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
}
}
}

View 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
}