Add prek pre-commit hooks via mise, fix formatting and clippy warnings
- Add mise.toml (prek 0.3.2) and .pre-commit-config.yaml with hooks for trailing whitespace, end-of-file, YAML check, merge conflicts, large files, cargo fmt, cargo clippy (-D warnings), and unit tests - Add prek/mise recipes to justfile - Run cargo fmt to fix formatting issues - Fix all clippy warnings: collapse nested if statements, derive Default for Avatar
This commit is contained in:
36
.pre-commit-config.yaml
Normal file
36
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
repos:
|
||||
# Fast built-in hooks (Rust-native, no dependencies)
|
||||
- repo: builtin
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=1024']
|
||||
|
||||
# Local hooks that run project-specific tools
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt-check
|
||||
name: Cargo Format Check
|
||||
entry: cargo fmt --all -- --check
|
||||
language: system
|
||||
files: '\.rs$'
|
||||
pass_filenames: false
|
||||
|
||||
- id: cargo-clippy
|
||||
name: Cargo Clippy
|
||||
entry: cargo clippy -- -D warnings
|
||||
language: system
|
||||
files: '\.rs$'
|
||||
pass_filenames: false
|
||||
priority: 100
|
||||
|
||||
- id: test-unit
|
||||
name: Unit Tests
|
||||
entry: just test
|
||||
language: system
|
||||
files: '\.rs$'
|
||||
pass_filenames: false
|
||||
priority: 100
|
||||
42
justfile
42
justfile
@@ -2,6 +2,9 @@ project_name := "baibot"
|
||||
container_image_name := "localhost/baibot"
|
||||
project_container_network := "baibot"
|
||||
|
||||
mise_data_dir := env("MISE_DATA_DIR", justfile_directory() / "var/mise")
|
||||
mise_trusted_config_paths := justfile_directory() / "mise.toml"
|
||||
|
||||
# Show help by default
|
||||
default:
|
||||
@just --list --justfile {{ justfile() }}
|
||||
@@ -159,6 +162,44 @@ synapse-register-regular-user username password: services-prepare
|
||||
clippy *extra_args:
|
||||
cargo clippy {{ extra_args }}
|
||||
|
||||
# Checks that the code compiles without building
|
||||
check:
|
||||
cargo check
|
||||
|
||||
# Invokes mise with the project-local data directory
|
||||
mise *args: _ensure_mise_data_directory
|
||||
#!/bin/sh
|
||||
export MISE_DATA_DIR="{{ mise_data_dir }}"
|
||||
export MISE_TRUSTED_CONFIG_PATHS="{{ mise_trusted_config_paths }}"
|
||||
mise {{ args }}
|
||||
|
||||
# Runs prek (pre-commit hooks manager) with the given arguments
|
||||
prek *args: _ensure_mise_tools_installed
|
||||
@just --justfile {{ justfile() }} mise exec -- prek {{ args }}
|
||||
|
||||
# Runs pre-commit hooks on staged files
|
||||
prek-run-on-staged *args: _ensure_mise_tools_installed
|
||||
@just --justfile {{ justfile() }} mise exec -- prek run {{ args }}
|
||||
|
||||
# Runs pre-commit hooks on all files
|
||||
prek-run-on-all *args: _ensure_mise_tools_installed
|
||||
@just --justfile {{ justfile() }} mise exec -- prek run --all-files {{ args }}
|
||||
|
||||
# Installs the git pre-commit hook (runs prek automatically before each commit)
|
||||
prek-install-git-pre-commit-hook: _ensure_mise_tools_installed
|
||||
@just --justfile {{ justfile() }} mise exec -- prek install
|
||||
|
||||
# Internal - ensures var/mise directory exists
|
||||
_ensure_mise_data_directory:
|
||||
#!/bin/sh
|
||||
if [ ! -d "{{ mise_data_dir }}" ]; then
|
||||
mkdir -p "{{ mise_data_dir }}"
|
||||
fi
|
||||
|
||||
# Internal - ensures mise tools are installed
|
||||
_ensure_mise_tools_installed: _ensure_mise_data_directory
|
||||
@just --justfile {{ justfile() }} mise install --quiet
|
||||
|
||||
_prepare-var-services-env:
|
||||
#!/bin/sh
|
||||
cd {{ justfile_directory() }};
|
||||
@@ -248,4 +289,3 @@ _prepare-var-app-container-data:
|
||||
if [ ! -f var/app/container/data ]; then
|
||||
mkdir -p var/app/container/data
|
||||
fi
|
||||
|
||||
|
||||
6
mise.toml
Normal file
6
mise.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[tools]
|
||||
prek = "0.3.2"
|
||||
|
||||
[settings]
|
||||
# Disable automatic trust prompts - we trust this config
|
||||
yes = true
|
||||
@@ -146,11 +146,11 @@ impl ControllerTrait for Controller {
|
||||
.temperature_override
|
||||
.unwrap_or(text_generation_config.temperature);
|
||||
|
||||
if let Some(prompt_message) = prompt_message {
|
||||
if let LLMMessageContent::Text(text) = &prompt_message.content {
|
||||
if let Some(prompt_message) = prompt_message
|
||||
&& let LLMMessageContent::Text(text) = &prompt_message.content
|
||||
{
|
||||
request.system = text.clone();
|
||||
}
|
||||
}
|
||||
|
||||
request.model = text_generation_config.model_id.clone();
|
||||
request.temperature = Some(temperature as f64);
|
||||
|
||||
@@ -5,14 +5,14 @@ use async_openai::{
|
||||
config::OpenAIConfig,
|
||||
types::{
|
||||
audio::{AudioInput, CreateSpeechRequestArgs, CreateTranscriptionRequestArgs},
|
||||
images::{
|
||||
CreateImageEditRequestArgs, CreateImageRequestArgs, Image, ImageInput, ImageModel,
|
||||
ImageResponseFormat,
|
||||
},
|
||||
responses::{
|
||||
CodeInterpreterContainerAuto, CodeInterpreterTool, CodeInterpreterToolContainer,
|
||||
CreateResponseArgs, OutputItem, OutputMessageContent, Tool, WebSearchTool,
|
||||
},
|
||||
images::{
|
||||
CreateImageEditRequestArgs, CreateImageRequestArgs,
|
||||
Image, ImageInput, ImageModel, ImageResponseFormat,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,8 +32,8 @@ use crate::{
|
||||
agent::{
|
||||
AgentPurpose,
|
||||
provider::entity::{
|
||||
ImageEditResult, ImageGenerationResult, ImageSource, PingResult,
|
||||
TextToSpeechParams, TextToSpeechResult,
|
||||
ImageEditResult, ImageGenerationResult, ImageSource, PingResult, TextToSpeechParams,
|
||||
TextToSpeechResult,
|
||||
},
|
||||
},
|
||||
strings,
|
||||
@@ -129,7 +129,8 @@ impl ControllerTrait for Controller {
|
||||
conversation_messages.insert(0, prompt_message);
|
||||
}
|
||||
|
||||
let input = super::utils::convert_llm_messages_to_openai_response_input(conversation_messages);
|
||||
let input =
|
||||
super::utils::convert_llm_messages_to_openai_response_input(conversation_messages);
|
||||
|
||||
let messages_count = match &input {
|
||||
async_openai::types::responses::InputParam::Items(items) => items.len(),
|
||||
@@ -182,10 +183,7 @@ impl ControllerTrait for Controller {
|
||||
|
||||
let response = self.client.responses().create(request).await?;
|
||||
|
||||
tracing::trace!(
|
||||
?response,
|
||||
"Got response from the OpenAI response API"
|
||||
);
|
||||
tracing::trace!(?response, "Got response from the OpenAI response API");
|
||||
|
||||
for item in response.output {
|
||||
if let OutputItem::Message(message) = item {
|
||||
@@ -271,9 +269,7 @@ impl ControllerTrait for Controller {
|
||||
ImageModel::GptImage1 => ImageModel::GptImage1Mini,
|
||||
ImageModel::GptImage1dot5 => ImageModel::GptImage1Mini,
|
||||
ImageModel::GptImage1Mini => ImageModel::GptImage1Mini,
|
||||
ImageModel::Other(_) => {
|
||||
ImageModel::DallE2
|
||||
}
|
||||
ImageModel::Other(_) => ImageModel::DallE2,
|
||||
}
|
||||
} else {
|
||||
original_model
|
||||
@@ -408,9 +404,15 @@ impl ControllerTrait for Controller {
|
||||
}
|
||||
|
||||
let dalle2_size = match image_generation_config.size {
|
||||
Some(async_openai::types::images::ImageSize::S256x256) => Some(async_openai::types::images::ImageSize::S256x256),
|
||||
Some(async_openai::types::images::ImageSize::S512x512) => Some(async_openai::types::images::ImageSize::S512x512),
|
||||
Some(async_openai::types::images::ImageSize::S1024x1024) => Some(async_openai::types::images::ImageSize::S1024x1024),
|
||||
Some(async_openai::types::images::ImageSize::S256x256) => {
|
||||
Some(async_openai::types::images::ImageSize::S256x256)
|
||||
}
|
||||
Some(async_openai::types::images::ImageSize::S512x512) => {
|
||||
Some(async_openai::types::images::ImageSize::S512x512)
|
||||
}
|
||||
Some(async_openai::types::images::ImageSize::S1024x1024) => {
|
||||
Some(async_openai::types::images::ImageSize::S1024x1024)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -419,12 +421,8 @@ impl ControllerTrait for Controller {
|
||||
.map_err(|err| anyhow::anyhow!(err))?;
|
||||
|
||||
let response_format = match model.clone() {
|
||||
ImageModel::DallE2 => {
|
||||
Some(ImageResponseFormat::B64Json)
|
||||
}
|
||||
ImageModel::DallE3 => {
|
||||
Some(ImageResponseFormat::B64Json)
|
||||
}
|
||||
ImageModel::DallE2 => Some(ImageResponseFormat::B64Json),
|
||||
ImageModel::DallE3 => Some(ImageResponseFormat::B64Json),
|
||||
// gpt-image-1 only outputs base64 and we don't need to specify the response format.
|
||||
// In fact, specifying the response format results in an error.
|
||||
ImageModel::GptImage1 => None,
|
||||
|
||||
@@ -162,13 +162,14 @@ impl TryInto<OpenAITextToSpeechConfig> for TextToSpeechConfig {
|
||||
type Error = String;
|
||||
|
||||
fn try_into(self) -> Result<OpenAITextToSpeechConfig, Self::Error> {
|
||||
let model_id = convert_string_to_enum::<async_openai::types::audio::SpeechModel>(&self.model_id)?;
|
||||
let model_id =
|
||||
convert_string_to_enum::<async_openai::types::audio::SpeechModel>(&self.model_id)?;
|
||||
|
||||
let voice = convert_string_to_enum::<async_openai::types::audio::Voice>(&self.voice)?;
|
||||
|
||||
let response_format = convert_string_to_enum::<async_openai::types::audio::SpeechResponseFormat>(
|
||||
&self.response_format,
|
||||
)?;
|
||||
let response_format = convert_string_to_enum::<
|
||||
async_openai::types::audio::SpeechResponseFormat,
|
||||
>(&self.response_format)?;
|
||||
|
||||
Ok(OpenAITextToSpeechConfig {
|
||||
model_id,
|
||||
@@ -225,25 +226,25 @@ impl TryInto<OpenAIImageGenerationConfig> for ImageGenerationConfig {
|
||||
|
||||
fn try_into(self) -> Result<OpenAIImageGenerationConfig, Self::Error> {
|
||||
let size = if let Some(size) = &self.size {
|
||||
Some(convert_string_to_enum::<async_openai::types::images::ImageSize>(
|
||||
size,
|
||||
)?)
|
||||
Some(convert_string_to_enum::<
|
||||
async_openai::types::images::ImageSize,
|
||||
>(size)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let style = if let Some(style) = &self.style {
|
||||
Some(convert_string_to_enum::<async_openai::types::images::ImageStyle>(
|
||||
style,
|
||||
)?)
|
||||
Some(convert_string_to_enum::<
|
||||
async_openai::types::images::ImageStyle,
|
||||
>(style)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let quality = if let Some(quality) = &self.quality {
|
||||
Some(convert_string_to_enum::<async_openai::types::images::ImageQuality>(
|
||||
quality,
|
||||
)?)
|
||||
Some(convert_string_to_enum::<
|
||||
async_openai::types::images::ImageQuality,
|
||||
>(quality)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -4,10 +4,10 @@ use std::{future::Future, pin::Pin};
|
||||
|
||||
use mxlink::matrix_sdk::Room;
|
||||
use mxlink::matrix_sdk::media::{MediaFormat, MediaRequestParameters};
|
||||
use mxlink::matrix_sdk::ruma::api::client::profile::{AvatarUrl, DisplayName};
|
||||
use mxlink::matrix_sdk::ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedUserId, events::room::MediaSource,
|
||||
};
|
||||
use mxlink::matrix_sdk::ruma::api::client::profile::{AvatarUrl, DisplayName};
|
||||
|
||||
use mxlink::{
|
||||
InitConfig, LoginConfig, LoginCredentials, LoginEncryption, MatrixLink, PersistenceConfig,
|
||||
|
||||
@@ -28,8 +28,9 @@ pub async fn handle_set(
|
||||
message_context: &MessageContext,
|
||||
patterns: &Option<Vec<String>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(patterns) = patterns {
|
||||
if let Err(err) = mxidwc::parse_patterns_vector(patterns) {
|
||||
if let Some(patterns) = patterns
|
||||
&& let Err(err) = mxidwc::parse_patterns_vector(patterns)
|
||||
{
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
@@ -40,7 +41,6 @@ pub async fn handle_set(
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut global_config_manager_guard = bot.global_config_manager().lock().await;
|
||||
|
||||
|
||||
@@ -24,8 +24,9 @@ pub async fn handle_set(
|
||||
message_context: &MessageContext,
|
||||
patterns: &Option<Vec<String>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(patterns) = patterns {
|
||||
if let Err(err) = mxidwc::parse_patterns_vector(patterns) {
|
||||
if let Some(patterns) = patterns
|
||||
&& let Err(err) = mxidwc::parse_patterns_vector(patterns)
|
||||
{
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
@@ -36,7 +37,6 @@ pub async fn handle_set(
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut global_config_manager_guard = bot.global_config_manager().lock().await;
|
||||
|
||||
|
||||
@@ -39,8 +39,9 @@ async fn dispatch_config_related_handler(
|
||||
message_context: &MessageContext,
|
||||
bot: &Bot,
|
||||
) -> anyhow::Result<()> {
|
||||
if let SettingsStorageSource::Global = config_type {
|
||||
if !message_context.sender_can_manage_global_config() {
|
||||
if let SettingsStorageSource::Global = config_type
|
||||
&& !message_context.sender_can_manage_global_config()
|
||||
{
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
@@ -50,7 +51,6 @@ async fn dispatch_config_related_handler(
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let room_settings = match config_type {
|
||||
SettingsStorageSource::Room => &message_context.room_config().settings,
|
||||
|
||||
@@ -154,8 +154,10 @@ pub async fn process_matrix_messages(
|
||||
|
||||
let mut message = message.clone();
|
||||
|
||||
if i == 0 && !params.first_message_prefixes_to_strip.is_empty() {
|
||||
if let MatrixMessageContent::Text(message_text) = &message.content {
|
||||
if i == 0
|
||||
&& !params.first_message_prefixes_to_strip.is_empty()
|
||||
&& let MatrixMessageContent::Text(message_text) = &message.content
|
||||
{
|
||||
let mut message_text = message_text.clone();
|
||||
|
||||
for prefix in ¶ms.first_message_prefixes_to_strip {
|
||||
@@ -166,13 +168,12 @@ pub async fn process_matrix_messages(
|
||||
|
||||
message.content = MatrixMessageContent::Text(message_text.trim().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
// We only strip `bot_user_prefixes_to_strip`-defined prefixes from messages that mention the bot user.
|
||||
if !params.bot_user_prefixes_to_strip.is_empty()
|
||||
&& message.mentioned_users.contains(¶ms.bot_user_id)
|
||||
&& let MatrixMessageContent::Text(message_text) = &message.content
|
||||
{
|
||||
if let MatrixMessageContent::Text(message_text) = &message.content {
|
||||
let mut message_text = message_text.clone();
|
||||
|
||||
for prefix in ¶ms.bot_user_prefixes_to_strip {
|
||||
@@ -183,7 +184,6 @@ pub async fn process_matrix_messages(
|
||||
|
||||
message.content = MatrixMessageContent::Text(message_text.trim().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
messages_filtered.push(message);
|
||||
}
|
||||
|
||||
@@ -88,9 +88,10 @@ impl ConfigHomeserver {
|
||||
/// - `Default`: Use the built-in default avatar (null, empty string, or missing in config)
|
||||
/// - `Keep`: Don't touch the avatar, keep whatever is already set ("keep" in config)
|
||||
/// - `Custom(String)`: Use a custom avatar from the specified file path
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize)]
|
||||
pub enum Avatar {
|
||||
/// Use the built-in default avatar
|
||||
#[default]
|
||||
Default,
|
||||
/// Keep the current avatar, don't change it
|
||||
Keep,
|
||||
@@ -98,12 +99,6 @@ pub enum Avatar {
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Default for Avatar {
|
||||
fn default() -> Self {
|
||||
Avatar::Default
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Avatar {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@@ -181,14 +176,14 @@ pub struct ConfigUserEncryption {
|
||||
|
||||
impl ConfigUserEncryption {
|
||||
pub fn validate(&self) -> anyhow::Result<()> {
|
||||
if let Some(passphrase) = &self.recovery_passphrase {
|
||||
if passphrase.is_empty() {
|
||||
if let Some(passphrase) = &self.recovery_passphrase
|
||||
&& passphrase.is_empty()
|
||||
{
|
||||
return Err(anyhow::anyhow!(
|
||||
"The user.encryption.recovery_passphrase ({}) configuration must either be null or set to a non-empty passphrase",
|
||||
super::env::BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user