Skip to main content

vulcan_luaskills/host/
database.rs

1use base64::Engine as _;
2use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::sync::{Arc, Mutex, OnceLock};
6
7/// Database access mode used by one host-facing runtime backend.
8/// 单个宿主侧运行时后端所使用的数据库访问模式。
9#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
10#[serde(rename_all = "snake_case")]
11pub enum LuaRuntimeDatabaseProviderMode {
12    /// The library loads and calls the local dynamic-library backend directly.
13    /// 由库直接加载并调用本地动态库后端。
14    #[default]
15    DynamicLibrary,
16    /// The library forwards database operations into one host-registered callback bridge.
17    /// 由库把数据库操作转发给宿主已注册的回调桥接。
18    HostCallback,
19    /// The library forwards database operations into one external space controller.
20    /// 由库把数据库操作转发给外部空间控制器。
21    SpaceController,
22}
23
24/// Callback transport mode used when the database provider mode is `host_callback`.
25/// 当数据库 provider 模式为 `host_callback` 时所使用的回调传输模式。
26#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
27#[serde(rename_all = "snake_case")]
28pub enum LuaRuntimeDatabaseCallbackMode {
29    /// The library uses the structured standard callback ABI.
30    /// 由库使用结构化标准回调 ABI。
31    #[default]
32    Standard,
33    /// The library uses the JSON callback ABI.
34    /// 由库使用 JSON 回调 ABI。
35    Json,
36}
37
38/// Logical database kind resolved for one provider request.
39/// 为单次 provider 请求解析出的逻辑数据库类型。
40#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
41#[serde(rename_all = "snake_case")]
42pub enum RuntimeDatabaseKind {
43    /// SQLite / FTS / BM25 backend operations.
44    /// SQLite / FTS / BM25 后端操作。
45    Sqlite,
46    /// LanceDB vector backend operations.
47    /// LanceDB 向量后端操作。
48    LanceDb,
49}
50
51/// Stable host-facing binding context for one skill-scoped database backend.
52/// 面向宿主的稳定 skill 级数据库后端绑定上下文。
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
54pub struct RuntimeDatabaseBindingContext {
55    /// Stable root label supplied by the host such as ROOT, USER, or one project label.
56    /// 由宿主提供的稳定根标签,例如 ROOT、USER 或某个项目标签。
57    pub space_label: String,
58    /// Stable skill identifier currently owning the database binding.
59    /// 当前拥有数据库绑定的稳定技能标识符。
60    pub skill_id: String,
61    /// Stable binding tag composed from the space label and skill id.
62    /// 由空间标签与技能标识符组合得到的稳定绑定标签。
63    pub binding_tag: String,
64    /// Physical skill root label currently resolving the effective skill instance.
65    /// 当前解析出生效技能实例时所命中的物理技能根标签。
66    pub root_name: String,
67    /// Physical skill root directory that owns the current effective skill instance.
68    /// 当前生效技能实例所属的物理技能根目录。
69    pub space_root: String,
70    /// Physical skill directory path.
71    /// 物理技能目录路径。
72    pub skill_dir: String,
73    /// Physical skill directory basename.
74    /// 物理技能目录名称。
75    pub skill_dir_name: String,
76    /// Logical database kind requested by the current provider binding.
77    /// 当前 provider 绑定请求的逻辑数据库类型。
78    pub database_kind: RuntimeDatabaseKind,
79    /// Default embedded database path resolved by the library for compatibility and diagnostics.
80    /// 由库按内嵌规则解析出的默认数据库路径,用于兼容和诊断。
81    pub default_database_path: String,
82}
83
84impl RuntimeDatabaseBindingContext {
85    /// Build one stable binding context from host-resolved root and skill information.
86    /// 基于宿主已解析的根信息与技能信息构造稳定绑定上下文。
87    pub fn new(
88        space_label: impl Into<String>,
89        skill_id: impl Into<String>,
90        root_name: impl Into<String>,
91        space_root: impl Into<String>,
92        skill_dir: impl Into<String>,
93        skill_dir_name: impl Into<String>,
94        database_kind: RuntimeDatabaseKind,
95        default_database_path: impl Into<String>,
96    ) -> Self {
97        let space_label = space_label.into();
98        let skill_id = skill_id.into();
99        Self {
100            binding_tag: format!("{}-{}", space_label, skill_id),
101            space_label,
102            skill_id,
103            root_name: root_name.into(),
104            space_root: space_root.into(),
105            skill_dir: skill_dir.into(),
106            skill_dir_name: skill_dir_name.into(),
107            database_kind,
108            default_database_path: default_database_path.into(),
109        }
110    }
111}
112
113/// Structured SQLite provider action routed through one host bridge.
114/// 通过宿主桥接路由的结构化 SQLite provider 动作。
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
116#[serde(rename_all = "snake_case")]
117pub enum RuntimeSqliteProviderAction {
118    /// Execute one SQL script or one single SQL statement.
119    /// 执行一个 SQL 脚本或单条 SQL 语句。
120    ExecuteScript,
121    /// Execute one batch SQL write request.
122    /// 执行一次批量 SQL 写入请求。
123    ExecuteBatch,
124    /// Execute one JSON row-set query.
125    /// 执行一次 JSON 行集查询。
126    QueryJson,
127    /// Create one query-stream handle.
128    /// 创建一个查询流句柄。
129    QueryStream,
130    /// Wait for query-stream metrics.
131    /// 等待查询流统计信息。
132    QueryStreamWaitMetrics,
133    /// Read one query-stream chunk.
134    /// 读取一个查询流分块。
135    QueryStreamChunk,
136    /// Close one query-stream handle.
137    /// 关闭一个查询流句柄。
138    QueryStreamClose,
139    /// Execute text tokenization.
140    /// 执行文本分词。
141    TokenizeText,
142    /// Upsert one custom dictionary word.
143    /// 写入或更新一个自定义词。
144    UpsertCustomWord,
145    /// Remove one custom dictionary word.
146    /// 删除一个自定义词。
147    RemoveCustomWord,
148    /// List current custom dictionary words.
149    /// 列出当前自定义词。
150    ListCustomWords,
151    /// Ensure one FTS index exists.
152    /// 确保一个 FTS 索引存在。
153    EnsureFtsIndex,
154    /// Rebuild one FTS index.
155    /// 重建一个 FTS 索引。
156    RebuildFtsIndex,
157    /// Upsert one FTS document.
158    /// 写入或更新一条 FTS 文档。
159    UpsertFtsDocument,
160    /// Delete one FTS document.
161    /// 删除一条 FTS 文档。
162    DeleteFtsDocument,
163    /// Execute one standardized FTS/BM25 search.
164    /// 执行一次标准化 FTS/BM25 检索。
165    SearchFts,
166}
167
168/// Structured LanceDB provider action routed through one host bridge.
169/// 通过宿主桥接路由的结构化 LanceDB provider 动作。
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
171#[serde(rename_all = "snake_case")]
172pub enum RuntimeLanceDbProviderAction {
173    /// Create one table.
174    /// 创建一张表。
175    CreateTable,
176    /// Upsert vectors into one table.
177    /// 向一张表写入向量。
178    VectorUpsert,
179    /// Search vectors from one table.
180    /// 从一张表检索向量。
181    VectorSearch,
182    /// Delete rows from one table.
183    /// 从一张表删除行。
184    Delete,
185    /// Drop one table.
186    /// 删除一张表。
187    DropTable,
188}
189
190/// Structured SQLite provider request delivered to one host bridge.
191/// 传递给宿主桥接的结构化 SQLite provider 请求。
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub struct RuntimeSqliteProviderRequest {
194    /// Requested SQLite provider action.
195    /// 请求的 SQLite provider 动作。
196    pub action: RuntimeSqliteProviderAction,
197    /// Stable binding context of the current skill-scoped database.
198    /// 当前 skill 级数据库的稳定绑定上下文。
199    pub binding: RuntimeDatabaseBindingContext,
200    /// Action-specific JSON input payload.
201    /// 动作对应的 JSON 输入载荷。
202    pub input: Value,
203}
204
205/// Structured LanceDB provider request delivered to one host bridge.
206/// 传递给宿主桥接的结构化 LanceDB provider 请求。
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
208pub struct RuntimeLanceDbProviderRequest {
209    /// Requested LanceDB provider action.
210    /// 请求的 LanceDB provider 动作。
211    pub action: RuntimeLanceDbProviderAction,
212    /// Stable binding context of the current skill-scoped database.
213    /// 当前 skill 级数据库的稳定绑定上下文。
214    pub binding: RuntimeDatabaseBindingContext,
215    /// Action-specific JSON input payload.
216    /// 动作对应的 JSON 输入载荷。
217    pub input: Value,
218}
219
220/// Standard host callback used for one structured SQLite provider request.
221/// 用于处理结构化 SQLite provider 请求的标准宿主回调。
222pub type RuntimeSqliteProviderCallback =
223    Arc<dyn Fn(&RuntimeSqliteProviderRequest) -> Result<Value, String> + Send + Sync>;
224
225/// Standard host callback used for one structured LanceDB provider request.
226/// 用于处理结构化 LanceDB provider 请求的标准宿主回调。
227pub type RuntimeLanceDbProviderCallback = Arc<
228    dyn Fn(&RuntimeLanceDbProviderRequest) -> Result<RuntimeLanceDbProviderResult, String>
229        + Send
230        + Sync,
231>;
232
233/// JSON host callback used for one SQLite provider request.
234/// 用于处理 SQLite provider 请求的 JSON 宿主回调。
235pub type RuntimeSqliteProviderJsonCallback =
236    Arc<dyn Fn(&str) -> Result<String, String> + Send + Sync>;
237
238/// JSON host callback used for one LanceDB provider request.
239/// 用于处理 LanceDB provider 请求的 JSON 宿主回调。
240pub type RuntimeLanceDbProviderJsonCallback =
241    Arc<dyn Fn(&str) -> Result<String, String> + Send + Sync>;
242
243/// One engine-scoped snapshot of all database provider callbacks visible at creation time.
244/// 一个在引擎创建时快照出的数据库 provider 回调集合,作用域限定为单个引擎实例。
245#[derive(Clone, Default)]
246pub(crate) struct RuntimeDatabaseProviderCallbacks {
247    /// Structured SQLite callback captured for the current engine snapshot.
248    /// 当前引擎快照捕获到的结构化 SQLite 回调。
249    sqlite_standard: Option<RuntimeSqliteProviderCallback>,
250    /// Structured LanceDB callback captured for the current engine snapshot.
251    /// 当前引擎快照捕获到的结构化 LanceDB 回调。
252    lancedb_standard: Option<RuntimeLanceDbProviderCallback>,
253    /// JSON SQLite callback captured for the current engine snapshot.
254    /// 当前引擎快照捕获到的 JSON SQLite 回调。
255    sqlite_json: Option<RuntimeSqliteProviderJsonCallback>,
256    /// JSON LanceDB callback captured for the current engine snapshot.
257    /// 当前引擎快照捕获到的 JSON LanceDB 回调。
258    lancedb_json: Option<RuntimeLanceDbProviderJsonCallback>,
259}
260
261impl RuntimeDatabaseProviderCallbacks {
262    /// Snapshot the current process-wide callback defaults into one engine-private registry.
263    /// 把当前进程级默认回调快照为一个引擎私有注册表。
264    pub(crate) fn capture_process_defaults() -> Result<Self, String> {
265        Ok(Self {
266            sqlite_standard: take_optional_callback(sqlite_provider_callback_registry())?,
267            lancedb_standard: take_optional_callback(lancedb_provider_callback_registry())?,
268            sqlite_json: take_optional_callback(sqlite_provider_json_callback_registry())?,
269            lancedb_json: take_optional_callback(lancedb_provider_json_callback_registry())?,
270        })
271    }
272
273    /// Return whether the snapshot contains one SQLite callback for the requested transport mode.
274    /// 返回当前快照是否包含指定传输模式的 SQLite 回调。
275    pub(crate) fn has_sqlite_provider_callback_for_mode(
276        &self,
277        callback_mode: LuaRuntimeDatabaseCallbackMode,
278    ) -> bool {
279        match callback_mode {
280            LuaRuntimeDatabaseCallbackMode::Standard => self.sqlite_standard.is_some(),
281            LuaRuntimeDatabaseCallbackMode::Json => self.sqlite_json.is_some(),
282        }
283    }
284
285    /// Return whether the snapshot contains one LanceDB callback for the requested transport mode.
286    /// 返回当前快照是否包含指定传输模式的 LanceDB 回调。
287    pub(crate) fn has_lancedb_provider_callback_for_mode(
288        &self,
289        callback_mode: LuaRuntimeDatabaseCallbackMode,
290    ) -> bool {
291        match callback_mode {
292            LuaRuntimeDatabaseCallbackMode::Standard => self.lancedb_standard.is_some(),
293            LuaRuntimeDatabaseCallbackMode::Json => self.lancedb_json.is_some(),
294        }
295    }
296
297    /// Dispatch one SQLite provider request through the callbacks captured by this snapshot.
298    /// 通过当前快照捕获的回调分发一次 SQLite provider 请求。
299    pub(crate) fn dispatch_sqlite_provider_request(
300        &self,
301        request: &RuntimeSqliteProviderRequest,
302        callback_mode: LuaRuntimeDatabaseCallbackMode,
303    ) -> Result<Value, String> {
304        match callback_mode {
305            LuaRuntimeDatabaseCallbackMode::Standard => {
306                let callback = self.sqlite_standard.clone().ok_or_else(|| {
307                    "SQLite host-callback mode requires one registered standard callback"
308                        .to_string()
309                })?;
310                callback(request)
311            }
312            LuaRuntimeDatabaseCallbackMode::Json => {
313                let callback = self.sqlite_json.clone().ok_or_else(|| {
314                    "SQLite host-callback JSON mode requires one registered JSON callback"
315                        .to_string()
316                })?;
317                let request_json = serde_json::to_string(request).map_err(|error| {
318                    format!("failed to encode sqlite provider request: {}", error)
319                })?;
320                let response_json = callback(&request_json)?;
321                serde_json::from_str::<Value>(&response_json).map_err(|error| {
322                    format!("failed to parse sqlite provider response json: {}", error)
323                })
324            }
325        }
326    }
327
328    /// Dispatch one LanceDB provider request through the callbacks captured by this snapshot.
329    /// 通过当前快照捕获的回调分发一次 LanceDB provider 请求。
330    pub(crate) fn dispatch_lancedb_provider_request(
331        &self,
332        request: &RuntimeLanceDbProviderRequest,
333        callback_mode: LuaRuntimeDatabaseCallbackMode,
334    ) -> Result<RuntimeLanceDbProviderResult, String> {
335        match callback_mode {
336            LuaRuntimeDatabaseCallbackMode::Standard => {
337                let callback = self.lancedb_standard.clone().ok_or_else(|| {
338                    "LanceDB host-callback mode requires one registered standard callback"
339                        .to_string()
340                })?;
341                callback(request)
342            }
343            LuaRuntimeDatabaseCallbackMode::Json => {
344                let callback = self.lancedb_json.clone().ok_or_else(|| {
345                    "LanceDB host-callback JSON mode requires one registered JSON callback"
346                        .to_string()
347                })?;
348                let request_json = serde_json::to_string(request).map_err(|error| {
349                    format!("failed to encode lancedb provider request: {}", error)
350                })?;
351                let response_json = callback(&request_json)?;
352                let value: Value = serde_json::from_str(&response_json).map_err(|error| {
353                    format!("failed to parse lancedb provider response json: {}", error)
354                })?;
355                let meta = value
356                    .get("meta")
357                    .cloned()
358                    .unwrap_or_else(|| Value::Object(Default::default()));
359                let bytes = value
360                    .get("data_base64")
361                    .and_then(Value::as_str)
362                    .map(|text| {
363                        BASE64_STANDARD.decode(text.as_bytes()).map_err(|error| {
364                            format!("failed to decode lancedb provider data_base64: {}", error)
365                        })
366                    })
367                    .transpose()?
368                    .unwrap_or_default();
369                Ok(RuntimeLanceDbProviderResult::binary(meta, bytes))
370            }
371        }
372    }
373}
374
375/// Structured LanceDB provider result returned by the standard host callback.
376/// 标准宿主回调返回的结构化 LanceDB provider 结果。
377#[derive(Debug, Clone, PartialEq)]
378pub struct RuntimeLanceDbProviderResult {
379    /// Response metadata JSON.
380    /// 响应元信息 JSON。
381    pub meta: Value,
382    /// Optional raw payload bytes such as vector search result data.
383    /// 可选原始载荷字节,例如向量检索结果数据。
384    pub bytes: Vec<u8>,
385}
386
387impl RuntimeLanceDbProviderResult {
388    /// Build one result carrying only metadata JSON.
389    /// 构造一个仅携带元信息 JSON 的结果。
390    pub fn json(meta: Value) -> Self {
391        Self {
392            meta,
393            bytes: Vec::new(),
394        }
395    }
396
397    /// Build one result carrying metadata JSON plus raw bytes.
398    /// 构造一个携带元信息 JSON 和原始字节的结果。
399    pub fn binary(meta: Value, bytes: Vec<u8>) -> Self {
400        Self { meta, bytes }
401    }
402}
403
404/// Install or clear the process-wide standard SQLite provider callback.
405/// 安装或清理进程级标准 SQLite provider 回调。
406pub fn set_sqlite_provider_callback(callback: Option<RuntimeSqliteProviderCallback>) {
407    let registry = sqlite_provider_callback_registry();
408    let mut guard = registry.lock().unwrap();
409    *guard = callback;
410}
411
412/// Install or clear the process-wide standard LanceDB provider callback.
413/// 安装或清理进程级标准 LanceDB provider 回调。
414pub fn set_lancedb_provider_callback(callback: Option<RuntimeLanceDbProviderCallback>) {
415    let registry = lancedb_provider_callback_registry();
416    let mut guard = registry.lock().unwrap();
417    *guard = callback;
418}
419
420/// Install or clear the process-wide JSON SQLite provider callback.
421/// 安装或清理进程级 JSON SQLite provider 回调。
422pub fn set_sqlite_provider_json_callback(callback: Option<RuntimeSqliteProviderJsonCallback>) {
423    let registry = sqlite_provider_json_callback_registry();
424    let mut guard = registry.lock().unwrap();
425    *guard = callback;
426}
427
428/// Install or clear the process-wide JSON LanceDB provider callback.
429/// 安装或清理进程级 JSON LanceDB provider 回调。
430pub fn set_lancedb_provider_json_callback(callback: Option<RuntimeLanceDbProviderJsonCallback>) {
431    let registry = lancedb_provider_json_callback_registry();
432    let mut guard = registry.lock().unwrap();
433    *guard = callback;
434}
435
436/// Read one optional callback from one mutex registry without cloning error-prone lock code at each call site.
437/// 从一个互斥量注册表读取可选回调,避免在每个调用点重复编写易错的加锁逻辑。
438fn take_optional_callback<T: Clone>(
439    registry: &'static Mutex<Option<T>>,
440) -> Result<Option<T>, String> {
441    let guard = registry
442        .lock()
443        .map_err(|_| "Database provider callback registry lock poisoned".to_string())?;
444    Ok(guard.clone())
445}
446
447/// Return the process-wide standard SQLite provider callback storage.
448/// 返回进程级标准 SQLite provider 回调存储。
449fn sqlite_provider_callback_registry() -> &'static Mutex<Option<RuntimeSqliteProviderCallback>> {
450    static REGISTRY: OnceLock<Mutex<Option<RuntimeSqliteProviderCallback>>> = OnceLock::new();
451    REGISTRY.get_or_init(|| Mutex::new(None))
452}
453
454/// Return the process-wide standard LanceDB provider callback storage.
455/// 返回进程级标准 LanceDB provider 回调存储。
456fn lancedb_provider_callback_registry() -> &'static Mutex<Option<RuntimeLanceDbProviderCallback>> {
457    static REGISTRY: OnceLock<Mutex<Option<RuntimeLanceDbProviderCallback>>> = OnceLock::new();
458    REGISTRY.get_or_init(|| Mutex::new(None))
459}
460
461/// Return the process-wide JSON SQLite provider callback storage.
462/// 返回进程级 JSON SQLite provider 回调存储。
463fn sqlite_provider_json_callback_registry()
464-> &'static Mutex<Option<RuntimeSqliteProviderJsonCallback>> {
465    static REGISTRY: OnceLock<Mutex<Option<RuntimeSqliteProviderJsonCallback>>> = OnceLock::new();
466    REGISTRY.get_or_init(|| Mutex::new(None))
467}
468
469/// Return the process-wide JSON LanceDB provider callback storage.
470/// 返回进程级 JSON LanceDB provider 回调存储。
471fn lancedb_provider_json_callback_registry()
472-> &'static Mutex<Option<RuntimeLanceDbProviderJsonCallback>> {
473    static REGISTRY: OnceLock<Mutex<Option<RuntimeLanceDbProviderJsonCallback>>> = OnceLock::new();
474    REGISTRY.get_or_init(|| Mutex::new(None))
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480    use serde_json::json;
481    use std::sync::{Mutex, OnceLock};
482
483    /// Return a process-wide test lock so callback-registry tests do not race in parallel.
484    /// 返回一个进程级测试锁,避免回调注册表测试并发互相干扰。
485    fn database_callback_test_lock() -> &'static Mutex<()> {
486        static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
487        LOCK.get_or_init(|| Mutex::new(()))
488    }
489
490    /// Restore the process-wide callback defaults captured before one test mutates them.
491    /// 恢复某个测试修改前捕获到的进程级默认回调集合。
492    struct ProcessCallbackRestoreGuard {
493        snapshot: RuntimeDatabaseProviderCallbacks,
494    }
495
496    impl ProcessCallbackRestoreGuard {
497        /// Capture the current process-wide callback defaults so they can be restored on drop.
498        /// 捕获当前进程级默认回调,以便在释放时恢复。
499        fn capture() -> Self {
500            Self {
501                snapshot: RuntimeDatabaseProviderCallbacks::capture_process_defaults()
502                    .expect("capture callback snapshot"),
503            }
504        }
505    }
506
507    impl Drop for ProcessCallbackRestoreGuard {
508        fn drop(&mut self) {
509            set_sqlite_provider_callback(self.snapshot.sqlite_standard.clone());
510            set_lancedb_provider_callback(self.snapshot.lancedb_standard.clone());
511            set_sqlite_provider_json_callback(self.snapshot.sqlite_json.clone());
512            set_lancedb_provider_json_callback(self.snapshot.lancedb_json.clone());
513        }
514    }
515
516    /// Build one stable binding context used by snapshot-isolation tests.
517    /// 构造供快照隔离测试使用的稳定绑定上下文。
518    fn sample_binding_context(database_kind: RuntimeDatabaseKind) -> RuntimeDatabaseBindingContext {
519        RuntimeDatabaseBindingContext::new(
520            "ROOT",
521            "test-skill",
522            "ROOT",
523            "D:/runtime-test-root/__database",
524            "D:/runtime-test-root/skills/test-skill",
525            "test-skill",
526            database_kind,
527            "D:/runtime-test-root/__database/default.db",
528        )
529    }
530
531    /// Verify that each captured callback snapshot keeps routing to the callbacks visible at capture time.
532    /// 验证每个捕获到的回调快照都会持续路由到捕获当时可见的回调实现。
533    #[test]
534    fn captured_callback_snapshots_stay_engine_scoped() {
535        let _serial_guard = database_callback_test_lock()
536            .lock()
537            .expect("lock callback test guard");
538        let _restore_guard = ProcessCallbackRestoreGuard::capture();
539
540        set_sqlite_provider_callback(Some(Arc::new(|_| {
541            Ok(json!({ "source": "sqlite-standard-a" }))
542        })));
543        set_sqlite_provider_json_callback(Some(Arc::new(|_| {
544            Ok("{\"source\":\"sqlite-json-a\"}".to_string())
545        })));
546        set_lancedb_provider_callback(Some(Arc::new(|_| {
547            Ok(RuntimeLanceDbProviderResult::json(
548                json!({ "source": "lancedb-standard-a" }),
549            ))
550        })));
551        set_lancedb_provider_json_callback(Some(Arc::new(|_| {
552            Ok("{\"meta\":{\"source\":\"lancedb-json-a\"}}".to_string())
553        })));
554        let snapshot_a = RuntimeDatabaseProviderCallbacks::capture_process_defaults()
555            .expect("capture callback snapshot A");
556
557        set_sqlite_provider_callback(Some(Arc::new(|_| {
558            Ok(json!({ "source": "sqlite-standard-b" }))
559        })));
560        set_sqlite_provider_json_callback(Some(Arc::new(|_| {
561            Ok("{\"source\":\"sqlite-json-b\"}".to_string())
562        })));
563        set_lancedb_provider_callback(Some(Arc::new(|_| {
564            Ok(RuntimeLanceDbProviderResult::json(
565                json!({ "source": "lancedb-standard-b" }),
566            ))
567        })));
568        set_lancedb_provider_json_callback(Some(Arc::new(|_| {
569            Ok("{\"meta\":{\"source\":\"lancedb-json-b\"}}".to_string())
570        })));
571        let snapshot_b = RuntimeDatabaseProviderCallbacks::capture_process_defaults()
572            .expect("capture callback snapshot B");
573
574        let sqlite_request = RuntimeSqliteProviderRequest {
575            action: RuntimeSqliteProviderAction::QueryJson,
576            binding: sample_binding_context(RuntimeDatabaseKind::Sqlite),
577            input: json!({ "sql": "select 1" }),
578        };
579        let lancedb_request = RuntimeLanceDbProviderRequest {
580            action: RuntimeLanceDbProviderAction::VectorSearch,
581            binding: sample_binding_context(RuntimeDatabaseKind::LanceDb),
582            input: json!({ "table": "demo" }),
583        };
584
585        assert_eq!(
586            snapshot_a
587                .dispatch_sqlite_provider_request(
588                    &sqlite_request,
589                    LuaRuntimeDatabaseCallbackMode::Standard,
590                )
591                .expect("dispatch sqlite standard A"),
592            json!({ "source": "sqlite-standard-a" })
593        );
594        assert_eq!(
595            snapshot_a
596                .dispatch_sqlite_provider_request(
597                    &sqlite_request,
598                    LuaRuntimeDatabaseCallbackMode::Json,
599                )
600                .expect("dispatch sqlite json A"),
601            json!({ "source": "sqlite-json-a" })
602        );
603        assert_eq!(
604            snapshot_b
605                .dispatch_sqlite_provider_request(
606                    &sqlite_request,
607                    LuaRuntimeDatabaseCallbackMode::Standard,
608                )
609                .expect("dispatch sqlite standard B"),
610            json!({ "source": "sqlite-standard-b" })
611        );
612        assert_eq!(
613            snapshot_b
614                .dispatch_sqlite_provider_request(
615                    &sqlite_request,
616                    LuaRuntimeDatabaseCallbackMode::Json,
617                )
618                .expect("dispatch sqlite json B"),
619            json!({ "source": "sqlite-json-b" })
620        );
621
622        assert_eq!(
623            snapshot_a
624                .dispatch_lancedb_provider_request(
625                    &lancedb_request,
626                    LuaRuntimeDatabaseCallbackMode::Standard,
627                )
628                .expect("dispatch lancedb standard A")
629                .meta,
630            json!({ "source": "lancedb-standard-a" })
631        );
632        assert_eq!(
633            snapshot_a
634                .dispatch_lancedb_provider_request(
635                    &lancedb_request,
636                    LuaRuntimeDatabaseCallbackMode::Json,
637                )
638                .expect("dispatch lancedb json A")
639                .meta,
640            json!({ "source": "lancedb-json-a" })
641        );
642        assert_eq!(
643            snapshot_b
644                .dispatch_lancedb_provider_request(
645                    &lancedb_request,
646                    LuaRuntimeDatabaseCallbackMode::Standard,
647                )
648                .expect("dispatch lancedb standard B")
649                .meta,
650            json!({ "source": "lancedb-standard-b" })
651        );
652        assert_eq!(
653            snapshot_b
654                .dispatch_lancedb_provider_request(
655                    &lancedb_request,
656                    LuaRuntimeDatabaseCallbackMode::Json,
657                )
658                .expect("dispatch lancedb json B")
659                .meta,
660            json!({ "source": "lancedb-json-b" })
661        );
662    }
663}