## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/realizar/src/gguf/config.rs

    Line
    Count
    Source
    1
    //! GGUF configuration extraction
    2
    //!
    3
    //! Extracts model configuration from GGUF metadata.
    4
    //!
    5
    //! This module defines `GGUFConfig` which holds the transformer
    6
    //! architecture parameters needed for inference.
    7
    8
    use super::types::GGUFModel;
    9
    use crate::error::{RealizarError, Result};
    10
    11
    /// Configuration for GGUF transformer inference
    12
    #[derive(Debug, Clone)]
    13
    pub struct GGUFConfig {
    14
        /// Model architecture (e.g., "phi2", "llama", "qwen2")
    15
        pub architecture: String,
    16
        /// Embedding dimension (hidden size)
    17
        pub hidden_dim: usize,
    18
        /// Number of transformer layers
    19
        pub num_layers: usize,
    20
        /// Number of attention heads
    21
        pub num_heads: usize,
    22
        /// Number of key-value heads (for GQA, often num_heads or num_heads/8)
    23
        pub num_kv_heads: usize,
    24
        /// Vocabulary size
    25
        pub vocab_size: usize,
    26
        /// FFN intermediate dimension
    27
        pub intermediate_dim: usize,
    28
        /// Context length
    29
        pub context_length: usize,
    30
        /// RoPE theta (position encoding base)
    31
        pub rope_theta: f32,
    32
        /// Layer norm epsilon
    33
        pub eps: f32,
    34
        /// RoPE type: 0 = NORM (adjacent pairs), 2 = NEOX (split halves)
    35
        pub rope_type: u32,
    36
    }
    37
    38
    impl GGUFConfig {
    39
        /// Extract configuration from GGUF model metadata
    40
        ///
    41
        /// # Errors
    42
        ///
    43
        /// Returns an error if required metadata fields are missing from the GGUF model.
    44
    12
        pub fn from_gguf(model: &GGUFModel) -> Result<Self> {
    45
    12
            let architecture10 = model
    46
    12
                .architecture()
    47
    12
                .ok_or_else(|| RealizarError::InvalidShape {
    48
    2
                    reason: "Missing general.architecture in GGUF metadata".to_string(),
    49
    2
                })?
    50
    10
                .to_string();
    51
    52
    10
            let hidden_dim = model
    53
    10
                .embedding_dim()
    54
    10
                .ok_or_else(|| RealizarError::InvalidShape {
    55
    0
                    reason: "Missing embedding_length in GGUF metadata".to_string(),
    56
    0
                })?;
    57
    58
    10
            let num_layers = model
    59
    10
                .num_layers()
    60
    10
                .ok_or_else(|| RealizarError::InvalidShape {
    61
    0
                    reason: "Missing block_count in GGUF metadata".to_string(),
    62
    0
                })?;
    63
    64
            // Try to get num_heads, default based on hidden_dim if not found
    65
    10
            let num_heads = model.num_heads().unwrap_or(hidden_dim / 64);
    66
    67
            // Get vocab_size from token_embd tensor
    68
            // After dims.reverse(), shape is [vocab_size, hidden_dim] - vocab is at index 0
    69
    10
            let vocab_size = model
    70
    10
                .tensors
    71
    10
                .iter()
    72
    10
                .find(|t| t.name9 == "token_embd.weight"9)
    73
    10
                .map_or(32000, |t| t.dims.first()9.copied9().unwrap_or9(32000) as usize);
    74
    75
            // Infer intermediate_dim from ffn_up tensor
    76
            // After dims.reverse(), shape is [intermediate_dim, hidden_dim] - intermediate is at index 0
    77
    10
            let intermediate_dim = model
    78
    10
                .tensors
    79
    10
                .iter()
    80
    70
                .find10(|t| t.name == "blk.0.ffn_up.weight")
    81
    10
                .map_or(hidden_dim * 4, |t| {9
    82
    9
                    t.dims.first().copied().unwrap_or(hidden_dim as u64 * 4) as usize
    83
    9
                });
    84
    85
    10
            let context_length = model.context_length().unwrap_or(2048);
    86
    87
            // Read rope_theta from metadata, or use default (10000.0 for LLaMA-style)
    88
            // Qwen2 uses 1000000.0, which is read from qwen2.rope.freq_base
    89
    10
            let rope_theta = model.rope_freq_base().unwrap_or(10000.0);
    90
    91
            // Read RMSNorm epsilon from metadata, or use default (1e-5 for LLaMA-style)
    92
            // Qwen2 uses 1e-6, which is read from qwen2.attention.layer_norm_rms_epsilon
    93
    10
            let eps = model.rms_epsilon().unwrap_or(1e-5);
    94
    95
            // num_kv_heads (for GQA - e.g., Qwen uses fewer KV heads than Q heads)
    96
    10
            let num_kv_heads = model.num_kv_heads().unwrap_or(num_heads);
    97
    98
            // Read rope_type: 0 = NORM (adjacent pairs, default for LLaMA), 2 = NEOX (split halves)
    99
            // LLaMA models use type 0 (adjacent pairs) per llama.cpp's LLAMA_ROPE_TYPE_NORM
    100
    10
            let rope_type = model.rope_type().unwrap_or(0);
    101
    102
    10
            Ok(Self {
    103
    10
                architecture,
    104
    10
                hidden_dim,
    105
    10
                num_layers,
    106
    10
                num_heads,
    107
    10
                num_kv_heads,
    108
    10
                vocab_size,
    109
    10
                intermediate_dim,
    110
    10
                context_length,
    111
    10
                rope_theta,
    112
    10
                eps,
    113
    10
                rope_type,
    114
    10
            })
    115
    12
        }
    116
    }
    117
    118
    #[cfg(test)]
    119
    mod tests {
    120
        use super::*;
    121
    122
        #[test]
    123
    1
        fn test_gguf_config_creation() {
    124
    1
            let config = GGUFConfig {
    125
    1
                architecture: "llama".to_string(),
    126
    1
                hidden_dim: 4096,
    127
    1
                num_layers: 32,
    128
    1
                num_heads: 32,
    129
    1
                num_kv_heads: 8,
    130
    1
                vocab_size: 32000,
    131
    1
                intermediate_dim: 11008,
    132
    1
                context_length: 4096,
    133
    1
                rope_theta: 10000.0,
    134
    1
                eps: 1e-5,
    135
    1
                rope_type: 0,
    136
    1
            };
    137
    138
    1
            assert_eq!(config.architecture, "llama");
    139
    1
            assert_eq!(config.hidden_dim, 4096);
    140
    1
            assert_eq!(config.num_layers, 32);
    141
    1
            assert_eq!(config.num_heads, 32);
    142
    1
            assert_eq!(config.num_kv_heads, 8);
    143
    1
            assert_eq!(config.vocab_size, 32000);
    144
    1
            assert_eq!(config.intermediate_dim, 11008);
    145
    1
            assert_eq!(config.context_length, 4096);
    146
    1
            assert!((config.rope_theta - 10000.0).abs() < f32::EPSILON);
    147
    1
            assert!((config.eps - 1e-5).abs() < f32::EPSILON);
    148
    1
            assert_eq!(config.rope_type, 0);
    149
    1
        }
    150
    151
        #[test]
    152
    1
        fn test_gguf_config_clone() {
    153
    1
            let config = GGUFConfig {
    154
    1
                architecture: "qwen2".to_string(),
    155
    1
                hidden_dim: 2048,
    156
    1
                num_layers: 24,
    157
    1
                num_heads: 16,
    158
    1
                num_kv_heads: 2,
    159
    1
                vocab_size: 151936,
    160
    1
                intermediate_dim: 5632,
    161
    1
                context_length: 32768,
    162
    1
                rope_theta: 1_000_000.0,
    163
    1
                eps: 1e-6,
    164
    1
                rope_type: 2,
    165
    1
            };
    166
    167
    1
            let cloned = config.clone();
    168
    1
            assert_eq!(cloned.architecture, "qwen2");
    169
    1
            assert_eq!(cloned.hidden_dim, config.hidden_dim);
    170
    1
            assert_eq!(cloned.rope_type, 2);
    171
    1
        }
    172
    173
        #[test]
    174
    1
        fn test_gguf_config_debug() {
    175
    1
            let config = GGUFConfig {
    176
    1
                architecture: "phi2".to_string(),
    177
    1
                hidden_dim: 2560,
    178
    1
                num_layers: 32,
    179
    1
                num_heads: 32,
    180
    1
                num_kv_heads: 32,
    181
    1
                vocab_size: 51200,
    182
    1
                intermediate_dim: 10240,
    183
    1
                context_length: 2048,
    184
    1
                rope_theta: 10000.0,
    185
    1
                eps: 1e-5,
    186
    1
                rope_type: 0,
    187
    1
            };
    188
    189
    1
            let debug_str = format!("{:?}", config);
    190
    1
            assert!(debug_str.contains("phi2"));
    191
    1
            assert!(debug_str.contains("2560"));
    192
    1
        }
    193
    }