offline_intelligence/engine_management/
mod.rs1pub mod registry;
12pub mod downloader;
13pub mod analyzer;
14pub mod download_progress;
15
16pub use registry::{EngineRegistry, EngineInfo, EngineStatus, AccelerationType};
17pub use downloader::{EngineDownloader, EngineSource};
18pub use analyzer::{HardwareAnalyzer, HardwareProfile};
19pub use download_progress::{EngineDownloadProgressTracker, EngineDownloadProgress, EngineDownloadStatus};
20
21use anyhow::Result;
22use std::sync::Arc;
23use tokio::sync::RwLock;
24
25use crate::config::Config;
26use crate::model_runtime::platform_detector::HardwareCapabilities;
27
28pub struct EngineManager {
30 pub registry: Arc<RwLock<EngineRegistry>>,
31 pub downloader: Arc<EngineDownloader>,
32 pub analyzer: Arc<HardwareAnalyzer>,
33 pub hardware_capabilities: HardwareCapabilities,
34}
35
36impl EngineManager {
37 pub fn new() -> Result<Self> {
38 let hardware_capabilities = HardwareCapabilities::detect();
39 let analyzer = Arc::new(HardwareAnalyzer::new(hardware_capabilities.clone()));
40 let registry = Arc::new(RwLock::new(EngineRegistry::new()?));
41 let downloader = Arc::new(EngineDownloader::new());
42
43 Ok(Self {
44 registry,
45 downloader,
46 analyzer,
47 hardware_capabilities,
48 })
49 }
50
51 pub async fn initialize(&self, _cfg: &Config) -> Result<bool> {
53 {
56 let mut registry = self.registry.write().await;
57 registry.scan_installed_engines(&self.hardware_capabilities).await?;
58 }
59
60 let installed_engines_count = self.registry.read().await.installed_engines.len();
62
63 if installed_engines_count == 0 {
64 tracing::info!("First run detected - no engines installed, automatically downloading most compatible engine");
65
66 match tokio::time::timeout(
69 std::time::Duration::from_secs(600),
70 self.download_suitable_engine()
71 ).await {
72 Ok(Ok(engine)) => {
73 tracing::info!("✅ Engine downloaded successfully on first run: {}", engine.name);
74 let mut reg = self.registry.write().await;
76 if let Some(first_engine_id) = reg.installed_engines.keys().next().cloned() {
77 reg.set_default_engine(&first_engine_id)?;
78 }
79 return Ok(true);
80 }
81 Ok(Err(e)) => {
82 tracing::warn!("⚠️ Engine download failed: {}. App will continue but models won't work until engine is downloaded.", e);
83 return Ok(false); }
85 Err(_) => {
86 tracing::warn!("⚠️ Engine download timed out after 600 seconds (10 minutes). App will continue with background retry.");
87 return Ok(false);
88 }
89 }
90 } else {
91 let has_suitable_engine = self.check_suitable_engine().await?;
93
94 if !has_suitable_engine {
95 tracing::info!("No suitable engine found for current hardware configuration");
96 tracing::info!("Attempting to download most compatible engine");
97
98 match self.download_suitable_engine().await {
99 Ok(engine) => {
100 tracing::info!("Automatically installed engine: {}", engine.name);
101 self.registry.write().await.set_default_engine(&engine.id)?;
103 }
104 Err(e) => {
105 tracing::warn!("Failed to automatically install suitable engine: {}", e);
106 }
109 }
110 } else {
111 self.select_best_engine().await?;
113 }
114 }
115
116 Ok(true)
117 }
118
119 pub async fn check_suitable_engine(&self) -> Result<bool> {
121 let registry = self.registry.read().await;
122 let suitable_engines = registry.get_compatible_engines(&self.hardware_capabilities);
123 Ok(!suitable_engines.is_empty())
124 }
125
126 pub async fn select_best_engine(&self) -> Result<Option<EngineInfo>> {
128 let mut registry = self.registry.write().await;
129 let best_engine = registry.select_best_compatible_engine(&self.hardware_capabilities);
130
131 if let Some(engine) = &best_engine {
132 registry.set_default_engine(&engine.id)?;
133 tracing::info!("Selected engine: {} for hardware: {:?}",
134 engine.name, self.hardware_capabilities);
135 }
136
137 Ok(best_engine)
138 }
139
140 pub async fn download_suitable_engine(&self) -> Result<EngineInfo> {
142 let can_start = {
144 let registry = self.registry.read().await;
145 registry.mark_download_started()
146 };
147
148 if !can_start {
149 return Err(anyhow::anyhow!("Another engine download is already in progress"));
150 }
151
152 let recommended_engine = {
153 let registry = self.registry.read().await;
154 registry.get_recommended_engine(&self.hardware_capabilities)
155 .ok_or_else(|| anyhow::anyhow!("No recommended engine found for current hardware"))?
156 };
157
158 tracing::info!("Downloading recommended engine: {}", recommended_engine.name);
159
160 {
162 let mut registry = self.registry.write().await;
163 let mut engine_to_download = recommended_engine.clone();
164 engine_to_download.status = EngineStatus::Downloading;
165 registry.add_installed_engine(engine_to_download).await?;
166 }
167
168 let download_result = self.downloader.download_engine(&recommended_engine).await;
170
171 {
173 let registry = self.registry.read().await;
174 registry.mark_download_finished();
175 }
176
177 let engine = download_result?;
179 self.registry.write().await.add_installed_engine(engine.clone()).await?;
180
181 Ok(engine)
182 }
183
184 pub async fn ensure_engine_available(&self) -> Result<bool> {
188 let registry = self.registry.read().await;
189
190 if registry.has_installed_engine() {
192 return Ok(true);
193 }
194
195 drop(registry); tracing::info!("No engine available, downloading suitable engine...");
199 match self.download_suitable_engine().await {
200 Ok(_) => {
201 tracing::info!("Engine downloaded successfully");
202 Ok(true)
203 }
204 Err(e) => {
205 tracing::error!("Failed to download engine: {}", e);
206 Ok(false)
207 }
208 }
209 }
210
211 pub fn get_hardware_info(&self) -> &HardwareCapabilities {
213 &self.hardware_capabilities
214 }
215
216 pub async fn refresh_available_engines(&self) -> Result<()> {
218 let mut registry = self.registry.write().await;
219 registry.refresh_available_engines(&self.hardware_capabilities).await?;
220 Ok(())
221 }
222
223 pub async fn get_status_info(&self) -> String {
225 let registry = self.registry.read().await;
226 let installed_count = registry.installed_engines.len();
227 let available_count = registry.available_engines.len();
228 let default_engine = registry.default_engine.as_deref().unwrap_or("None");
229
230 format!(
231 "Engine Manager Status:\n Installed Engines: {}\n Available Engines: {}\n Default Engine: {}\n Hardware: {:?} {:?} (CUDA: {})\n Recommended Engine: {:?}",
232 installed_count,
233 available_count,
234 default_engine,
235 self.hardware_capabilities.platform,
236 self.hardware_capabilities.architecture,
237 self.hardware_capabilities.has_cuda,
238 registry.get_recommended_engine(&self.hardware_capabilities).map(|e| e.name)
239 )
240 }
241
242 pub async fn install_engine_by_id(&self, engine_id: &str) -> Result<EngineInfo> {
244 let registry = self.registry.read().await;
245 let engine_to_install = registry.available_engines.iter()
246 .find(|engine| engine.id == engine_id)
247 .cloned();
248
249 match engine_to_install {
250 Some(engine_info) => {
251 drop(registry); let installed_engine = self.downloader.download_engine(&engine_info).await?;
253 self.registry.write().await.add_installed_engine(installed_engine.clone()).await?;
254 Ok(installed_engine)
255 }
256 None => {
257 Err(anyhow::anyhow!("Engine not found: {}", engine_id))
258 }
259 }
260 }
261}