khive_runtime/
backend_handle.rs1use std::fmt;
4use std::sync::Arc;
5
6use khive_db::StorageBackend;
7use khive_storage::{
8 EntityStore, EventStore, GraphStore, NoteStore, SparseStore, SparseStoreFactory, SqlAccess,
9 StorageError, TextSearch, TextSearchFactory, VectorStore, VectorStoreFactory,
10};
11use thiserror::Error;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum RetrievalTier {
16 Vector,
17 Sparse,
18 Text,
19}
20
21impl fmt::Display for RetrievalTier {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.write_str(match self {
24 Self::Vector => "vector",
25 Self::Sparse => "sparse",
26 Self::Text => "text",
27 })
28 }
29}
30
31#[derive(Debug, Error)]
33pub enum BackendHandleError {
34 #[error("backend does not provide the {tier} capability")]
35 MissingCapability { tier: RetrievalTier },
36 #[error(transparent)]
37 Storage(#[from] StorageError),
38}
39
40#[derive(Clone)]
42pub struct BackendHandle {
43 entity: Arc<dyn EntityStore>,
44 note: Arc<dyn NoteStore>,
45 graph: Arc<dyn GraphStore>,
46 event: Arc<dyn EventStore>,
47 sql: Arc<dyn SqlAccess>,
48 vector: Option<Arc<dyn VectorStoreFactory>>,
49 sparse: Option<Arc<dyn SparseStoreFactory>>,
50 text: Option<Arc<dyn TextSearchFactory>>,
51}
52
53impl BackendHandle {
54 pub fn from_sqlite(backend: Arc<StorageBackend>) -> Self {
56 Self {
57 entity: backend.clone(),
58 note: backend.clone(),
59 graph: backend.clone(),
60 event: backend.clone(),
61 sql: backend.clone(),
62 vector: Some(backend.clone()),
63 sparse: Some(backend.clone()),
64 text: Some(backend),
65 }
66 }
67
68 #[allow(clippy::too_many_arguments)]
70 pub fn from_parts(
71 entity: Arc<dyn EntityStore>,
72 note: Arc<dyn NoteStore>,
73 graph: Arc<dyn GraphStore>,
74 event: Arc<dyn EventStore>,
75 sql: Arc<dyn SqlAccess>,
76 vector: Option<Arc<dyn VectorStoreFactory>>,
77 sparse: Option<Arc<dyn SparseStoreFactory>>,
78 text: Option<Arc<dyn TextSearchFactory>>,
79 ) -> Self {
80 Self {
81 entity,
82 note,
83 graph,
84 event,
85 sql,
86 vector,
87 sparse,
88 text,
89 }
90 }
91
92 pub fn entity(&self) -> &Arc<dyn EntityStore> {
93 &self.entity
94 }
95
96 pub fn note(&self) -> &Arc<dyn NoteStore> {
97 &self.note
98 }
99
100 pub fn graph(&self) -> &Arc<dyn GraphStore> {
101 &self.graph
102 }
103
104 pub fn event(&self) -> &Arc<dyn EventStore> {
105 &self.event
106 }
107
108 pub fn sql(&self) -> &Arc<dyn SqlAccess> {
109 &self.sql
110 }
111
112 pub fn vector(
113 &self,
114 model_key: &str,
115 embedding_model: &str,
116 dimensions: usize,
117 namespace: &str,
118 ) -> Result<Arc<dyn VectorStore>, BackendHandleError> {
119 self.vector
120 .as_ref()
121 .ok_or(BackendHandleError::MissingCapability {
122 tier: RetrievalTier::Vector,
123 })?
124 .open(model_key, embedding_model, dimensions, namespace)
125 .map_err(BackendHandleError::Storage)
126 }
127
128 pub fn sparse(
129 &self,
130 model_key: &str,
131 namespace: &str,
132 ) -> Result<Arc<dyn SparseStore>, BackendHandleError> {
133 self.sparse
134 .as_ref()
135 .ok_or(BackendHandleError::MissingCapability {
136 tier: RetrievalTier::Sparse,
137 })?
138 .open(model_key, namespace)
139 .map_err(BackendHandleError::Storage)
140 }
141
142 pub fn text(
143 &self,
144 table_key: &str,
145 tokenizer: &str,
146 ) -> Result<Arc<dyn TextSearch>, BackendHandleError> {
147 self.text
148 .as_ref()
149 .ok_or(BackendHandleError::MissingCapability {
150 tier: RetrievalTier::Text,
151 })?
152 .open(table_key, tokenizer)
153 .map_err(BackendHandleError::Storage)
154 }
155}