Skip to main content

semantic_memory/
quantize_governed.rs

1//! Governed compression pipeline integration for semantic-memory.
2//!
3//! This module evaluates `quant-governor` policy and produces an artifact only
4//! when the selected runtime codec has a real encoder. It never relabels raw
5//! bytes as a compressed representation.
6
7#[cfg(feature = "turbo-quant-codec")]
8pub mod governed {
9    use quant_governor::{
10        AdmissibilityClass, CodecProfile, ContentType, GovernancePolicy, GovernanceRequest,
11    };
12
13    /// Result of governed encoding — encoded bytes plus policy metadata.
14    #[derive(Debug, Clone)]
15    pub struct GovernedEncodeResult {
16        /// Bytes in the representation selected by `codec_profile`.
17        pub compressed_bytes: Vec<u8>,
18        /// Codec profile selected by the governance policy.
19        pub codec_profile: CodecProfile,
20        /// Governance receipt ID for the audit trail.
21        pub governance_receipt_id: String,
22        /// Allowed degradation budget from the policy decision.
23        pub degradation_budget: f64,
24    }
25
26    /// Encode only profiles backed by a concrete runtime encoder.
27    ///
28    /// `scr-runtime-compression` currently provides strict decode adapters but
29    /// no registered TurboQuant/FibQuant encoder. Returning an error for those
30    /// profiles preserves artifact truth: callers cannot mistake raw f32 bytes
31    /// for a compressed representation.
32    pub(super) fn encode_selected_profile(
33        embedding: &[f32],
34        profile: &CodecProfile,
35    ) -> Result<Vec<u8>, String> {
36        match profile {
37            CodecProfile::Raw => Ok(bytemuck::cast_slice(embedding).to_vec()),
38            CodecProfile::Q8 => Err("governed q8 encoding is not implemented".to_string()),
39            CodecProfile::Q4 => Err("governed q4 encoding is not implemented".to_string()),
40            CodecProfile::Turbo => Err(
41                "governed turbo-quant encoding is unavailable: no runtime encoder is registered"
42                    .to_string(),
43            ),
44            CodecProfile::Fib => Err(
45                "governed fib-quant encoding is unavailable: no runtime encoder is registered"
46                    .to_string(),
47            ),
48        }
49    }
50
51    /// Encode an embedding vector through the governed compression pipeline.
52    ///
53    /// The caller receives bytes only when their representation is truthful.
54    /// Unsupported compression decisions fail explicitly instead of silently
55    /// substituting the uncompressed representation.
56    pub fn encode_governed(
57        embedding: &[f32],
58        policy: &GovernancePolicy,
59    ) -> Result<GovernedEncodeResult, String> {
60        let request = GovernanceRequest {
61            content_type: ContentType::Structured,
62            size_bytes: (embedding.len() * std::mem::size_of::<f32>()) as u64,
63            accuracy_requirement: 0.99,
64            latency_tolerance_ms: 500,
65            admissibility: AdmissibilityClass::Standard,
66        };
67
68        let decision = policy.evaluate(request).map_err(|e| e.to_string())?;
69        let encoded = encode_selected_profile(embedding, &decision.codec)?;
70
71        Ok(GovernedEncodeResult {
72            compressed_bytes: encoded,
73            codec_profile: decision.codec,
74            governance_receipt_id: format!("gr-{}", uuid::Uuid::new_v4()),
75            degradation_budget: decision.degradation_budget,
76        })
77    }
78
79    /// Encode with governance using the default policy.
80    pub fn encode_governed_default(embedding: &[f32]) -> Result<GovernedEncodeResult, String> {
81        encode_governed(embedding, &GovernancePolicy::default())
82    }
83}
84
85#[cfg(not(feature = "turbo-quant-codec"))]
86pub mod governed {
87    //! Stub module when `turbo-quant-codec` is not enabled.
88
89    /// Stub result type — codec_profile is a string when feature is disabled.
90    #[derive(Debug, Clone)]
91    pub struct GovernedEncodeResult {
92        pub compressed_bytes: Vec<u8>,
93        pub codec_profile: String,
94        pub governance_receipt_id: String,
95        pub degradation_budget: f64,
96    }
97
98    /// Returns an error indicating the turbo-quant-codec feature is not enabled.
99    pub fn encode_governed(
100        _embedding: &[f32],
101        _policy: (),
102    ) -> Result<GovernedEncodeResult, String> {
103        Err("turbo-quant-codec feature is not enabled".to_string())
104    }
105
106    /// Returns an error indicating the turbo-quant-codec feature is not enabled.
107    pub fn encode_governed_default(_embedding: &[f32]) -> Result<GovernedEncodeResult, String> {
108        Err("turbo-quant-codec feature is not enabled".to_string())
109    }
110}
111
112pub use governed::{encode_governed, encode_governed_default, GovernedEncodeResult};
113
114#[cfg(all(test, feature = "turbo-quant-codec"))]
115mod tests {
116    use super::governed::{encode_governed_default, encode_selected_profile};
117    use quant_governor::CodecProfile;
118
119    #[test]
120    fn raw_governed_encoding_preserves_f32_wire_bytes() {
121        let embedding = vec![1.25_f32, -2.5, 0.0, 4.75];
122        let result = encode_governed_default(&embedding).expect("default policy selects raw");
123        assert_eq!(result.codec_profile, CodecProfile::Raw);
124        assert_eq!(
125            result.compressed_bytes,
126            bytemuck::cast_slice::<f32, u8>(&embedding)
127        );
128        assert!(result.governance_receipt_id.starts_with("gr-"));
129    }
130
131    #[test]
132    fn unavailable_compression_profiles_fail_closed() {
133        let embedding = vec![0.25_f32; 16];
134        for profile in [
135            CodecProfile::Q8,
136            CodecProfile::Q4,
137            CodecProfile::Turbo,
138            CodecProfile::Fib,
139        ] {
140            let error = encode_selected_profile(&embedding, &profile)
141                .expect_err("unsupported profile must not be relabeled as raw bytes");
142            assert!(error.contains("encoding"));
143        }
144    }
145}