use serde::{Deserialize, Serialize};
use rvcsi_core::{CsiEvent, CsiWindow, RvcsiError, SourceId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct EmbeddingId(pub u64);
impl EmbeddingId {
#[inline]
pub const fn value(self) -> u64 {
self.0
}
}
impl From<u64> for EmbeddingId {
#[inline]
fn from(v: u64) -> Self {
EmbeddingId(v)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecordKind {
Window,
Event,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SimilarHit {
pub id: EmbeddingId,
pub score: f32,
pub kind: RecordKind,
pub source_id: SourceId,
pub timestamp_ns: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DriftReport {
pub room: String,
pub baseline_version: String,
pub distance: f32,
pub threshold: f32,
pub exceeded: bool,
}
pub trait RfMemoryStore {
fn store_window(&mut self, w: &CsiWindow) -> Result<EmbeddingId, RvcsiError>;
fn store_event(&mut self, e: &CsiEvent) -> Result<EmbeddingId, RvcsiError>;
fn query_similar(&self, query: &[f32], k: usize) -> Result<Vec<SimilarHit>, RvcsiError>;
fn set_baseline(
&mut self,
room: &str,
version: &str,
embedding: Vec<f32>,
) -> Result<(), RvcsiError>;
fn compute_drift(
&self,
room: &str,
current: &[f32],
threshold: f32,
) -> Result<Option<DriftReport>, RvcsiError>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embedding_id_roundtrips() {
let id = EmbeddingId::from(42);
assert_eq!(id.value(), 42);
let json = serde_json::to_string(&id).unwrap();
assert_eq!(serde_json::from_str::<EmbeddingId>(&json).unwrap(), id);
}
#[test]
fn value_objects_serde() {
let hit = SimilarHit {
id: EmbeddingId(1),
score: 0.9,
kind: RecordKind::Window,
source_id: SourceId::from("s"),
timestamp_ns: 5,
};
let json = serde_json::to_string(&hit).unwrap();
assert_eq!(serde_json::from_str::<SimilarHit>(&json).unwrap(), hit);
let d = DriftReport {
room: "lab".into(),
baseline_version: "v1".into(),
distance: 0.1,
threshold: 0.2,
exceeded: false,
};
let json = serde_json::to_string(&d).unwrap();
assert_eq!(serde_json::from_str::<DriftReport>(&json).unwrap(), d);
}
}