Skip to main content

nodedb_types/
vector_model.rs

1//! Per-column vector model metadata for embedding tracking and migration.
2//!
3//! Stored in the system catalog keyed by `(tenant_id, collection, column)`.
4//! Informational by default; optional strict dimension enforcement.
5
6use serde::{Deserialize, Serialize};
7
8/// Metadata about the embedding model that produced vectors in a column.
9///
10/// Stored in the system catalog via `ALTER COLLECTION x SET VECTOR METADATA ON column (...)`.
11/// Retrieved via `SELECT VECTOR_METADATA('collection', 'column')` or `SHOW VECTOR MODELS`.
12#[derive(
13    Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
14)]
15pub struct VectorModelMetadata {
16    /// Embedding model identifier (e.g. "text-embedding-3-large", "all-MiniLM-L6-v2").
17    pub model: String,
18    /// Expected vector dimensionality for this model.
19    pub dimensions: usize,
20    /// ISO-8601 date when the metadata was set (informational).
21    pub created_at: String,
22    /// If true, INSERT rejects vectors whose dimension doesn't match `dimensions`.
23    /// Default: false (no enforcement — metadata only).
24    #[serde(default)]
25    pub strict_dimensions: bool,
26}
27
28/// Catalog entry for vector model metadata.
29///
30/// Wraps `VectorModelMetadata` with the key fields needed for catalog storage
31/// and the `_system.vector_models` view.
32#[derive(
33    Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
34)]
35pub struct VectorModelEntry {
36    /// Tenant that owns this collection.
37    pub tenant_id: u32,
38    /// Collection name.
39    pub collection: String,
40    /// Vector column name.
41    pub column: String,
42    /// The model metadata.
43    pub metadata: VectorModelMetadata,
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn serde_roundtrip() {
52        let entry = VectorModelEntry {
53            tenant_id: 1,
54            collection: "chunks".into(),
55            column: "embedding".into(),
56            metadata: VectorModelMetadata {
57                model: "text-embedding-3-large".into(),
58                dimensions: 1536,
59                created_at: "2026-03-01".into(),
60                strict_dimensions: false,
61            },
62        };
63        let bytes = zerompk::to_msgpack_vec(&entry).unwrap();
64        let restored: VectorModelEntry = zerompk::from_msgpack(&bytes).unwrap();
65        assert_eq!(restored.metadata.model, "text-embedding-3-large");
66        assert_eq!(restored.metadata.dimensions, 1536);
67        assert!(!restored.metadata.strict_dimensions);
68    }
69
70    #[test]
71    fn strict_dimensions_default_false() {
72        // Deserialize without strict_dimensions field → defaults to false.
73        let meta = VectorModelMetadata {
74            model: "test".into(),
75            dimensions: 128,
76            created_at: "2026-01-01".into(),
77            strict_dimensions: false,
78        };
79        let bytes = zerompk::to_msgpack_vec(&meta).unwrap();
80        let restored: VectorModelMetadata = zerompk::from_msgpack(&bytes).unwrap();
81        assert!(!restored.strict_dimensions);
82    }
83}