sqlite_graphrag/spawn/
mod.rs1pub mod claude_adapter;
8pub mod codex_adapter;
9pub mod compat_matrix;
10pub mod error_propagator;
11pub mod executor_version;
12pub mod opencode_adapter;
13
14use crate::errors::AppError;
15use async_trait::async_trait;
16use executor_version::ExecutorVersion;
17use std::collections::BTreeMap;
18use std::process::Stdio;
19
20#[derive(Debug, Clone)]
22pub struct ParsedOutput {
23 pub items: Vec<serde_json::Value>,
24 pub raw_stdout: String,
25 pub raw_stderr: String,
26 pub exit_code: i32,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ExecutorCapabilities {
32 pub supports_mcp_map: bool,
33 pub supports_ask_for_approval_flag: bool,
34 pub supports_strict_schema: bool,
35 pub default_flags: Vec<String>,
36 pub removed_flags: Vec<String>,
37}
38
39impl ExecutorCapabilities {
40 pub fn empty() -> Self {
41 Self {
42 supports_mcp_map: false,
43 supports_ask_for_approval_flag: false,
44 supports_strict_schema: false,
45 default_flags: Vec::new(),
46 removed_flags: Vec::new(),
47 }
48 }
49}
50
51#[async_trait]
53pub trait VersionAdapter: Send + Sync {
54 fn name(&self) -> &'static str;
56
57 async fn detect(&self) -> Result<ExecutorVersion, AppError>;
59
60 fn capabilities_for(&self, version: &ExecutorVersion) -> ExecutorCapabilities;
62
63 fn build_args(
65 &self,
66 prompt: &str,
67 caps: &ExecutorCapabilities,
68 compat_mode: CompatMode,
69 ) -> Vec<String>;
70
71 fn parse_output(&self, raw_stdout: &str, raw_stderr: &str, exit_code: i32) -> ParsedOutput;
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum CompatMode {
78 Strict,
80 Lenient,
82 Auto,
84}
85
86impl CompatMode {
87 pub fn parse(s: &str) -> Self {
88 match s.to_ascii_lowercase().as_str() {
89 "strict" => Self::Strict,
90 "lenient" => Self::Lenient,
91 _ => Self::Auto,
92 }
93 }
94}
95
96#[derive(Debug, Default)]
99pub struct VersionCache {
100 inner: std::sync::Mutex<BTreeMap<String, ExecutorVersion>>,
101}
102
103impl VersionCache {
104 pub fn new() -> Self {
105 Self::default()
106 }
107
108 pub fn get(&self, name: &str) -> Option<ExecutorVersion> {
109 self.inner.lock().ok().and_then(|m| m.get(name).cloned())
110 }
111
112 pub fn put(&self, name: &str, version: ExecutorVersion) {
113 if let Ok(mut m) = self.inner.lock() {
114 m.insert(name.to_string(), version);
115 }
116 }
117
118 pub fn clear(&self) {
119 if let Ok(mut m) = self.inner.lock() {
120 m.clear();
121 }
122 }
123}
124
125static VERSION_CACHE: std::sync::OnceLock<VersionCache> = std::sync::OnceLock::new();
126
127pub fn global_version_cache() -> &'static VersionCache {
128 VERSION_CACHE.get_or_init(VersionCache::new)
129}
130
131pub fn base_command(binary: &str) -> std::process::Command {
133 let mut cmd = std::process::Command::new(binary);
134 cmd.stdin(Stdio::null())
135 .stdout(Stdio::piped())
136 .stderr(Stdio::piped());
137 cmd
138}