diff --git a/.claude/rules/github-actions.md b/.claude/rules/github-actions.md new file mode 100644 index 00000000..cdcada72 --- /dev/null +++ b/.claude/rules/github-actions.md @@ -0,0 +1,10 @@ +--- +paths: + - ".github/actions/**/*" +--- + +# GitHub Actions Standards + +- Actions must represent a single purpose and a single concern. +- Compose actions if they need to provide a more complex functionality. +- GitHub actions must be named after their purpose and intent. Avoid names like "setup", "ci", and such. They need to describe the intention, and purpose behind them. diff --git a/.claude/rules/github-workflows.md b/.claude/rules/github-workflows.md index 70530bc3..84556c4c 100644 --- a/.claude/rules/github-workflows.md +++ b/.claude/rules/github-workflows.md @@ -1,12 +1,15 @@ --- paths: - - ".github/**/*" + - ".github/workflows/**/*" --- # GitHub Workflows Standards -- Always use Makefile targets in the workflow to avoid code duplication. +- Always use Makefile targets in the workflow to avoid code duplication (if they need to run something that is already present in a Makefile). - Never add the tests that use LLMs to GitHub workflows, because the default GitHub worker does not have the capacity to run them. -- Only add unit tests to GitHub workflows. +- Only add unit tests or linters to GitHub workflows. - Keep GitHub workflows responsible for only a single concern. For example, run linter, and tests in parallel. -- Do not collect code coverage in GitHub workflows. Do not instrument code. +- Treat GitHub workflows as a coding project. Use composable actions, factor similar concerns into actions. +- Encapsulate functionalities in composable actions. +- Keep the workflows clean and purposeful. +- GitHub workflows must be named after their purpose and intent. Avoid names like "setup", "ci", and such. They need to describe the intention, and purpose behind them. diff --git a/.claude/rules/nix-shell.md b/.claude/rules/nix-shell.md new file mode 100644 index 00000000..7f582f58 --- /dev/null +++ b/.claude/rules/nix-shell.md @@ -0,0 +1,12 @@ +--- +paths: + - "**/shell.nix" +--- + +# Nix Shell Standards + +- shell.nix must follow idiomatic NixOS ways of providing packages +- shell.nix must be minimal, and only providing project dependencies that would not be able to run natively otherwise +- shell.nix must not contain any workarounds +- shell.nix must not contain any kind of ELF patching +- shell.nix must not contain any kind of monkey patching diff --git a/.claude/rules/rust-integration-tests.md b/.claude/rules/rust-integration-tests.md new file mode 100644 index 00000000..62dd7dc2 --- /dev/null +++ b/.claude/rules/rust-integration-tests.md @@ -0,0 +1,14 @@ +--- +paths: + - "**/tests/**/*.rs" +--- + +# Rust Integration Tests Standards + +- Each test needs to be named after what functionality, or issue it actually tests. +- Each test file needs to be named after what functionality, or issue it actually tests. +- Each test represents a specific scenario that the core project needs to support, or represent an uncovered issue. +- If you uncover a new issue while testing, create yet another targeted test that covers that. +- Every test muse use production code. Never recreate the original code to test something conceptually. Always use production code. +- They must be single-purpose. +- It must be clear what is being tested in the test file by just reading the filename. diff --git a/.claude/rules/rust.md b/.claude/rules/rust.md index 3d940a23..4584b330 100644 --- a/.claude/rules/rust.md +++ b/.claude/rules/rust.md @@ -21,3 +21,16 @@ paths: - Never use Result<> as a function argument. - Never forward Result in enums if you can instead create a targeted error enum. It is always better to signal the specific issue, so it can be handled downstream. - Always destructure structs in arguments if possible. + +# Code Style + +Imports/uses must not be mixed with other kinds of rust syntax. + +Each file needs to follow this order: +1. `pub mod`/`mod` exports +2. vendor crate `use` +2. project crate `use` +3. local crate `use` +4. private function helpers +5. private struct helpers +6. single public export diff --git a/llama-cpp-bindings/src/log.rs b/llama-cpp-bindings/src/log.rs index 639cc5f0..47cfebbc 100644 --- a/llama-cpp-bindings/src/log.rs +++ b/llama-cpp-bindings/src/log.rs @@ -87,6 +87,26 @@ impl Module { } } +#[cfg(target_env = "msvc")] +const fn ggml_log_level_to_i32(level: llama_cpp_bindings_sys::ggml_log_level) -> i32 { + level +} + +#[cfg(not(target_env = "msvc"))] +const fn ggml_log_level_to_i32(level: llama_cpp_bindings_sys::ggml_log_level) -> i32 { + level.cast_signed() +} + +#[cfg(target_env = "msvc")] +const fn ggml_log_level_from_i32(stored: i32) -> llama_cpp_bindings_sys::ggml_log_level { + stored +} + +#[cfg(not(target_env = "msvc"))] +const fn ggml_log_level_from_i32(stored: i32) -> llama_cpp_bindings_sys::ggml_log_level { + stored.cast_unsigned() +} + fn meta_for_level( level: llama_cpp_bindings_sys::ggml_log_level, ) -> Option<(&'static Metadata<'static>, &'static OverridableFields)> { @@ -194,10 +214,10 @@ impl State { *lock = Some((previous_log_level, buffer)); } } else { - let level = self - .previous_level - .load(std::sync::atomic::Ordering::Acquire) - .cast_unsigned(); + let level = ggml_log_level_from_i32( + self.previous_level + .load(std::sync::atomic::Ordering::Acquire), + ); tracing::warn!( inferred_level = level, text = text, @@ -231,8 +251,10 @@ impl State { self.is_buffering .store(true, std::sync::atomic::Ordering::Release); - self.previous_level - .store(level.cast_signed(), std::sync::atomic::Ordering::Release); + self.previous_level.store( + ggml_log_level_to_i32(level), + std::sync::atomic::Ordering::Release, + ); } /// Emit a normal unbuffered log message (not the CONT log level and the text ends with a newline). @@ -254,8 +276,10 @@ impl State { self.generate_log(buf_level, buf_text.as_str()); } - self.previous_level - .store(level.cast_signed(), std::sync::atomic::Ordering::Release); + self.previous_level.store( + ggml_log_level_to_i32(level), + std::sync::atomic::Ordering::Release, + ); let (text, _trailing_newline) = text.split_at(text.len() - 1); @@ -294,8 +318,10 @@ impl State { level: llama_cpp_bindings_sys::ggml_log_level, ) { if level != llama_cpp_bindings_sys::GGML_LOG_LEVEL_CONT { - self.previous_level - .store(level.cast_signed(), std::sync::atomic::Ordering::Release); + self.previous_level.store( + ggml_log_level_to_i32(level), + std::sync::atomic::Ordering::Release, + ); } } @@ -304,9 +330,10 @@ impl State { /// being checked on their own. pub fn is_enabled_for_level(&self, level: llama_cpp_bindings_sys::ggml_log_level) -> bool { let level = if level == llama_cpp_bindings_sys::GGML_LOG_LEVEL_CONT { - self.previous_level - .load(std::sync::atomic::Ordering::Relaxed) - .cast_unsigned() + ggml_log_level_from_i32( + self.previous_level + .load(std::sync::atomic::Ordering::Relaxed), + ) } else { level }; @@ -397,7 +424,7 @@ mod tests { use tracing_subscriber::util::SubscriberInitExt; - use super::{Module, State, logs_to_trace}; + use super::{Module, State, ggml_log_level_to_i32, logs_to_trace}; use crate::log_options::LogOptions; #[test] @@ -435,7 +462,7 @@ mod tests { assert_eq!( stored, - llama_cpp_bindings_sys::GGML_LOG_LEVEL_WARN.cast_signed() + ggml_log_level_to_i32(llama_cpp_bindings_sys::GGML_LOG_LEVEL_WARN) ); } @@ -452,7 +479,7 @@ mod tests { assert_eq!( stored, - llama_cpp_bindings_sys::GGML_LOG_LEVEL_ERROR.cast_signed() + ggml_log_level_to_i32(llama_cpp_bindings_sys::GGML_LOG_LEVEL_ERROR) ); }