Skip to main content

orbok_models/
lib.rs

1//! # orbok-models
2//!
3//! Local AI model vocabulary (RFC-012). Milestone M1–M6 only needs the
4//! shared types and the "what is available" summary the UI shows; the
5//! install/locate/validate workflow lands in M12.
6//!
7//! Privacy rule carried from the requirements: model *download* is the
8//! only network operation orbok may ever perform, it is explicit, and
9//! it never involves document contents.
10
11use serde::{Deserialize, Serialize};
12
13/// Model roles (catalog `models.role`).
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum ModelRole {
17    Embedding,
18    Reranker,
19}
20
21impl ModelRole {
22    pub fn as_str(&self) -> &'static str {
23        match self {
24            ModelRole::Embedding => "embedding",
25            ModelRole::Reranker => "reranker",
26        }
27    }
28}
29
30/// Model availability (catalog `models.status`).
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum ModelStatus {
34    Available,
35    Missing,
36    Invalid,
37    Installing,
38    Disabled,
39}
40
41impl ModelStatus {
42    pub fn as_str(&self) -> &'static str {
43        match self {
44            ModelStatus::Available => "available",
45            ModelStatus::Missing => "missing",
46            ModelStatus::Invalid => "invalid",
47            ModelStatus::Installing => "installing",
48            ModelStatus::Disabled => "disabled",
49        }
50    }
51}
52
53/// Search capability derived from model availability. Keyword search
54/// never depends on models (RFC-007: works with zero models installed).
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum SearchCapability {
57    /// Keyword only: no embedding model available.
58    KeywordOnly,
59    /// Keyword + semantic: embedding model available.
60    Hybrid,
61    /// Keyword + semantic + rerank refinement.
62    HybridWithRerank,
63}
64
65/// Derive the capability shown in the UI from model statuses.
66pub fn search_capability(
67    embedding: Option<ModelStatus>,
68    reranker: Option<ModelStatus>,
69) -> SearchCapability {
70    match (embedding, reranker) {
71        (Some(ModelStatus::Available), Some(ModelStatus::Available)) => {
72            SearchCapability::HybridWithRerank
73        }
74        (Some(ModelStatus::Available), _) => SearchCapability::Hybrid,
75        _ => SearchCapability::KeywordOnly,
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    // RFC-007/RFC-010: search degrades gracefully without models.
84    #[test]
85    fn capability_degrades_gracefully() {
86        assert_eq!(search_capability(None, None), SearchCapability::KeywordOnly);
87        assert_eq!(
88            search_capability(Some(ModelStatus::Missing), None),
89            SearchCapability::KeywordOnly
90        );
91        assert_eq!(
92            search_capability(Some(ModelStatus::Available), None),
93            SearchCapability::Hybrid
94        );
95        assert_eq!(
96            search_capability(Some(ModelStatus::Available), Some(ModelStatus::Missing)),
97            SearchCapability::Hybrid
98        );
99        assert_eq!(
100            search_capability(Some(ModelStatus::Available), Some(ModelStatus::Available)),
101            SearchCapability::HybridWithRerank
102        );
103    }
104}