Skip to main content

nodedb_types/
vector_model.rs

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