use crate::error::AppError; use anyhow::Result; use std::path::Path; use std::sync::Arc; use std::time::Duration; use tokio::process::Command; use tokio::sync::{Mutex, RwLock, mpsc}; use tokio::time::{sleep, timeout}; use tracing::{debug, error, info, instrument, warn}; /// 环境管理器 #[derive(Debug, Clone)] pub struct EnvironmentManager { python_path: String, base_dir: String, progress_sender: Option>>>, timeout_duration: Duration, retry_config: RetryConfig, environment_cache: Arc>>, cache_ttl: Duration, } /// 重试配置 #[derive(Debug, Clone)] pub struct RetryConfig { pub max_attempts: u32, pub base_delay: Duration, pub max_delay: Duration, pub backoff_multiplier: f64, } /// 环境检查结果 #[derive(Debug, Clone)] pub struct EnvironmentStatus { pub python_available: bool, pub python_version: Option, pub python_path: Option, pub uv_available: bool, pub uv_version: Option, pub cuda_available: bool, pub cuda_version: Option, pub cuda_devices: Vec, pub mineru_available: bool, pub mineru_version: Option, pub markitdown_available: bool, pub markitdown_version: Option, pub virtual_env_active: bool, pub virtual_env_path: Option, pub issues: Vec, pub warnings: Vec, pub last_checked: std::time::SystemTime, pub check_duration: Duration, } /// 环境问题详情 #[derive(Debug, Clone)] pub struct EnvironmentIssue { pub component: String, pub severity: IssueSeverity, pub message: String, pub suggestion: String, pub auto_fixable: bool, } /// 环境警告详情 #[derive(Debug, Clone)] pub struct EnvironmentWarning { pub component: String, pub message: String, pub impact: String, } /// 问题严重程度 #[derive(Debug, Clone, PartialEq)] pub enum IssueSeverity { Critical, High, Medium, Low, } /// CUDA设备信息 #[derive(Debug, Clone)] pub struct CudaDevice { pub id: u32, pub name: String, pub memory_total: u64, pub memory_free: u64, pub compute_capability: String, } /// 依赖安装进度 #[derive(Debug, Clone)] pub struct InstallProgress { pub package: String, pub stage: InstallStage, pub progress: f32, pub message: String, pub estimated_time_remaining: Option, pub bytes_downloaded: Option, pub total_bytes: Option, } /// 安装阶段 #[derive(Debug, Clone)] pub enum InstallStage { Preparing, Downloading, Installing, Configuring, Verifying, Completed, Failed(String), Retrying { attempt: u32, max_attempts: u32 }, } /// Python环境信息 #[derive(Debug)] struct PythonInfo { version: Option, path: String, virtual_env_active: bool, virtual_env_path: Option, } /// uv工具信息 #[derive(Debug)] struct UvInfo { version: String, } /// CUDA环境信息 #[derive(Debug)] pub struct CudaInfo { pub available: bool, pub version: Option, pub devices: Vec, } /// Python包信息 #[derive(Debug, Clone)] pub struct PackageInfo { pub version: String, } /// 包版本兼容性信息 #[derive(Debug, Clone)] pub struct PackageCompatibility { pub package_name: String, pub current_version: String, pub minimum_version: String, pub recommended_version: Option, pub is_compatible: bool, pub compatibility_issues: Vec, pub upgrade_available: bool, pub upgrade_recommendation: Option, } /// 依赖验证结果 #[derive(Debug, Clone)] pub struct DependencyVerificationResult { pub mineru_status: DependencyStatus, pub markitdown_status: DependencyStatus, pub overall_compatible: bool, pub recommendations: Vec, pub critical_issues: Vec, } /// 依赖状态 #[derive(Debug, Clone)] pub struct DependencyStatus { pub package_name: String, pub is_available: bool, pub is_functional: bool, pub version_info: Option, pub compatibility: Option, pub issues: Vec, pub path: Option, } /// 虚拟环境状态详细信息 #[derive(Debug, Clone)] pub struct VirtualEnvStatus { pub is_active: bool, pub path: Option, pub expected_path: Option, pub python_executable: Option, pub is_properly_configured: bool, pub activation_command: String, } /// 虚拟环境详细信息(跨平台) #[derive(Debug, Clone)] pub struct VirtualEnvInfo { pub path: std::path::PathBuf, pub python_executable: std::path::PathBuf, pub pip_executable: std::path::PathBuf, pub activation_script: std::path::PathBuf, pub is_valid: bool, pub platform: String, } /// 诊断报告 #[derive(Debug, Clone)] pub struct DiagnosticReport { pub overall_status: String, pub health_score: u8, pub components: Vec, pub recommendations: Vec, pub next_steps: Vec, } /// 组件诊断信息 #[derive(Debug, Clone)] pub struct ComponentDiagnostic { pub name: String, pub status: String, pub version: Option, pub path: Option, pub issues: Vec, pub details: String, } /// UV工具可用性状态 #[derive(Debug, Clone)] pub enum UvAvailabilityStatus { /// UV可用且版本兼容 Available { version: String, compatibility: UvVersionCompatibility, }, /// UV已安装但版本不兼容 IncompatibleVersion { version: String, issue: String }, /// UV命令执行失败 ExecutionFailed { error: String }, /// UV未安装 NotInstalled { error: String }, } /// UV版本兼容性信息 #[derive(Debug, Clone)] pub struct UvVersionCompatibility { pub is_compatible: bool, pub minimum_version: String, pub current_version: String, pub recommendation: Option, } /// UV安装方法 #[derive(Debug, Clone)] pub enum UvInstallationMethod { /// 使用curl脚本安装(推荐) CurlScript, /// 使用PowerShell脚本安装(Windows) PowerShellScript, /// 使用pip安装 PipInstall, /// 使用系统包管理器 SystemPackageManager, } /// 目录验证结果 #[derive(Debug, Clone)] pub struct DirectoryValidationResult { pub is_valid: bool, pub current_directory: std::path::PathBuf, pub venv_path: std::path::PathBuf, pub issues: Vec, pub warnings: Vec, pub cleanup_options: Vec, pub recommendations: Vec, } /// 目录验证问题 #[derive(Debug, Clone)] pub struct DirectoryValidationIssue { pub issue_type: DirectoryIssueType, pub message: String, pub severity: ValidationSeverity, pub auto_fixable: bool, pub fix_suggestion: String, } /// 目录验证警告 #[derive(Debug, Clone)] pub struct DirectoryValidationWarning { pub warning_type: DirectoryWarningType, pub message: String, pub impact: String, } /// 清理选项 #[derive(Debug, Clone)] pub struct CleanupOption { pub option_type: CleanupType, pub description: String, pub risk_level: CleanupRisk, pub command: String, } /// 目录问题类型 #[derive(Debug, Clone, PartialEq)] pub enum DirectoryIssueType { PermissionDenied, InsufficientSpace, PathConflict, PathTooLong, } /// 目录警告类型 #[derive(Debug, Clone, PartialEq)] pub enum DirectoryWarningType { ExistingVenv, CorruptedVenv, PathWithSpaces, } /// 验证严重程度 #[derive(Debug, Clone, PartialEq)] pub enum ValidationSeverity { Critical, High, Medium, Low, } /// 清理类型 #[derive(Debug, Clone, PartialEq)] pub enum CleanupType { RemoveConflictingFile, RemoveCorruptedVenv, CreateBackup, } /// 清理风险级别 #[derive(Debug, Clone, PartialEq)] pub enum CleanupRisk { Low, Medium, High, } impl Default for EnvironmentStatus { fn default() -> Self { Self { python_available: false, python_version: None, python_path: None, uv_available: false, uv_version: None, cuda_available: false, cuda_version: None, cuda_devices: Vec::new(), mineru_available: false, mineru_version: None, markitdown_available: false, markitdown_version: None, virtual_env_active: false, virtual_env_path: None, issues: Vec::new(), warnings: Vec::new(), last_checked: std::time::SystemTime::now(), check_duration: Duration::from_secs(0), } } } impl Default for RetryConfig { fn default() -> Self { Self { max_attempts: 3, base_delay: Duration::from_millis(500), max_delay: Duration::from_secs(30), backoff_multiplier: 2.0, } } } impl EnvironmentStatus { /// 检查环境是否就绪 pub fn is_ready(&self) -> bool { self.python_available && self.mineru_available && self.markitdown_available } /// 获取问题列表 pub fn get_issues(&self) -> &Vec { &self.issues } /// 获取警告列表 pub fn get_warnings(&self) -> &Vec { &self.warnings } /// 获取关键问题(阻止系统运行的问题) pub fn get_critical_issues(&self) -> Vec<&EnvironmentIssue> { self.issues .iter() .filter(|issue| issue.severity == IssueSeverity::Critical) .collect() } /// 获取可自动修复的问题 pub fn get_auto_fixable_issues(&self) -> Vec<&EnvironmentIssue> { self.issues .iter() .filter(|issue| issue.auto_fixable) .collect() } /// 检查是否有CUDA支持 pub fn has_cuda_support(&self) -> bool { self.cuda_available && !self.cuda_devices.is_empty() } /// 获取推荐的CUDA设备 pub fn get_recommended_cuda_device(&self) -> Option<&CudaDevice> { self.cuda_devices .iter() .max_by_key(|device| device.memory_free) } /// 检查缓存是否过期 pub fn is_cache_expired(&self, ttl: Duration) -> bool { self.last_checked.elapsed().unwrap_or(Duration::MAX) > ttl } /// 获取虚拟环境状态详细信息 pub fn get_virtual_env_status(&self) -> VirtualEnvStatus { VirtualEnvStatus { is_active: self.virtual_env_active, path: self.virtual_env_path.clone(), expected_path: Some("./venv".to_string()), python_executable: self.python_path.clone(), is_properly_configured: self.is_virtual_env_properly_configured(), activation_command: self.get_activation_command(), } } /// 检查虚拟环境是否正确配置 pub fn is_virtual_env_properly_configured(&self) -> bool { if !self.virtual_env_active { return false; } // 检查虚拟环境路径是否符合预期(当前目录下的venv) if let Some(ref venv_path) = self.virtual_env_path { // 检查路径是否以当前目录的venv结尾 venv_path.ends_with("venv") || venv_path.contains("/venv") || venv_path.contains("\\venv") } else { false } } /// 获取虚拟环境激活命令 pub fn get_activation_command(&self) -> String { if cfg!(windows) { // Windows supports both batch and PowerShell activation ".\\venv\\Scripts\\activate.bat".to_string() } else { "source ./venv/bin/activate".to_string() } } /// 获取虚拟环境激活命令(PowerShell版本,仅Windows) pub fn get_powershell_activation_command(&self) -> Option { if cfg!(windows) { Some(".\\venv\\Scripts\\Activate.ps1".to_string()) } else { None } } /// 生成详细的诊断报告 pub fn generate_diagnostic_report(&self) -> DiagnosticReport { let mut report = DiagnosticReport { overall_status: if self.is_ready() { "Ready" } else { "Not Ready" } .to_string(), health_score: self.health_score(), components: Vec::new(), recommendations: Vec::new(), next_steps: Vec::new(), }; // Python组件诊断 let python_component = ComponentDiagnostic { name: "Python".to_string(), status: if self.python_available { "Available" } else { "Missing" } .to_string(), version: self.python_version.clone(), path: self.python_path.clone(), issues: self.get_component_issues("Python"), details: if self.python_available { format!( "Python {} is available at {:?}", self.python_version.as_deref().unwrap_or("unknown"), self.python_path.as_deref().unwrap_or("unknown") ) } else { "Python is not available or not properly configured".to_string() }, }; report.components.push(python_component); // 虚拟环境组件诊断 let venv_status = self.get_virtual_env_status(); let venv_component = ComponentDiagnostic { name: "Virtual Environment".to_string(), status: if venv_status.is_active { "Active" } else { "Inactive" } .to_string(), version: None, path: venv_status.path.clone(), issues: self.get_component_issues("Virtual Environment"), details: if venv_status.is_active { format!( "Virtual environment is active at {:?}", venv_status.path.as_deref().unwrap_or("unknown") ) } else { format!( "Virtual environment is not active. Expected at ./venv. Use: {}", venv_status.activation_command ) }, }; report.components.push(venv_component); // UV工具诊断 let uv_component = ComponentDiagnostic { name: "UV Tool".to_string(), status: if self.uv_available { "Available" } else { "Missing" } .to_string(), version: self.uv_version.clone(), path: None, issues: self.get_component_issues("UV"), details: if self.uv_available { format!( "UV {} is available", self.uv_version.as_deref().unwrap_or("unknown") ) } else { "UV tool is not installed. Install with: curl -LsSf https://astral.sh/uv/install.sh | sh".to_string() }, }; report.components.push(uv_component); // MinerU组件诊断 let mineru_component = ComponentDiagnostic { name: "MinerU".to_string(), status: if self.mineru_available { "Available" } else { "Missing" } .to_string(), version: self.mineru_version.clone(), path: None, issues: self.get_component_issues("MinerU"), details: if self.mineru_available { format!( "MinerU {} is available", self.mineru_version.as_deref().unwrap_or("unknown") ) } else { "MinerU is not installed. Install with: uv pip install magic-pdf[full]".to_string() }, }; report.components.push(mineru_component); // MarkItDown组件诊断 let markitdown_component = ComponentDiagnostic { name: "MarkItDown".to_string(), status: if self.markitdown_available { "Available" } else { "Missing" } .to_string(), version: self.markitdown_version.clone(), path: None, issues: self.get_component_issues("MarkItDown"), details: if self.markitdown_available { format!( "MarkItDown {} is available", self.markitdown_version.as_deref().unwrap_or("unknown") ) } else { "MarkItDown is not installed. Install with: uv pip install markitdown".to_string() }, }; report.components.push(markitdown_component); // CUDA组件诊断(可选) let cuda_component = ComponentDiagnostic { name: "CUDA".to_string(), status: if self.cuda_available { "Available" } else { "Not Available" } .to_string(), version: self.cuda_version.clone(), path: None, issues: self.get_component_issues("CUDA"), details: if self.cuda_available { format!( "CUDA {} is available with {} device(s)", self.cuda_version.as_deref().unwrap_or("unknown"), self.cuda_devices.len() ) } else { "CUDA is not available. GPU acceleration will not be used.".to_string() }, }; report.components.push(cuda_component); // 生成推荐和下一步操作 self.generate_recommendations(&mut report); report } /// 获取特定组件的问题 fn get_component_issues(&self, component_name: &str) -> Vec { self.issues .iter() .filter(|issue| issue.component == component_name) .map(|issue| format!("{}: {}", issue.message, issue.suggestion)) .collect() } /// 生成推荐和下一步操作 fn generate_recommendations(&self, report: &mut DiagnosticReport) { // 基于当前状态生成推荐 if !self.python_available { report .recommendations .push("Install Python 3.8+ to enable document parsing functionality".to_string()); report .next_steps .push("1. Install Python 3.8 or higher".to_string()); } if !self.virtual_env_active { report.recommendations.push( "Create and activate a virtual environment for isolated dependency management" .to_string(), ); report .next_steps .push("2. Run 'document-parser uv-init' to set up the environment".to_string()); } if !self.uv_available { report .recommendations .push("Install UV tool for fast Python package management".to_string()); if !report .next_steps .iter() .any(|step| step.contains("uv-init")) { report.next_steps.push( "2. Run 'document-parser uv-init' to install UV and set up dependencies" .to_string(), ); } } if !self.mineru_available { report .recommendations .push("Install MinerU for PDF document parsing capabilities".to_string()); if !report .next_steps .iter() .any(|step| step.contains("uv-init")) { report .next_steps .push("3. Install MinerU with: uv pip install magic-pdf[full]".to_string()); } } if !self.markitdown_available { report .recommendations .push("Install MarkItDown for multi-format document parsing".to_string()); if !report .next_steps .iter() .any(|step| step.contains("uv-init")) { report .next_steps .push("4. Install MarkItDown with: uv pip install markitdown".to_string()); } } if self.is_ready() { report.recommendations.push( "Environment is ready! You can start the document parsing server".to_string(), ); report .next_steps .push("Run 'document-parser server' to start the service".to_string()); } // 添加CUDA相关推荐 if !self.cuda_available && self.python_available { report.recommendations.push( "Consider installing CUDA for improved PDF processing performance".to_string(), ); } // 添加虚拟环境配置推荐 if self.virtual_env_active && !self.is_virtual_env_properly_configured() { report.recommendations.push( "Virtual environment detected but may not be in the expected location (./venv)" .to_string(), ); } } /// 格式化诊断报告为可读字符串 pub fn format_diagnostic_report(&self) -> String { let report = self.generate_diagnostic_report(); let mut output = String::new(); output.push_str("=== Environment Diagnostic Report ===\n"); output.push_str(&format!("Overall Status: {}\n", report.overall_status)); output.push_str(&format!("Health Score: {}/100\n\n", report.health_score)); output.push_str("=== Components ===\n"); for component in &report.components { output.push_str(&format!("• {}: {} ", component.name, component.status)); if let Some(ref version) = component.version { output.push_str(&format!("({version})")); } output.push('\n'); if let Some(ref path) = component.path { output.push_str(&format!(" Path: {path}\n")); } output.push_str(&format!(" Details: {}\n", component.details)); if !component.issues.is_empty() { output.push_str(" Issues:\n"); for issue in &component.issues { output.push_str(&format!(" - {issue}\n")); } } output.push('\n'); } if !report.recommendations.is_empty() { output.push_str("=== Recommendations ===\n"); for (i, recommendation) in report.recommendations.iter().enumerate() { output.push_str(&format!("{}. {}\n", i + 1, recommendation)); } output.push('\n'); } if !report.next_steps.is_empty() { output.push_str("=== Next Steps ===\n"); for step in &report.next_steps { output.push_str(&format!("{step}\n")); } } output } /// 生成环境健康评分 (0-100) pub fn health_score(&self) -> u8 { let mut score = 0u8; // 基础组件检查 (60分) if self.python_available { score += 20; } if self.mineru_available { score += 20; } if self.markitdown_available { score += 20; } // 工具支持 (20分) if self.uv_available { score += 10; } if self.virtual_env_active { score += 10; } // CUDA支持 (10分) if self.has_cuda_support() { score += 10; } // 扣除问题分数 (最多扣30分) let issue_penalty = self .issues .iter() .map(|issue| match issue.severity { IssueSeverity::Critical => 10, IssueSeverity::High => 5, IssueSeverity::Medium => 2, IssueSeverity::Low => 1, }) .sum::() .min(30); score.saturating_sub(issue_penalty) } } impl EnvironmentManager { /// 创建新的环境管理器 pub fn new(python_path: String, base_dir: String) -> Self { Self { python_path, base_dir, progress_sender: None, timeout_duration: Duration::from_secs(300), // 5分钟默认超时 retry_config: RetryConfig::default(), environment_cache: Arc::new(RwLock::new(None)), cache_ttl: Duration::from_secs(300), // 5分钟缓存 } } /// 为当前目录创建环境管理器(推荐使用) pub fn for_current_directory() -> Result { let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let python_path = Self::get_venv_python_path(¤t_dir.join("venv")); Ok(Self { python_path: python_path.to_string_lossy().to_string(), base_dir: current_dir.to_string_lossy().to_string(), progress_sender: None, timeout_duration: Duration::from_secs(300), // 5分钟默认超时 retry_config: RetryConfig::default(), environment_cache: Arc::new(RwLock::new(None)), cache_ttl: Duration::from_secs(300), // 5分钟缓存 }) } /// 获取虚拟环境中的Python可执行文件路径(跨平台) pub fn get_venv_python_path(venv_path: &Path) -> std::path::PathBuf { if cfg!(windows) { // Windows: Scripts/python.exe venv_path.join("Scripts").join("python.exe") } else { // Unix-like: bin/python venv_path.join("bin").join("python") } } /// 获取虚拟环境中的可执行文件路径(跨平台) pub fn get_venv_executable_path(venv_path: &Path, executable_name: &str) -> std::path::PathBuf { if cfg!(windows) { // Windows: Scripts/{executable}.exe let exe_name = if executable_name.ends_with(".exe") { executable_name.to_string() } else { format!("{executable_name}.exe") }; venv_path.join("Scripts").join(exe_name) } else { // Unix-like: bin/{executable} venv_path.join("bin").join(executable_name) } } /// 获取虚拟环境激活脚本路径(跨平台) pub fn get_venv_activation_script(venv_path: &Path) -> std::path::PathBuf { if cfg!(windows) { // Windows: Scripts/activate.bat or Scripts/Activate.ps1 venv_path.join("Scripts").join("activate.bat") } else { // Unix-like: bin/activate venv_path.join("bin").join("activate") } } /// 获取系统Python可执行文件名(跨平台) pub fn get_system_python_executable() -> Vec { if cfg!(windows) { // Windows: python.exe, python3.exe, py.exe vec![ "python.exe".to_string(), "python3.exe".to_string(), "py.exe".to_string(), ] } else { // Unix-like: python3, python vec!["python3".to_string(), "python".to_string()] } } /// 检查可执行文件是否存在于PATH中(跨平台) pub async fn is_executable_in_path(executable: &str) -> bool { let which_cmd = if cfg!(windows) { "where" } else { "which" }; match Command::new(which_cmd).arg(executable).output().await { Ok(output) => output.status.success(), Err(_) => false, } } /// 查找系统中可用的Python可执行文件(跨平台) async fn find_system_python(&self) -> Option { let python_candidates = Self::get_system_python_executable(); for candidate in python_candidates { if Self::is_executable_in_path(&candidate).await { debug!("Found system Python: {}", candidate); return Some(candidate); } } debug!("System Python executable not found"); None } /// 测试虚拟环境激活(跨平台) pub async fn test_virtual_environment_activation( &self, venv_path: &Path, ) -> Result { let python_exe = Self::get_venv_python_path(venv_path); if !python_exe.exists() { return Ok(false); } // 测试Python可执行文件是否工作 let test_cmd = Command::new(&python_exe) .arg("-c") .arg("import sys; print('VENV_TEST_SUCCESS'); print(sys.prefix)") .output(); match timeout(Duration::from_secs(10), test_cmd).await { Ok(Ok(output)) if output.status.success() => { let stdout = String::from_utf8_lossy(&output.stdout); if stdout.contains("VENV_TEST_SUCCESS") { debug!( "Virtual environment activation test successful: {}", python_exe.display() ); Ok(true) } else { debug!("Virtual environment activation test failed: Incorrect output"); Ok(false) } } Ok(Ok(output)) => { let stderr = String::from_utf8_lossy(&output.stderr); debug!("Virtual environment activation test failed: {}", stderr); Ok(false) } Ok(Err(e)) => { debug!( "Virtual environment activation test execution failed: {}", e ); Ok(false) } Err(_) => { debug!("Virtual environment activation test timed out"); Ok(false) } } } /// 获取虚拟环境信息(跨平台) pub async fn get_virtual_environment_info( &self, venv_path: &Path, ) -> Result { let python_exe = Self::get_venv_python_path(venv_path); let activation_script = Self::get_venv_activation_script(venv_path); let pip_exe = Self::get_venv_executable_path(venv_path, "pip"); let is_valid = self.test_virtual_environment_activation(venv_path).await?; Ok(VirtualEnvInfo { path: venv_path.to_path_buf(), python_executable: python_exe, pip_executable: pip_exe, activation_script, is_valid, platform: if cfg!(windows) { "windows".to_string() } else { "unix".to_string() }, }) } /// 获取跨平台环境变量设置 pub fn get_cross_platform_env_vars( &self, venv_path: &Path, ) -> std::collections::HashMap { let mut env_vars = std::collections::HashMap::new(); if cfg!(windows) { // Windows环境变量 env_vars.insert( "VIRTUAL_ENV".to_string(), venv_path.to_string_lossy().to_string(), ); env_vars.insert( "PATH".to_string(), format!( "{};{}", venv_path.join("Scripts").to_string_lossy(), std::env::var("PATH").unwrap_or_default() ), ); } else { // Unix-like环境变量 env_vars.insert( "VIRTUAL_ENV".to_string(), venv_path.to_string_lossy().to_string(), ); env_vars.insert( "PATH".to_string(), format!( "{}:{}", venv_path.join("bin").to_string_lossy(), std::env::var("PATH").unwrap_or_default() ), ); } env_vars } /// 创建带进度跟踪的环境管理器 pub fn with_progress_tracking( python_path: String, base_dir: String, progress_sender: mpsc::UnboundedSender, ) -> Self { Self { python_path, base_dir, progress_sender: Some(Arc::new(Mutex::new(progress_sender))), timeout_duration: Duration::from_secs(300), retry_config: RetryConfig::default(), environment_cache: Arc::new(RwLock::new(None)), cache_ttl: Duration::from_secs(300), } } /// 为当前目录创建带进度跟踪的环境管理器 pub fn for_current_directory_with_progress( progress_sender: mpsc::UnboundedSender, ) -> Result { let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let python_path = Self::get_venv_python_path(¤t_dir.join("venv")); Ok(Self { python_path: python_path.to_string_lossy().to_string(), base_dir: current_dir.to_string_lossy().to_string(), progress_sender: Some(Arc::new(Mutex::new(progress_sender))), timeout_duration: Duration::from_secs(300), retry_config: RetryConfig::default(), environment_cache: Arc::new(RwLock::new(None)), cache_ttl: Duration::from_secs(300), }) } /// 添加进度发送器到现有环境管理器 pub fn with_progress_sender( mut self, progress_sender: mpsc::UnboundedSender, ) -> Self { self.progress_sender = Some(Arc::new(Mutex::new(progress_sender))); self } /// 设置操作超时时间 pub fn with_timeout(mut self, timeout: Duration) -> Self { self.timeout_duration = timeout; self } /// 设置重试配置 pub fn with_retry_config(mut self, retry_config: RetryConfig) -> Self { self.retry_config = retry_config; self } /// 设置缓存TTL pub fn with_cache_ttl(mut self, ttl: Duration) -> Self { self.cache_ttl = ttl; self } /// 检查完整环境状态(带缓存支持) #[instrument(skip(self))] pub async fn check_environment(&self) -> Result { // 检查缓存 if let Some(cached_status) = self.get_cached_status().await { if !cached_status.is_cache_expired(self.cache_ttl) { debug!("Using cached environment state"); return Ok(cached_status); } } let start_time = std::time::SystemTime::now(); let mut status = EnvironmentStatus::default(); info!("Start environment check"); // 并行检查各个环境组件 let (python_result, uv_result, cuda_result) = tokio::join!( self.check_python_environment_with_retry(), self.check_uv_environment_with_retry(), self.check_cuda_environment_with_retry() ); // 处理Python环境检查结果 match python_result { Ok(python_info) => { status.python_available = true; status.python_version = python_info.version.clone(); status.python_path = Some(python_info.path.clone()); status.virtual_env_active = python_info.virtual_env_active; status.virtual_env_path = python_info.virtual_env_path.clone(); info!( "Python environment check passed: {:?}", status.python_version ); // 增强虚拟环境状态验证 self.validate_virtual_environment_status(&mut status, &python_info); } Err(e) => { let issue = EnvironmentIssue { component: "Python".to_string(), severity: IssueSeverity::Critical, message: format!("Python环境检查失败: {e}"), suggestion: self.get_python_installation_suggestion(&e.to_string()), auto_fixable: false, }; status.issues.push(issue); error!("Python environment check failed: {}", e); } } // 处理uv工具检查结果 match uv_result { Ok(uv_info) => { status.uv_available = true; status.uv_version = Some(uv_info.version); info!("UV tool inspection passed: {:?}", status.uv_version); } Err(e) => { let issue = EnvironmentIssue { component: "UV".to_string(), severity: IssueSeverity::High, message: format!("uv工具检查失败: {e}"), suggestion: self.get_uv_installation_suggestion(&e.to_string()), auto_fixable: true, }; status.issues.push(issue); warn!("uv tool check failed: {}", e); } } // 处理CUDA环境检查结果 match cuda_result { Ok(cuda_info) => { status.cuda_available = cuda_info.available; status.cuda_version = cuda_info.version; status.cuda_devices = cuda_info.devices; if status.cuda_available { info!("CUDA environment check passed: {:?}", status.cuda_version); } else { let warning = EnvironmentWarning { component: "CUDA".to_string(), message: "CUDA环境不可用".to_string(), impact: "PDF处理性能可能较慢".to_string(), }; status.warnings.push(warning); info!("CUDA environment is not available"); } } Err(e) => { let warning = EnvironmentWarning { component: "CUDA".to_string(), message: format!("CUDA环境检查失败: {e}"), impact: "将使用CPU进行PDF处理".to_string(), }; status.warnings.push(warning); warn!("CUDA environment check failed: {}", e); } } // 如果Python可用,检查Python包 if status.python_available { let (mineru_result, markitdown_result) = tokio::join!( self.check_mineru_environment_with_retry(), self.check_markitdown_environment_with_retry() ); match mineru_result { Ok(mineru_info) => { status.mineru_available = true; status.mineru_version = Some(mineru_info.version); info!( "MinerU environment check passed: {:?}", status.mineru_version ); } Err(e) => { let issue = EnvironmentIssue { component: "MinerU".to_string(), severity: IssueSeverity::Critical, message: format!("MinerU环境检查失败: {e}"), suggestion: self.get_mineru_installation_suggestion(&e.to_string()), auto_fixable: true, }; status.issues.push(issue); warn!("MinerU environment check failed: {}", e); } } match markitdown_result { Ok(markitdown_info) => { status.markitdown_available = true; status.markitdown_version = Some(markitdown_info.version); info!( "MarkItDown environment check passed: {:?}", status.markitdown_version ); } Err(e) => { let issue = EnvironmentIssue { component: "MarkItDown".to_string(), severity: IssueSeverity::Critical, message: format!("MarkItDown环境检查失败: {e}"), suggestion: self.get_markitdown_installation_suggestion(&e.to_string()), auto_fixable: true, }; status.issues.push(issue); warn!("MarkItDown environment check failed: {}", e); } } } else { // Python不可用时,添加相关问题 let mineru_issue = EnvironmentIssue { component: "MinerU".to_string(), severity: IssueSeverity::Critical, message: "无法检查MinerU:Python环境不可用".to_string(), suggestion: "首先修复Python环境问题".to_string(), auto_fixable: false, }; let markitdown_issue = EnvironmentIssue { component: "MarkItDown".to_string(), severity: IssueSeverity::Critical, message: "无法检查MarkItDown:Python环境不可用".to_string(), suggestion: "首先修复Python环境问题".to_string(), auto_fixable: false, }; status.issues.push(mineru_issue); status.issues.push(markitdown_issue); } // 设置检查时间和持续时间 status.last_checked = start_time; status.check_duration = start_time.elapsed().unwrap_or(Duration::from_secs(0)); // 更新缓存 self.update_cache(status.clone()).await; info!( "Environmental check completed, status: ready={}, health score: {}/100, time taken: {:?}", status.is_ready(), status.health_score(), status.check_duration ); Ok(status) } /// 获取详细的环境状态报告 pub async fn get_detailed_status_report(&self) -> Result { let status = self.check_environment().await?; Ok(status.format_diagnostic_report()) } /// 获取增强的依赖验证报告 pub async fn get_enhanced_dependency_report(&self) -> Result { let verification_result = self.verify_dependency_compatibility().await?; let mut report = String::new(); report.push_str("=== 增强依赖验证报告 ===\n\n"); // 总体状态 report.push_str(&format!( "总体兼容性: {}\n", if verification_result.overall_compatible { "✓ 兼容" } else { "✗ 不兼容" } )); report.push('\n'); // MinerU状态 report.push_str("=== MinerU 状态 ===\n"); let mineru = &verification_result.mineru_status; report.push_str(&format!( "可用性: {}\n", if mineru.is_available { "✓ 可用" } else { "✗ 不可用" } )); report.push_str(&format!( "功能性: {}\n", if mineru.is_functional { "✓ 正常" } else { "✗ 异常" } )); if let Some(ref version_info) = mineru.version_info { report.push_str(&format!("版本: {}\n", version_info.version)); } if let Some(ref path) = mineru.path { report.push_str(&format!("路径: {path}\n")); } if let Some(ref compat) = mineru.compatibility { report.push_str(&format!( "版本兼容性: {}\n", if compat.is_compatible { "✓ 兼容" } else { "✗ 不兼容" } )); report.push_str(&format!("当前版本: {}\n", compat.current_version)); report.push_str(&format!("最低要求: {}\n", compat.minimum_version)); if !compat.compatibility_issues.is_empty() { report.push_str("兼容性问题:\n"); for issue in &compat.compatibility_issues { report.push_str(&format!(" - {issue}\n")); } } if compat.upgrade_available { if let Some(ref rec) = compat.upgrade_recommendation { report.push_str(&format!("升级建议: {rec}\n")); } } } if !mineru.issues.is_empty() { report.push_str("问题:\n"); for issue in &mineru.issues { report.push_str(&format!(" - {issue}\n")); } } report.push('\n'); // MarkItDown状态 report.push_str("=== MarkItDown 状态 ===\n"); let markitdown = &verification_result.markitdown_status; report.push_str(&format!( "可用性: {}\n", if markitdown.is_available { "✓ 可用" } else { "✗ 不可用" } )); report.push_str(&format!( "功能性: {}\n", if markitdown.is_functional { "✓ 正常" } else { "✗ 异常" } )); if let Some(ref version_info) = markitdown.version_info { report.push_str(&format!("版本: {}\n", version_info.version)); } if let Some(ref path) = markitdown.path { report.push_str(&format!("路径: {path}\n")); } if let Some(ref compat) = markitdown.compatibility { report.push_str(&format!( "版本兼容性: {}\n", if compat.is_compatible { "✓ 兼容" } else { "✗ 不兼容" } )); report.push_str(&format!("当前版本: {}\n", compat.current_version)); report.push_str(&format!("最低要求: {}\n", compat.minimum_version)); if !compat.compatibility_issues.is_empty() { report.push_str("兼容性问题:\n"); for issue in &compat.compatibility_issues { report.push_str(&format!(" - {issue}\n")); } } if compat.upgrade_available { if let Some(ref rec) = compat.upgrade_recommendation { report.push_str(&format!("升级建议: {rec}\n")); } } } if !markitdown.issues.is_empty() { report.push_str("问题:\n"); for issue in &markitdown.issues { report.push_str(&format!(" - {issue}\n")); } } report.push('\n'); // 关键问题 if !verification_result.critical_issues.is_empty() { report.push_str("=== 关键问题 ===\n"); for issue in &verification_result.critical_issues { report.push_str(&format!("⚠️ {issue}\n")); } report.push('\n'); } // 推荐操作 if !verification_result.recommendations.is_empty() { report.push_str("=== 推荐操作 ===\n"); for (i, rec) in verification_result.recommendations.iter().enumerate() { report.push_str(&format!("{}. {}\n", i + 1, rec)); } } Ok(report) } /// 检查并报告虚拟环境状态 pub async fn check_virtual_environment_status(&self) -> Result { let status = self.check_environment().await?; Ok(status.get_virtual_env_status()) } /// 诊断虚拟环境路径问题 pub async fn diagnose_venv_path_issues(&self) -> Vec { let mut issues = Vec::new(); let venv_path = Path::new(&self.base_dir).join("venv"); let base_dir = Path::new(&self.base_dir); // 检查基础目录 if !base_dir.exists() { issues.push(format!("基础目录不存在: {}", base_dir.display())); } else if !base_dir.is_dir() { issues.push(format!("基础路径不是目录: {}", base_dir.display())); } else { // 检查写入权限 if let Err(e) = self.check_directory_writable(base_dir).await { issues.push(format!( "基础目录无写入权限: {} ({})", base_dir.display(), e )); } } // 检查虚拟环境路径 if venv_path.exists() { if !venv_path.is_dir() { issues.push(format!( "虚拟环境路径存在但不是目录: {}", venv_path.display() )); } else { // 检查虚拟环境完整性 let python_exe = Self::get_venv_python_path(&venv_path); if !python_exe.exists() { issues.push(format!( "虚拟环境不完整,缺少Python可执行文件: {}", python_exe.display() )); } } } // 检查路径长度(Windows) if cfg!(windows) && venv_path.to_string_lossy().len() > 260 { issues.push(format!( "虚拟环境路径过长 ({} 字符),Windows限制为260字符", venv_path.to_string_lossy().len() )); } issues } /// 生成虚拟环境问题的恢复建议 pub async fn get_venv_recovery_suggestions(&self) -> Vec { let mut suggestions = Vec::new(); let issues = self.diagnose_venv_path_issues().await; if issues.is_empty() { suggestions.push("虚拟环境路径检查通过,可以尝试创建虚拟环境".to_string()); return suggestions; } suggestions.push("检测到以下虚拟环境路径问题:".to_string()); for issue in &issues { suggestions.push(format!(" - {issue}")); } suggestions.push("".to_string()); suggestions.push("建议的解决方案:".to_string()); // 基于问题类型提供具体建议 for issue in &issues { if issue.contains("不存在") { suggestions.push("1. 确保在正确的项目目录中运行命令".to_string()); suggestions.push("2. 检查当前工作目录: pwd (Unix) 或 cd (Windows)".to_string()); } else if issue.contains("权限") { suggestions.push("1. 检查目录权限: ls -la (Unix)".to_string()); suggestions.push("2. 使用管理员权限运行命令".to_string()); if cfg!(unix) { suggestions.push("3. 修改目录权限: chmod 755 .".to_string()); suggestions.push("4. 修改目录所有者: chown $USER .".to_string()); } } else if issue.contains("不是目录") { suggestions .push("1. 删除同名文件: rm venv (Unix) 或 del venv (Windows)".to_string()); suggestions.push("2. 重新创建虚拟环境".to_string()); } else if issue.contains("不完整") { suggestions.push("1. 删除损坏的虚拟环境: rm -rf ./venv".to_string()); suggestions.push("2. 重新运行 document-parser uv-init".to_string()); } else if issue.contains("路径过长") { suggestions.push("1. 移动项目到路径较短的目录".to_string()); suggestions.push("2. 使用较短的目录名称".to_string()); } else if issue.contains("磁盘空间") { suggestions.push("1. 清理磁盘空间,至少保留500MB可用空间".to_string()); suggestions.push("2. 删除不需要的文件和目录".to_string()); } } suggestions.push("".to_string()); suggestions.push("如果问题仍然存在,请尝试:".to_string()); suggestions.push("1. 重启终端或命令提示符".to_string()); suggestions.push("2. 检查防病毒软件是否阻止文件操作".to_string()); suggestions.push("3. 在不同的目录中尝试创建虚拟环境".to_string()); suggestions } /// 尝试自动修复常见的虚拟环境路径问题 pub async fn auto_fix_venv_path_issues(&self) -> Result, AppError> { let mut fixed_issues = Vec::new(); let venv_path = Path::new(&self.base_dir).join("venv"); // 尝试清理损坏的虚拟环境 if venv_path.exists() && !venv_path.is_dir() { match std::fs::remove_file(&venv_path) { Ok(_) => { fixed_issues.push(format!( "已删除阻碍虚拟环境创建的文件: {}", venv_path.display() )); } Err(e) => { return Err(AppError::permission_error( format!("无法删除阻碍文件: {e}"), &venv_path, )); } } } // 尝试清理损坏的虚拟环境目录 if venv_path.exists() && venv_path.is_dir() { let python_exe = Self::get_venv_python_path(&venv_path); if !python_exe.exists() { match self.cleanup_corrupted_venv(&venv_path).await { Ok(_) => { fixed_issues.push(format!("已清理损坏的虚拟环境: {}", venv_path.display())); } Err(e) => { return Err(e); } } } } Ok(fixed_issues) } /// 获取缓存的环境状态 async fn get_cached_status(&self) -> Option { self.environment_cache.read().await.clone() } /// 更新环境状态缓存 async fn update_cache(&self, status: EnvironmentStatus) { *self.environment_cache.write().await = Some(status); } /// 清除环境状态缓存 pub async fn clear_cache(&self) { *self.environment_cache.write().await = None; } /// 验证虚拟环境状态并添加相关问题和警告 fn validate_virtual_environment_status( &self, status: &mut EnvironmentStatus, python_info: &PythonInfo, ) { if !python_info.virtual_env_active { let issue = EnvironmentIssue { component: "Virtual Environment".to_string(), severity: IssueSeverity::High, message: "虚拟环境未激活".to_string(), suggestion: format!( "创建并激活虚拟环境: 运行 'document-parser uv-init' 或手动运行 '{}'", self.get_activation_command() ), auto_fixable: true, }; status.issues.push(issue); } else { // 检查虚拟环境路径是否符合预期 if let Some(ref venv_path) = python_info.virtual_env_path { let expected_venv_path = std::env::current_dir().map(|dir| dir.join("venv")).ok(); let is_expected_location = expected_venv_path .as_ref() .map(|expected| venv_path.contains(&expected.to_string_lossy().to_string())) .unwrap_or(false); if !is_expected_location { let warning = EnvironmentWarning { component: "Virtual Environment".to_string(), message: format!("虚拟环境位于非预期位置: {venv_path}"), impact: "可能影响依赖管理和路径解析".to_string(), }; status.warnings.push(warning); } } // 检查虚拟环境中的Python可执行文件 let expected_python_path = std::env::current_dir() .map(|dir| Self::get_venv_python_path(&dir.join("venv"))) .ok(); if let Some(expected_path) = expected_python_path { if !expected_path.exists() { let issue = EnvironmentIssue { component: "Virtual Environment".to_string(), severity: IssueSeverity::Medium, message: format!( "预期的Python可执行文件不存在: {}", expected_path.display() ), suggestion: "重新创建虚拟环境: 运行 'document-parser uv-init'".to_string(), auto_fixable: true, }; status.issues.push(issue); } } } } /// 获取虚拟环境激活命令 fn get_activation_command(&self) -> String { if cfg!(windows) { ".\\venv\\Scripts\\activate.bat".to_string() } else { "source ./venv/bin/activate".to_string() } } /// 获取Python安装建议 fn get_python_installation_suggestion(&self, error_message: &str) -> String { if error_message.contains("command not found") || error_message.contains("not found") { "Python未安装。请安装Python 3.8+: https://www.python.org/downloads/".to_string() } else if error_message.contains("版本过低") { "Python版本过低。请升级到Python 3.8或更高版本".to_string() } else if error_message.contains("超时") { "Python命令执行超时。检查系统负载或Python安装是否正常".to_string() } else { format!("Python环境问题: {error_message}。请检查Python安装并确保可以正常执行") } } /// 获取UV安装建议 fn get_uv_installation_suggestion(&self, error_message: &str) -> String { if error_message.contains("command not found") || error_message.contains("not found") { "UV工具未安装。安装命令: curl -LsSf https://astral.sh/uv/install.sh | sh".to_string() } else if error_message.contains("版本") { "UV版本不兼容。请更新到最新版本: curl -LsSf https://astral.sh/uv/install.sh | sh" .to_string() } else { format!("UV工具问题: {error_message}。请重新安装UV工具") } } /// 获取MinerU安装建议 fn get_mineru_installation_suggestion(&self, error_message: &str) -> String { if error_message.contains("command not found") || error_message.contains("not found") { "MinerU未安装。在虚拟环境中安装: uv pip install magic-pdf[full]".to_string() } else if error_message.contains("模块") || error_message.contains("module") { "MinerU模块缺失。重新安装: uv pip install --force-reinstall magic-pdf[full]".to_string() } else if error_message.contains("版本") { "MinerU版本问题。更新到最新版本: uv pip install -U magic-pdf[full]".to_string() } else { format!("MinerU问题: {error_message}。请检查安装或重新安装") } } /// 获取MarkItDown安装建议 fn get_markitdown_installation_suggestion(&self, error_message: &str) -> String { if error_message.contains("模块") || error_message.contains("module") { "MarkItDown模块未找到。在虚拟环境中安装: uv pip install markitdown".to_string() } else if error_message.contains("版本") { "MarkItDown版本问题。更新到最新版本: uv pip install -U markitdown".to_string() } else { format!("MarkItDown问题: {error_message}。请检查安装或重新安装") } } /// 带重试的Python环境检查 async fn check_python_environment_with_retry(&self) -> Result { self.retry_with_backoff("Python环境检查", || self.check_python_environment()) .await } /// 带重试的uv环境检查 async fn check_uv_environment_with_retry(&self) -> Result { self.retry_with_backoff("uv环境检查", || self.check_uv_environment()) .await } /// 带重试的CUDA环境检查 async fn check_cuda_environment_with_retry(&self) -> Result { self.retry_with_backoff("CUDA环境检查", || self.check_cuda_environment()) .await } /// 带重试的MinerU环境检查 async fn check_mineru_environment_with_retry(&self) -> Result { self.retry_with_backoff("MinerU环境检查", || self.check_mineru_environment()) .await } /// 带重试的MarkItDown环境检查 async fn check_markitdown_environment_with_retry(&self) -> Result { self.retry_with_backoff("MarkItDown环境检查", || { self.check_markitdown_environment() }) .await } /// 验证依赖版本兼容性 pub async fn verify_dependency_compatibility( &self, ) -> Result { debug!("Start relying on version compatibility verification"); let (mineru_result, markitdown_result) = tokio::join!( self.verify_mineru_dependency(), self.verify_markitdown_dependency() ); let mineru_status = mineru_result.unwrap_or_else(|e| DependencyStatus { package_name: "MinerU".to_string(), is_available: false, is_functional: false, version_info: None, compatibility: None, issues: vec![e.to_string()], path: None, }); let markitdown_status = markitdown_result.unwrap_or_else(|e| DependencyStatus { package_name: "MarkItDown".to_string(), is_available: false, is_functional: false, version_info: None, compatibility: None, issues: vec![e.to_string()], path: None, }); let overall_compatible = mineru_status.is_available && mineru_status.is_functional && markitdown_status.is_available && markitdown_status.is_functional && mineru_status .compatibility .as_ref() .is_none_or(|c| c.is_compatible) && markitdown_status .compatibility .as_ref() .is_none_or(|c| c.is_compatible); let mut recommendations = Vec::new(); let mut critical_issues = Vec::new(); // 收集推荐和关键问题 if let Some(ref compat) = mineru_status.compatibility { if !compat.is_compatible { critical_issues.push(format!( "MinerU版本不兼容: {} (最低要求: {})", compat.current_version, compat.minimum_version )); } if compat.upgrade_available { if let Some(ref rec) = compat.upgrade_recommendation { recommendations.push(rec.clone()); } } } if let Some(ref compat) = markitdown_status.compatibility { if !compat.is_compatible { critical_issues.push(format!( "MarkItDown版本不兼容: {} (最低要求: {})", compat.current_version, compat.minimum_version )); } if compat.upgrade_available { if let Some(ref rec) = compat.upgrade_recommendation { recommendations.push(rec.clone()); } } } // 添加通用推荐 if !mineru_status.is_available { recommendations.push("安装MinerU: uv pip install -U \"mineru[core]\"".to_string()); } if !markitdown_status.is_available { recommendations.push("安装MarkItDown: uv pip install markitdown".to_string()); } Ok(DependencyVerificationResult { mineru_status, markitdown_status, overall_compatible, recommendations, critical_issues, }) } /// 验证MinerU依赖 async fn verify_mineru_dependency(&self) -> Result { let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let venv_path = current_dir.join("venv"); let mineru_path = Self::get_venv_executable_path(&venv_path, "mineru"); let mut status = DependencyStatus { package_name: "MinerU".to_string(), is_available: mineru_path.exists(), is_functional: false, version_info: None, compatibility: None, issues: Vec::new(), path: Some(mineru_path.to_string_lossy().to_string()), }; if !status.is_available { status.issues.push("MinerU命令不存在".to_string()); return Ok(status); } // 检查功能性 match self.check_mineru_environment().await { Ok(package_info) => { status.is_functional = true; status.version_info = Some(package_info.clone()); // 验证版本兼容性 status.compatibility = Some( self.check_mineru_version_compatibility(&package_info.version) .await, ); } Err(e) => { status.issues.push(format!("MinerU功能检查失败: {e}")); } } Ok(status) } /// 验证MarkItDown依赖 async fn verify_markitdown_dependency(&self) -> Result { let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let venv_path = current_dir.join("venv"); let python_path = Self::get_venv_python_path(&venv_path); let mut status = DependencyStatus { package_name: "MarkItDown".to_string(), is_available: false, is_functional: false, version_info: None, compatibility: None, issues: Vec::new(), path: Some(python_path.to_string_lossy().to_string()), }; // 检查功能性 match self.check_markitdown_environment().await { Ok(package_info) => { status.is_available = true; status.is_functional = true; status.version_info = Some(package_info.clone()); // 验证版本兼容性 status.compatibility = Some( self.check_markitdown_version_compatibility(&package_info.version) .await, ); } Err(e) => { status.issues.push(format!("MarkItDown检查失败: {e}")); } } Ok(status) } /// 检查MinerU版本兼容性 async fn check_mineru_version_compatibility( &self, current_version: &str, ) -> PackageCompatibility { let minimum_version = "0.1.0"; // MinerU最低版本要求 let recommended_version = "latest"; // 推荐版本 let is_compatible = self.is_version_compatible(current_version, minimum_version); let upgrade_available = current_version != "latest" && current_version != "available"; let mut compatibility_issues = Vec::new(); let mut upgrade_recommendation = None; if !is_compatible { compatibility_issues.push(format!( "当前版本 {current_version} 低于最低要求版本 {minimum_version}" )); } if upgrade_available { upgrade_recommendation = Some("升级MinerU到最新版本: uv pip install -U \"mineru[core]\"".to_string()); } // 检查特定版本的已知问题 if current_version.contains("0.0.") { compatibility_issues.push("检测到早期版本,可能存在稳定性问题".to_string()); } PackageCompatibility { package_name: "MinerU".to_string(), current_version: current_version.to_string(), minimum_version: minimum_version.to_string(), recommended_version: Some(recommended_version.to_string()), is_compatible, compatibility_issues, upgrade_available, upgrade_recommendation, } } /// 检查MarkItDown版本兼容性 async fn check_markitdown_version_compatibility( &self, current_version: &str, ) -> PackageCompatibility { let minimum_version = "0.0.1"; // MarkItDown最低版本要求 let recommended_version = "latest"; // 推荐版本 let is_compatible = self.is_version_compatible(current_version, minimum_version); let upgrade_available = current_version != "latest" && current_version != "available"; let mut compatibility_issues = Vec::new(); let mut upgrade_recommendation = None; if !is_compatible { compatibility_issues.push(format!( "当前版本 {current_version} 低于最低要求版本 {minimum_version}" )); } if upgrade_available { upgrade_recommendation = Some("升级MarkItDown到最新版本: uv pip install -U markitdown".to_string()); } PackageCompatibility { package_name: "MarkItDown".to_string(), current_version: current_version.to_string(), minimum_version: minimum_version.to_string(), recommended_version: Some(recommended_version.to_string()), is_compatible, compatibility_issues, upgrade_available, upgrade_recommendation, } } /// 比较版本号兼容性(简单的语义版本比较) fn is_version_compatible(&self, current: &str, minimum: &str) -> bool { // 处理特殊版本字符串 if current == "available" || current == "latest" || current == "unknown" { return true; // 假设可用 } // 简单的版本比较逻辑 match (self.parse_version(current), self.parse_version(minimum)) { (Some(current_parts), Some(min_parts)) => { for i in 0..3 { let current_part = current_parts.get(i).unwrap_or(&0); let min_part = min_parts.get(i).unwrap_or(&0); if current_part > min_part { return true; } else if current_part < min_part { return false; } } true // 版本相等 } _ => true, // 无法解析版本时假设兼容 } } /// 解析版本号为数字数组 fn parse_version(&self, version: &str) -> Option> { // 提取版本号中的数字部分 let version_clean = version .split_whitespace() .next()? .trim_start_matches('v') .split('-') .next()?; let parts: Result, _> = version_clean .split('.') .take(3) .map(|s| s.parse::()) .collect(); parts.ok() } /// 通用重试机制 async fn retry_with_backoff( &self, operation_name: &str, mut operation: F, ) -> Result where F: FnMut() -> Fut, Fut: std::future::Future>, { let mut last_error = None; let mut delay = self.retry_config.base_delay; for attempt in 1..=self.retry_config.max_attempts { match operation().await { Ok(result) => { if attempt > 1 { info!("{} succeeded after {} attempt", operation_name, attempt); } return Ok(result); } Err(e) => { last_error = Some(e); if attempt < self.retry_config.max_attempts { warn!( "{} The {} attempt failed, try again in {} seconds", operation_name, attempt, delay.as_secs_f32() ); // 发送重试进度 if let Some(sender) = &self.progress_sender { let progress = InstallProgress { package: operation_name.to_string(), stage: InstallStage::Retrying { attempt, max_attempts: self.retry_config.max_attempts, }, progress: (attempt as f32 / self.retry_config.max_attempts as f32) * 100.0, message: format!( "重试中... ({}/{})", attempt, self.retry_config.max_attempts ), estimated_time_remaining: Some( delay * (self.retry_config.max_attempts - attempt), ), bytes_downloaded: None, total_bytes: None, }; if let Ok(sender) = sender.try_lock() { let _ = sender.send(progress); } } sleep(delay).await; delay = std::cmp::min( Duration::from_secs_f64( delay.as_secs_f64() * self.retry_config.backoff_multiplier, ), self.retry_config.max_delay, ); } } } } error!( "{} Still failed after {} attempts", operation_name, self.retry_config.max_attempts ); Err(last_error.unwrap_or_else(|| AppError::Environment(format!("{operation_name} 失败")))) } /// 检查Python环境 #[instrument(skip(self))] async fn check_python_environment(&self) -> Result { debug!("Check Python environment: {}", self.python_path); // 首先检查配置的Python路径是否存在 let python_executable = if Path::new(&self.python_path).exists() { self.python_path.clone() } else { // 如果虚拟环境Python不存在,尝试使用系统Python debug!( "The virtual environment Python path does not exist, try to find the system Python" ); self.find_system_python().await.unwrap_or_else(|| { // 如果找不到系统Python,使用平台默认值 if cfg!(windows) { "python.exe".to_string() } else { "python3".to_string() } }) }; // 检查Python版本(带超时) let version_cmd = Command::new(&python_executable).arg("--version").output(); let output = timeout(self.timeout_duration, version_cmd) .await .map_err(|_| { AppError::Environment(format!( "Python版本检查超时: {}", self.timeout_duration.as_secs() )) })? .map_err(|e| AppError::Environment(format!("无法执行Python命令: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::Environment(format!( "Python命令执行失败: {stderr}" ))); } let version_output = String::from_utf8_lossy(&output.stdout); let version = version_output.trim().to_string(); // 验证Python版本是否符合要求(3.8+) if let Some(version_num) = self.extract_python_version(&version) { if version_num < (3, 8) { return Err(AppError::Environment(format!( "Python版本过低: {version},需要3.8或更高版本" ))); } } // 检查虚拟环境(带超时) let venv_cmd = Command::new(&python_executable) .arg("-c") .arg("import sys; print(hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)); print(getattr(sys, 'prefix', ''))") .output(); let venv_output = timeout(self.timeout_duration, venv_cmd) .await .map_err(|_| AppError::Environment("虚拟环境检查超时".to_string()))? .map_err(|e| AppError::Environment(format!("无法检查虚拟环境: {e}")))?; let venv_info = String::from_utf8_lossy(&venv_output.stdout); let lines: Vec<&str> = venv_info.trim().split('\n').collect(); let virtual_env_active = lines .first() .and_then(|line| line.parse::().ok()) .unwrap_or(false); let virtual_env_path = if virtual_env_active { lines.get(1).map(|s| s.to_string()) } else { None }; debug!("Python environment check passed: {}", version); if virtual_env_active { debug!("Virtual environment detected: {:?}", virtual_env_path); } Ok(PythonInfo { version: Some(version), path: python_executable, virtual_env_active, virtual_env_path, }) } /// 提取Python版本号 fn extract_python_version(&self, version_str: &str) -> Option<(u32, u32)> { // 解析类似 "Python 3.9.7" 的版本字符串 let parts: Vec<&str> = version_str.split_whitespace().collect(); if parts.len() >= 2 { let version_part = parts[1]; let version_nums: Vec<&str> = version_part.split('.').collect(); if version_nums.len() >= 2 { if let (Ok(major), Ok(minor)) = ( version_nums[0].parse::(), version_nums[1].parse::(), ) { return Some((major, minor)); } } } None } /// 检查uv工具 async fn check_uv_environment(&self) -> Result { debug!("Check uv tools"); let uv_cmd = Command::new("uv").arg("--version").output(); let output = timeout(self.timeout_duration, uv_cmd) .await .map_err(|_| AppError::Environment("uv版本检查超时".to_string()))? .map_err(|e| AppError::Environment(format!("无法执行uv命令: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::Environment(format!("uv命令执行失败: {stderr}"))); } let version_output = String::from_utf8_lossy(&output.stdout); let version = version_output.trim().to_string(); debug!("UV tool inspection passed: {}", version); Ok(UvInfo { version }) } /// 检查CUDA环境 pub async fn check_cuda_environment(&self) -> Result { debug!("Check CUDA environment"); let nvidia_cmd = Command::new("nvidia-smi") .arg("--query-gpu=index,name,memory.total,memory.free,compute_cap") .arg("--format=csv,noheader,nounits") .output(); let output = match timeout(Duration::from_secs(10), nvidia_cmd).await { Ok(Ok(output)) if output.status.success() => output, Ok(Ok(_)) => { debug!("nvidia-smi execution failed, CUDA is not available"); return Ok(CudaInfo { available: false, version: None, devices: Vec::new(), }); } Ok(Err(_)) | Err(_) => { debug!("CUDA environment is not available"); return Ok(CudaInfo { available: false, version: None, devices: Vec::new(), }); } }; // 解析CUDA设备信息 let output_str = String::from_utf8_lossy(&output.stdout); let mut devices = Vec::new(); for line in output_str.lines() { if let Some(device) = self.parse_cuda_device_info(line) { devices.push(device); } } // 获取CUDA版本 let version = self.get_cuda_version().await; debug!( "CUDA environment check completed: available={}, devices={}", !devices.is_empty(), devices.len() ); Ok(CudaInfo { available: !devices.is_empty(), version, devices, }) } /// 解析CUDA设备信息 fn parse_cuda_device_info(&self, line: &str) -> Option { let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect(); if parts.len() >= 5 { if let (Ok(id), Ok(memory_total), Ok(memory_free)) = ( parts[0].parse::(), parts[2].parse::(), parts[3].parse::(), ) { return Some(CudaDevice { id, name: parts[1].to_string(), memory_total: memory_total * 1024 * 1024, // 转换为字节 memory_free: memory_free * 1024 * 1024, // 转换为字节 compute_capability: parts[4].to_string(), }); } } None } /// 获取CUDA版本 async fn get_cuda_version(&self) -> Option { let version_cmd = Command::new("nvidia-smi") .arg("--query-gpu=driver_version") .arg("--format=csv,noheader,nounits") .output(); if let Ok(Ok(output)) = timeout(Duration::from_secs(5), version_cmd).await { if output.status.success() { let version_str = String::from_utf8_lossy(&output.stdout); return Some(version_str.trim().to_string()); } } None } /// 检查MinerU环境 async fn check_mineru_environment(&self) -> Result { debug!("Check MinerU environment"); // 使用当前目录的虚拟环境中的mineru命令路径 let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let venv_path = current_dir.join("venv"); let mineru_path = Self::get_venv_executable_path(&venv_path, "mineru"); // 首先检查mineru可执行文件是否存在 if !mineru_path.exists() { return Err(AppError::Environment(format!( "MinerU命令不存在: {}. 请运行 'uv pip install -U \"mineru[core]\"' 安装MinerU", mineru_path.display() ))); } // 检查mineru命令是否可执行 let help_cmd = Command::new(&mineru_path).arg("--help").output(); let help_output = timeout(self.timeout_duration, help_cmd) .await .map_err(|_| AppError::Environment("MinerU帮助命令检查超时".to_string()))? .map_err(|e| { AppError::Environment(format!( "无法执行MinerU帮助命令: {e}. 请确保已正确安装MinerU" )) })?; if !help_output.status.success() { let stderr = String::from_utf8_lossy(&help_output.stderr); return Err(AppError::Environment(format!( "MinerU帮助命令执行失败: {stderr}. 请检查MinerU安装" ))); } // 验证mineru命令功能性 - 测试基本功能 let test_cmd = Command::new(&mineru_path).arg("--version").output(); let version_output = timeout(Duration::from_secs(30), test_cmd) .await .map_err(|_| AppError::Environment("MinerU版本检查超时".to_string()))?; let version = match version_output { Ok(output) if output.status.success() => { let version_str = String::from_utf8_lossy(&output.stdout); let version = version_str.trim().to_string(); if version.is_empty() { // 如果版本输出为空,尝试从stderr获取 let stderr_str = String::from_utf8_lossy(&output.stderr); if !stderr_str.is_empty() { stderr_str.trim().to_string() } else { "unknown".to_string() } } else { version } } Ok(output) => { // 版本命令失败,但帮助命令成功,说明mineru可用但版本获取有问题 let stderr = String::from_utf8_lossy(&output.stderr); warn!( "MinerU version acquisition failed, but the command is available: {}", stderr ); "available".to_string() } Err(e) => { return Err(AppError::Environment(format!( "MinerU版本检查执行失败: {e}. 请检查MinerU安装" ))); } }; // MinerU命令验证已通过,无需额外的模块导入测试 debug!("MinerU environment check passed, version: {}", version); Ok(PackageInfo { version }) } /// 检查MarkItDown环境 async fn check_markitdown_environment(&self) -> Result { debug!("Check MarkItDown environment"); // 优先使用虚拟环境中的Python let current_dir = std::env::current_dir() .map_err(|e| AppError::Environment(format!("无法获取当前目录: {e}")))?; let venv_path = current_dir.join("venv"); let python_executable = if venv_path.exists() { Self::get_venv_python_path(&venv_path) } else if Path::new(&self.python_path).exists() { std::path::PathBuf::from(&self.python_path) } else { // 回退到系统Python let system_python = self.find_system_python().await.unwrap_or_else(|| { if cfg!(windows) { "python.exe".to_string() } else { "python3".to_string() } }); std::path::PathBuf::from(system_python) }; // 首先测试MarkItDown模块导入 let import_test_cmd = Command::new(&python_executable) .arg("-c") .arg("import markitdown; print('MarkItDown模块导入成功')") .output(); let import_output = timeout(self.timeout_duration, import_test_cmd) .await .map_err(|_| AppError::Environment("MarkItDown模块导入测试超时".to_string()))? .map_err(|e| AppError::Environment(format!("无法测试MarkItDown模块导入: {e}")))?; if !import_output.status.success() { let stderr = String::from_utf8_lossy(&import_output.stderr); return Err(AppError::Environment(format!( "MarkItDown模块导入失败: {stderr}. 请运行 'uv pip install markitdown' 安装MarkItDown" ))); } // 获取版本信息 let version_cmd = Command::new(&python_executable) .arg("-c") .arg("import markitdown; print(markitdown.__version__)") .output(); let version_output = timeout(self.timeout_duration, version_cmd) .await .map_err(|_| AppError::Environment("MarkItDown版本检查超时".to_string()))? .map_err(|e| AppError::Environment(format!("无法获取MarkItDown版本: {e}")))?; let version = if version_output.status.success() { let version_str = String::from_utf8_lossy(&version_output.stdout); version_str.trim().to_string() } else { // 如果版本获取失败但导入成功,使用默认版本 warn!("MarkItDown version acquisition failed, but the module is available"); "available".to_string() }; // 功能性验证 - 测试MarkItDown基本功能 let functionality_test_cmd = Command::new(&python_executable) .arg("-c") .arg( r#" import markitdown from markitdown import MarkItDown md = MarkItDown() # 测试基本功能是否可用 print('MarkItDown功能验证成功') "#, ) .output(); let func_result = timeout(Duration::from_secs(15), functionality_test_cmd).await; match func_result { Ok(Ok(output)) if output.status.success() => { debug!("MarkItDown function verification successful"); } Ok(Ok(output)) => { let stderr = String::from_utf8_lossy(&output.stderr); warn!("MarkItDown function verification failed: {}", stderr); return Err(AppError::Environment(format!( "MarkItDown功能验证失败: {stderr}. 请重新安装MarkItDown" ))); } Ok(Err(e)) => { warn!("MarkItDown functional test execution failed: {}", e); } Err(_) => { warn!("MarkItDown function test timeout"); } } debug!("MarkItDown environment check passed: {}", version); Ok(PackageInfo { version }) } /// Python环境设置 #[instrument(skip(self))] pub async fn setup_python_environment(&self) -> Result<(), AppError> { info!("Start Python environment setup"); // 发送开始进度 self.send_progress("环境设置", InstallStage::Preparing, 0.0, "准备环境设置") .await; // 确保基础目录存在 self.ensure_base_directory().await?; self.send_progress("环境设置", InstallStage::Preparing, 10.0, "准备工作目录") .await; // 检查并安装uv match self.is_uv_available().await? { UvAvailabilityStatus::Available { version, compatibility, } => { if compatibility.is_compatible { info!("uv tool is available and compatible: {}", version); if let Some(recommendation) = compatibility.recommendation { info!("UV upgrade suggestion: {}", recommendation); } } else { warn!( "The uv version is incompatible, reinstall: {}", compatibility.recommendation.unwrap_or_default() ); self.send_progress( "环境设置", InstallStage::Installing, 20.0, "重新安装兼容版本的uv", ) .await; self.install_uv_with_progress().await?; } } UvAvailabilityStatus::IncompatibleVersion { version, issue } => { warn!("UV version is not compatible: {} - {}", version, issue); self.send_progress( "环境设置", InstallStage::Installing, 20.0, "安装兼容版本的uv", ) .await; self.install_uv_with_progress().await?; } UvAvailabilityStatus::ExecutionFailed { error } => { warn!("UV execution failed, reinstall: {}", error); self.send_progress("环境设置", InstallStage::Installing, 20.0, "重新安装uv工具") .await; self.install_uv_with_progress().await?; } UvAvailabilityStatus::NotInstalled { error: _ } => { info!("The uv tool is not installed, start the installation"); self.send_progress("环境设置", InstallStage::Installing, 20.0, "安装uv工具") .await; self.install_uv_with_progress().await?; } } // 创建Python虚拟环境 self.send_progress( "环境设置", InstallStage::Configuring, 40.0, "创建Python虚拟环境", ) .await; self.create_python_venv_with_progress().await?; // 安装依赖 self.send_progress("环境设置", InstallStage::Installing, 60.0, "安装Python依赖") .await; self.install_dependencies().await?; // 验证安装(非阻塞) self.send_progress("环境设置", InstallStage::Verifying, 90.0, "验证环境") .await; match self.validate_engines().await { Ok(is_valid) => { if is_valid { self.send_progress("环境设置", InstallStage::Completed, 100.0, "环境设置完成") .await; info!("Python environment setup completed"); } else { warn!( "Environment verification did not fully pass, but the installation process was completed" ); self.send_progress( "环境设置", InstallStage::Completed, 100.0, "安装完成(部分验证待完善)", ) .await; } } Err(e) => { warn!( "There was a problem with the environment verification process: {}", e ); self.send_progress( "环境设置", InstallStage::Completed, 100.0, "安装完成(验证待重试)", ) .await; } } // 清除缓存以强制重新检查 self.clear_cache().await; Ok(()) } /// 安装依赖包 #[instrument(skip(self))] pub async fn install_dependencies(&self) -> Result<(), AppError> { info!("Start installing Python dependencies"); // 并行安装MinerU和MarkItDown let (mineru_result, markitdown_result) = tokio::join!( self.install_mineru_with_progress(), self.install_markitdown_with_progress() ); mineru_result?; markitdown_result?; info!("Python dependency installation completed"); Ok(()) } /// 验证所有引擎 #[instrument(skip(self))] pub async fn validate_engines(&self) -> Result { info!("Verify parsing engine"); // 清除缓存以确保获取最新状态 self.clear_cache().await; // 等待一小段时间确保安装完成 sleep(Duration::from_millis(500)).await; let status = self.check_environment().await?; let is_valid = status.is_ready(); if !is_valid { let critical_issues = status.get_critical_issues(); for issue in critical_issues { error!("Key questions: {} - {}", issue.component, issue.message); } } Ok(is_valid) } /// 发送安装进度 async fn send_progress( &self, package: &str, stage: InstallStage, progress: f32, message: &str, ) { if let Some(sender) = &self.progress_sender { let progress_info = InstallProgress { package: package.to_string(), stage, progress, message: message.to_string(), estimated_time_remaining: None, bytes_downloaded: None, total_bytes: None, }; if let Ok(sender) = sender.try_lock() { let _ = sender.send(progress_info); } } } /// 确保基础目录存在 async fn ensure_base_directory(&self) -> Result<(), AppError> { if !Path::new(&self.base_dir).exists() { std::fs::create_dir_all(&self.base_dir) .map_err(|e| AppError::File(format!("创建基础目录失败: {e}")))?; info!("Create base directory: {}", self.base_dir); } Ok(()) } /// 检查uv是否可用(增强版本,带详细错误报告) pub async fn is_uv_available(&self) -> Result { debug!("Check uv tool availability"); let uv_cmd = Command::new("uv").arg("--version").output(); let output = timeout(Duration::from_secs(10), uv_cmd) .await .map_err(|_| AppError::Environment("uv版本检查超时".to_string()))?; match output { Ok(output) if output.status.success() => { let version_output = String::from_utf8_lossy(&output.stdout); let version = version_output.trim().to_string(); // 检查版本兼容性 match self.check_uv_version_compatibility(&version) { Ok(compatibility) => { debug!("uv tool available: {}", version); Ok(UvAvailabilityStatus::Available { version, compatibility, }) } Err(e) => { warn!("Incompatible uv version: {}", e); Ok(UvAvailabilityStatus::IncompatibleVersion { version, issue: e.to_string(), }) } } } Ok(output) => { let stderr = String::from_utf8_lossy(&output.stderr); let error_msg = if stderr.is_empty() { "uv命令执行失败,未知错误".to_string() } else { format!("uv命令执行失败: {stderr}") }; debug!("uv command execution failed: {}", error_msg); Ok(UvAvailabilityStatus::ExecutionFailed { error: error_msg }) } Err(e) => { let error_msg = format!("无法执行uv命令: {e}"); debug!( "The uv command does not exist or cannot be executed: {}", error_msg ); Ok(UvAvailabilityStatus::NotInstalled { error: error_msg }) } } } /// 检查UV版本兼容性 fn check_uv_version_compatibility( &self, version_str: &str, ) -> Result { // UV最低版本要求:0.1.0 let minimum_version = "0.1.0"; // 解析版本号 let current_version = self .extract_uv_version(version_str) .ok_or_else(|| AppError::Environment(format!("无法解析uv版本: {version_str}")))?; let min_version = self.parse_version_tuple(minimum_version).ok_or_else(|| { AppError::Environment(format!("无法解析最低版本要求: {minimum_version}")) })?; let is_compatible = current_version >= min_version; let recommendation = if !is_compatible { Some(format!( "请升级uv到{minimum_version}或更高版本,运行: curl -LsSf https://astral.sh/uv/install.sh | sh" )) } else if current_version.0 == 0 && current_version.1 < 5 { // 如果版本低于0.5.0,建议升级以获得更好的性能 Some("建议升级到uv 0.5.0+以获得更好的性能和稳定性".to_string()) } else { None }; Ok(UvVersionCompatibility { is_compatible, minimum_version: minimum_version.to_string(), current_version: version_str.to_string(), recommendation, }) } /// 提取UV版本号 fn extract_uv_version(&self, version_str: &str) -> Option<(u32, u32, u32)> { // 解析类似 "uv 0.4.15" 或 "0.4.15" 的版本字符串 let version_part = if version_str.starts_with("uv ") { version_str.strip_prefix("uv ").unwrap_or(version_str) } else { version_str }; self.parse_version_tuple(version_part) } /// 解析版本号为元组 (major, minor, patch) fn parse_version_tuple(&self, version_str: &str) -> Option<(u32, u32, u32)> { let parts: Vec<&str> = version_str.split('.').collect(); if parts.len() >= 2 { let major = parts[0].parse::().ok()?; let minor = parts[1].parse::().ok()?; let patch = if parts.len() >= 3 { parts[2].parse::().unwrap_or(0) } else { 0 }; Some((major, minor, patch)) } else { None } } /// 安装uv工具(增强版本,带进度跟踪和多种安装方法) pub async fn install_uv_with_progress(&self) -> Result<(), AppError> { info!("Start installing uv tools"); self.send_progress("uv", InstallStage::Preparing, 0.0, "准备安装uv工具") .await; // 确定最佳安装方法 let installation_method = self.determine_best_uv_installation_method().await; info!("Select installation method: {:?}", installation_method); // 尝试安装 let install_result = match installation_method { UvInstallationMethod::CurlScript => self.install_uv_with_curl_script().await, UvInstallationMethod::PowerShellScript => { self.install_uv_with_powershell_script().await } UvInstallationMethod::PipInstall => self.install_uv_with_pip().await, UvInstallationMethod::SystemPackageManager => { self.install_uv_with_system_package_manager().await } }; match install_result { Ok(_) => { self.send_progress("uv", InstallStage::Verifying, 90.0, "验证uv安装") .await; // 验证安装 match self.is_uv_available().await? { UvAvailabilityStatus::Available { version, compatibility, } => { if compatibility.is_compatible { self.send_progress( "uv", InstallStage::Completed, 100.0, &format!("uv安装完成: {version}"), ) .await; info!("UV installation successful: {}", version); Ok(()) } else { let error_msg = format!( "uv版本不兼容: {}", compatibility.recommendation.unwrap_or_default() ); self.send_progress( "uv", InstallStage::Failed(error_msg.clone()), 0.0, "版本不兼容", ) .await; Err(AppError::Environment(error_msg)) } } UvAvailabilityStatus::IncompatibleVersion { version, issue } => { let error_msg = format!("uv版本不兼容: {version} - {issue}"); self.send_progress( "uv", InstallStage::Failed(error_msg.clone()), 0.0, "版本不兼容", ) .await; Err(AppError::Environment(error_msg)) } UvAvailabilityStatus::ExecutionFailed { error } => { let error_msg = format!("uv安装后执行失败: {error}"); self.send_progress( "uv", InstallStage::Failed(error_msg.clone()), 0.0, "执行失败", ) .await; Err(AppError::Environment(error_msg)) } UvAvailabilityStatus::NotInstalled { error } => { let error_msg = format!("uv安装后仍不可用: {error}"); self.send_progress( "uv", InstallStage::Failed(error_msg.clone()), 0.0, "安装失败", ) .await; Err(AppError::Environment(error_msg)) } } } Err(e) => { let error_msg = format!("uv安装失败: {e}"); self.send_progress( "uv", InstallStage::Failed(error_msg.clone()), 0.0, "安装失败", ) .await; // 如果主要方法失败,尝试备用方法 warn!("Primary installation method failed, try alternate method"); self.try_fallback_uv_installation().await } } } /// 确定最佳UV安装方法 async fn determine_best_uv_installation_method(&self) -> UvInstallationMethod { if cfg!(target_os = "windows") { // Windows优先使用PowerShell脚本 if self.is_powershell_available().await { UvInstallationMethod::PowerShellScript } else { UvInstallationMethod::CurlScript } } else { // Unix系统优先使用curl脚本 if self.is_curl_available().await { UvInstallationMethod::CurlScript } else if self.is_pip_available().await { UvInstallationMethod::PipInstall } else { UvInstallationMethod::SystemPackageManager } } } /// 检查PowerShell是否可用 async fn is_powershell_available(&self) -> bool { Command::new("powershell") .arg("-Command") .arg("Get-Host") .output() .await .map(|output| output.status.success()) .unwrap_or(false) } /// 检查curl是否可用 async fn is_curl_available(&self) -> bool { Command::new("curl") .arg("--version") .output() .await .map(|output| output.status.success()) .unwrap_or(false) } /// 检查pip是否可用 async fn is_pip_available(&self) -> bool { Command::new("pip") .arg("--version") .output() .await .map(|output| output.status.success()) .unwrap_or(false) } /// 使用curl脚本安装UV async fn install_uv_with_curl_script(&self) -> Result<(), AppError> { self.send_progress("uv", InstallStage::Downloading, 10.0, "下载uv安装脚本") .await; let install_cmd = Command::new("sh") .arg("-c") .arg("curl -LsSf https://astral.sh/uv/install.sh | sh") .output(); self.send_progress("uv", InstallStage::Installing, 50.0, "执行curl安装脚本") .await; let output = timeout(Duration::from_secs(300), install_cmd) .await .map_err(|_| AppError::Environment("uv curl安装超时".to_string()))? .map_err(|e| AppError::Environment(format!("curl安装uv失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::Environment(format!("curl安装uv失败: {stderr}"))); } info!("Successfully installed uv using curl script"); Ok(()) } /// 使用PowerShell脚本安装UV async fn install_uv_with_powershell_script(&self) -> Result<(), AppError> { self.send_progress( "uv", InstallStage::Downloading, 10.0, "下载uv PowerShell脚本", ) .await; let install_cmd = Command::new("powershell") .arg("-ExecutionPolicy") .arg("ByPass") .arg("-c") .arg("irm https://astral.sh/uv/install.ps1 | iex") .output(); self.send_progress( "uv", InstallStage::Installing, 50.0, "执行PowerShell安装脚本", ) .await; let output = timeout(Duration::from_secs(300), install_cmd) .await .map_err(|_| AppError::Environment("uv PowerShell安装超时".to_string()))? .map_err(|e| AppError::Environment(format!("PowerShell安装uv失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::Environment(format!( "PowerShell安装uv失败: {stderr}" ))); } info!("Successfully installed uv using PowerShell script"); Ok(()) } /// 使用pip安装UV async fn install_uv_with_pip(&self) -> Result<(), AppError> { self.send_progress("uv", InstallStage::Installing, 30.0, "使用pip安装uv") .await; let install_cmd = Command::new("pip").arg("install").arg("uv").output(); let output = timeout(Duration::from_secs(180), install_cmd) .await .map_err(|_| AppError::Environment("uv pip安装超时".to_string()))? .map_err(|e| AppError::Environment(format!("pip安装uv失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::Environment(format!("pip安装uv失败: {stderr}"))); } info!("Successfully installed uv using pip"); Ok(()) } /// 使用系统包管理器安装UV async fn install_uv_with_system_package_manager(&self) -> Result<(), AppError> { self.send_progress( "uv", InstallStage::Installing, 30.0, "使用系统包管理器安装uv", ) .await; // 尝试不同的包管理器 let package_managers = if cfg!(target_os = "macos") { vec![("brew", vec!["install", "uv"])] } else if cfg!(target_os = "linux") { vec![ ("apt", vec!["install", "-y", "uv"]), ("yum", vec!["install", "-y", "uv"]), ("dnf", vec!["install", "-y", "uv"]), ("pacman", vec!["-S", "--noconfirm", "uv"]), ] } else { vec![] }; for (manager, args) in package_managers { if self.is_command_available(manager).await { let install_cmd = Command::new(manager).args(&args).output(); match timeout(Duration::from_secs(300), install_cmd).await { Ok(Ok(output)) if output.status.success() => { info!("Use {} to install uv successfully", manager); return Ok(()); } Ok(Ok(output)) => { let stderr = String::from_utf8_lossy(&output.stderr); warn!("{} failed to install uv: {}", manager, stderr); } Ok(Err(e)) => { warn!("{} command execution failed: {}", manager, e); } Err(_) => { warn!("{} installation uv timeout", manager); } } } } Err(AppError::Environment( "所有系统包管理器都无法安装uv".to_string(), )) } /// 检查命令是否可用 async fn is_command_available(&self, command: &str) -> bool { Command::new("which") .arg(command) .output() .await .map(|output| output.status.success()) .unwrap_or(false) } /// 尝试备用UV安装方法 async fn try_fallback_uv_installation(&self) -> Result<(), AppError> { warn!("Try alternative uv installation methods"); // 备用方法列表 let fallback_methods = vec![ UvInstallationMethod::PipInstall, UvInstallationMethod::CurlScript, UvInstallationMethod::SystemPackageManager, ]; for method in fallback_methods { info!("Try alternative installation method: {:?}", method); let result = match method { UvInstallationMethod::PipInstall => self.install_uv_with_pip().await, UvInstallationMethod::CurlScript => self.install_uv_with_curl_script().await, UvInstallationMethod::SystemPackageManager => { self.install_uv_with_system_package_manager().await } UvInstallationMethod::PowerShellScript => { self.install_uv_with_powershell_script().await } }; match result { Ok(_) => { // 验证安装 match self.is_uv_available().await? { UvAvailabilityStatus::Available { version, compatibility, } => { if compatibility.is_compatible { self.send_progress( "uv", InstallStage::Completed, 100.0, &format!("uv备用安装成功: {version}"), ) .await; info!("UV backup installation successful: {}", version); return Ok(()); } } _ => continue, } } Err(e) => { warn!("Alternate installation method failed: {}", e); continue; } } } Err(AppError::Environment("所有uv安装方法都失败了".to_string())) } /// 验证虚拟环境创建的前置条件 async fn validate_venv_creation_preconditions(&self, venv_path: &Path) -> Result<(), AppError> { let base_dir = Path::new(&self.base_dir); // 检查基础目录是否存在 if !base_dir.exists() { return Err(AppError::path_error("基础目录不存在".to_string(), base_dir)); } // 检查基础目录是否为目录 if !base_dir.is_dir() { return Err(AppError::path_error( "基础路径不是目录".to_string(), base_dir, )); } // 检查基础目录写入权限 if let Err(e) = self.check_directory_writable(base_dir).await { return Err(AppError::permission_error( format!("基础目录无写入权限: {e}"), base_dir, )); } // 检查虚拟环境路径是否已存在且为文件(而非目录) if venv_path.exists() && !venv_path.is_dir() { return Err(AppError::virtual_environment_path_error( "虚拟环境路径已存在但不是目录".to_string(), venv_path, )); } // 检查路径长度(Windows路径长度限制) if cfg!(windows) && venv_path.to_string_lossy().len() > 260 { return Err(AppError::virtual_environment_path_error( "虚拟环境路径过长,Windows系统限制为260字符".to_string(), venv_path, )); } Ok(()) } /// 检查目录是否可写 async fn check_directory_writable(&self, dir: &Path) -> Result<(), std::io::Error> { let test_file = dir.join(".write_test"); // 尝试创建测试文件 match std::fs::File::create(&test_file) { Ok(_) => { // 清理测试文件 let _ = std::fs::remove_file(&test_file); Ok(()) } Err(e) => Err(e), } } /// 处理虚拟环境创建错误并提供恢复建议 fn handle_venv_creation_error(&self, error: &str, venv_path: &Path) -> AppError { let error_lower = error.to_lowercase(); if error_lower.contains("permission") || error_lower.contains("权限") { AppError::permission_error( format!("虚拟环境创建权限错误: {error}"), venv_path.parent().unwrap_or(venv_path), ) } else if error_lower.contains("space") || error_lower.contains("空间") || error_lower.contains("disk") { AppError::virtual_environment_path_error( format!("磁盘空间不足导致虚拟环境创建失败: {error}"), venv_path, ) } else if error_lower.contains("exists") || error_lower.contains("存在") { AppError::virtual_environment_path_error( format!("虚拟环境路径冲突: {error}"), venv_path, ) } else if error_lower.contains("path") || error_lower.contains("路径") { AppError::path_error(format!("虚拟环境路径错误: {error}"), venv_path) } else if error_lower.contains("timeout") || error_lower.contains("超时") { AppError::Environment(format!("虚拟环境创建超时: {error}")) } else { AppError::virtual_environment_path_error( format!("虚拟环境创建失败: {error}"), venv_path, ) } } /// 尝试清理损坏的虚拟环境 async fn cleanup_corrupted_venv(&self, venv_path: &Path) -> Result<(), AppError> { if !venv_path.exists() { return Ok(()); } info!( "Try cleaning the corrupted virtual environment: {}", venv_path.display() ); // 检查是否有权限删除 if let Err(e) = self .check_directory_writable(venv_path.parent().unwrap_or(venv_path)) .await { return Err(AppError::permission_error( format!("无权限清理虚拟环境: {e}"), venv_path, )); } // 尝试删除虚拟环境目录 match std::fs::remove_dir_all(venv_path) { Ok(_) => { info!("Successfully cleans corrupted virtual environment"); Ok(()) } Err(e) => Err(AppError::permission_error( format!("清理虚拟环境失败: {e}"), venv_path, )), } } /// 验证当前目录设置(任务12的核心功能) #[instrument(skip(self))] pub async fn validate_current_directory_setup( &self, ) -> Result { let current_dir = Path::new(&self.base_dir); let venv_path = current_dir.join("venv"); info!( "Start verifying current directory settings: {}", current_dir.display() ); let mut result = DirectoryValidationResult { is_valid: true, current_directory: current_dir.to_path_buf(), venv_path: venv_path.clone(), issues: Vec::new(), warnings: Vec::new(), cleanup_options: Vec::new(), recommendations: Vec::new(), }; // 1. 检查当前目录是否可写 if let Err(e) = self.check_directory_writable(current_dir).await { result.is_valid = false; result.issues.push(DirectoryValidationIssue { issue_type: DirectoryIssueType::PermissionDenied, message: format!("当前目录不可写: {e}"), severity: ValidationSeverity::Critical, auto_fixable: false, fix_suggestion: "检查目录权限,确保当前用户有写入权限".to_string(), }); } // 3. 检查虚拟环境路径冲突 if venv_path.exists() { if venv_path.is_file() { result.is_valid = false; result.issues.push(DirectoryValidationIssue { issue_type: DirectoryIssueType::PathConflict, message: "虚拟环境路径被文件占用".to_string(), severity: ValidationSeverity::High, auto_fixable: true, fix_suggestion: "删除冲突的文件".to_string(), }); result.cleanup_options.push(CleanupOption { option_type: CleanupType::RemoveConflictingFile, description: format!("删除冲突文件: {}", venv_path.display()), risk_level: CleanupRisk::Low, command: format!("rm {}", venv_path.display()), }); } else if venv_path.is_dir() { // 检查虚拟环境是否损坏 let python_exe = Self::get_venv_python_path(&venv_path); if !python_exe.exists() { result.warnings.push(DirectoryValidationWarning { warning_type: DirectoryWarningType::CorruptedVenv, message: "检测到损坏的虚拟环境".to_string(), impact: "虚拟环境无法正常使用".to_string(), }); result.cleanup_options.push(CleanupOption { option_type: CleanupType::RemoveCorruptedVenv, description: format!("清理损坏的虚拟环境: {}", venv_path.display()), risk_level: CleanupRisk::Medium, command: format!("rm -rf {}", venv_path.display()), }); } else { // 虚拟环境存在且看起来完整,进行更深入的验证 match self.test_virtual_environment_activation(&venv_path).await { Ok(true) => { result.warnings.push(DirectoryValidationWarning { warning_type: DirectoryWarningType::ExistingVenv, message: "检测到现有的虚拟环境".to_string(), impact: "将使用现有虚拟环境,可能需要更新依赖".to_string(), }); } Ok(false) => { result.warnings.push(DirectoryValidationWarning { warning_type: DirectoryWarningType::CorruptedVenv, message: "现有虚拟环境无法激活".to_string(), impact: "虚拟环境可能已损坏".to_string(), }); result.cleanup_options.push(CleanupOption { option_type: CleanupType::RemoveCorruptedVenv, description: format!( "清理无法激活的虚拟环境: {}", venv_path.display() ), risk_level: CleanupRisk::Medium, command: format!("rm -rf {}", venv_path.display()), }); } Err(e) => { result.warnings.push(DirectoryValidationWarning { warning_type: DirectoryWarningType::CorruptedVenv, message: format!("虚拟环境测试失败: {e}"), impact: "无法确定虚拟环境状态".to_string(), }); } } } } } // 4. 检查路径长度(Windows特有问题) if cfg!(windows) && venv_path.to_string_lossy().len() > 260 { result.is_valid = false; result.issues.push(DirectoryValidationIssue { issue_type: DirectoryIssueType::PathTooLong, message: format!( "虚拟环境路径过长 ({} 字符),Windows限制为260字符", venv_path.to_string_lossy().len() ), severity: ValidationSeverity::High, auto_fixable: false, fix_suggestion: "移动项目到路径较短的目录".to_string(), }); } // 5. 检查特殊字符和编码问题 let path_str = venv_path.to_string_lossy(); if path_str.contains(' ') { result.warnings.push(DirectoryValidationWarning { warning_type: DirectoryWarningType::PathWithSpaces, message: "路径包含空格".to_string(), impact: "某些工具可能无法正确处理包含空格的路径".to_string(), }); } // 6. 生成推荐建议 self.generate_directory_recommendations(&mut result); info!( "Directory verification completed: valid={}, issues={}, warnings={}", result.is_valid, result.issues.len(), result.warnings.len() ); Ok(result) } /// 生成目录验证推荐建议 fn generate_directory_recommendations(&self, result: &mut DirectoryValidationResult) { if result.is_valid && result.warnings.is_empty() { result .recommendations .push("当前目录设置良好,可以安全创建虚拟环境".to_string()); return; } if !result.is_valid { result .recommendations .push("请先解决关键问题后再创建虚拟环境".to_string()); } // 基于问题类型生成具体建议 for issue in &result.issues { match issue.issue_type { DirectoryIssueType::PermissionDenied => { if cfg!(unix) { result .recommendations .push("使用 'chmod 755 .' 修改目录权限".to_string()); result .recommendations .push("使用 'chown $USER .' 修改目录所有者".to_string()); } else if cfg!(windows) { result .recommendations .push("以管理员身份运行命令".to_string()); result .recommendations .push("检查Windows用户账户控制(UAC)设置".to_string()); } } DirectoryIssueType::InsufficientSpace => { result .recommendations .push("清理不需要的文件释放磁盘空间".to_string()); result .recommendations .push("考虑移动项目到有更多可用空间的磁盘".to_string()); } DirectoryIssueType::PathConflict => { result .recommendations .push("删除或重命名冲突的文件/目录".to_string()); } DirectoryIssueType::PathTooLong => { result .recommendations .push("移动项目到路径较短的目录".to_string()); result .recommendations .push("使用较短的目录名称".to_string()); } } } // 基于清理选项生成建议 if !result.cleanup_options.is_empty() { result .recommendations .push("可以使用以下清理选项解决问题:".to_string()); for option in &result.cleanup_options { result.recommendations.push(format!( " - {} (风险: {:?})", option.description, option.risk_level )); } } } /// 执行自动清理选项 pub async fn execute_cleanup_option( &self, option_type: CleanupType, ) -> Result { let venv_path = Path::new(&self.base_dir).join("venv"); match option_type { CleanupType::RemoveConflictingFile => { if venv_path.exists() && venv_path.is_file() { std::fs::remove_file(&venv_path).map_err(|e| { AppError::permission_error(format!("删除冲突文件失败: {e}"), &venv_path) })?; Ok(format!("成功删除冲突文件: {}", venv_path.display())) } else { Err(AppError::path_error( "冲突文件不存在".to_string(), &venv_path, )) } } CleanupType::RemoveCorruptedVenv => { if venv_path.exists() && venv_path.is_dir() { self.cleanup_corrupted_venv(&venv_path).await?; Ok(format!("成功清理损坏的虚拟环境: {}", venv_path.display())) } else { Err(AppError::path_error( "虚拟环境目录不存在".to_string(), &venv_path, )) } } CleanupType::CreateBackup => { if venv_path.exists() { let backup_path = Path::new(&self.base_dir).join("venv.backup"); std::fs::rename(&venv_path, &backup_path).map_err(|e| { AppError::permission_error(format!("创建备份失败: {e}"), &venv_path) })?; Ok(format!("成功备份虚拟环境到: {}", backup_path.display())) } else { Err(AppError::path_error( "虚拟环境不存在,无需备份".to_string(), &venv_path, )) } } } } /// 创建Python虚拟环境(带进度跟踪和增强错误处理) async fn create_python_venv_with_progress(&self) -> Result<(), AppError> { let venv_path = Path::new(&self.base_dir).join("venv"); // 预检查:验证创建条件 self.send_progress("虚拟环境", InstallStage::Preparing, 5.0, "验证创建条件") .await; if let Err(e) = self.validate_venv_creation_preconditions(&venv_path).await { self.send_progress( "虚拟环境", InstallStage::Failed(e.to_string()), 0.0, "前置条件检查失败", ) .await; return Err(e); } // 检查虚拟环境是否已存在 if venv_path.exists() && venv_path.is_dir() { // 验证现有虚拟环境是否完整 let python_exe = Self::get_venv_python_path(&venv_path); if python_exe.exists() { info!( "The Python virtual environment exists and is complete: {}", venv_path.display() ); self.send_progress("虚拟环境", InstallStage::Completed, 100.0, "虚拟环境已存在") .await; return Ok(()); } else { warn!("Corrupted virtual environment detected, attempt to clean"); self.send_progress( "虚拟环境", InstallStage::Preparing, 10.0, "清理损坏的虚拟环境", ) .await; self.cleanup_corrupted_venv(&venv_path).await?; } } info!( "Create a Python virtual environment: {}", venv_path.display() ); self.send_progress( "虚拟环境", InstallStage::Preparing, 15.0, "准备创建虚拟环境", ) .await; // 使用 uv venv venv 在当前目录下创建名为venv的虚拟环境 let create_cmd = Command::new("uv") .arg("venv") .arg("venv") .arg("--python") .arg("python3") .current_dir(&self.base_dir) .output(); self.send_progress("虚拟环境", InstallStage::Installing, 50.0, "创建虚拟环境") .await; let output = timeout(Duration::from_secs(120), create_cmd) .await .map_err(|_| self.handle_venv_creation_error("虚拟环境创建超时", &venv_path))? .map_err(|e| { self.handle_venv_creation_error(&format!("命令执行失败: {e}"), &venv_path) })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); let stdout = String::from_utf8_lossy(&output.stdout); let error_msg = if !stderr.is_empty() { stderr.to_string() } else if !stdout.is_empty() { stdout.to_string() } else { "未知错误".to_string() }; self.send_progress( "虚拟环境", InstallStage::Failed(error_msg.clone()), 0.0, "创建失败", ) .await; let error = self.handle_venv_creation_error(&error_msg, &venv_path); // 记录详细的错误信息和恢复建议 error!("Virtual environment creation failed: {}", error); for suggestion in error.get_path_recovery_suggestions() { error!("Recovery suggestion: {}", suggestion); } return Err(error); } self.send_progress("虚拟环境", InstallStage::Verifying, 90.0, "验证虚拟环境") .await; // 验证虚拟环境创建结果 if let Err(e) = self.verify_venv_creation(&venv_path).await { self.send_progress( "虚拟环境", InstallStage::Failed(e.to_string()), 0.0, "验证失败", ) .await; return Err(e); } self.send_progress( "虚拟环境", InstallStage::Completed, 100.0, "虚拟环境创建完成", ) .await; info!("The Python virtual environment is created"); Ok(()) } /// 验证虚拟环境创建结果 async fn verify_venv_creation(&self, venv_path: &Path) -> Result<(), AppError> { // 检查虚拟环境目录是否存在 if !venv_path.exists() { return Err(AppError::virtual_environment_path_error( "虚拟环境创建后目录不存在".to_string(), venv_path, )); } if !venv_path.is_dir() { return Err(AppError::virtual_environment_path_error( "虚拟环境路径不是目录".to_string(), venv_path, )); } // 检查Python可执行文件 let python_exe = Self::get_venv_python_path(venv_path); if !python_exe.exists() { return Err(AppError::virtual_environment_path_error( "虚拟环境中Python可执行文件不存在".to_string(), &python_exe, )); } // 检查pip是否可用 let pip_exe = Self::get_venv_executable_path(venv_path, "pip"); if !pip_exe.exists() { warn!( "pip does not exist in the virtual environment, but this may be normal (using uv management package)" ); } // 尝试运行Python验证虚拟环境 let test_cmd = Command::new(&python_exe) .arg("-c") .arg("import sys; print(sys.prefix)") .output(); match timeout(Duration::from_secs(10), test_cmd).await { Ok(Ok(output)) if output.status.success() => { let prefix = String::from_utf8_lossy(&output.stdout).trim().to_string(); debug!("Virtual environment Python prefix: {}", prefix); // 验证Python前缀是否指向虚拟环境 if !prefix.contains("venv") { warn!( "Python prefix may not point to virtual environment: {}", prefix ); } } Ok(Ok(output)) => { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AppError::virtual_environment_path_error( format!("虚拟环境Python测试失败: {stderr}"), &python_exe, )); } Ok(Err(e)) => { return Err(AppError::virtual_environment_path_error( format!("无法执行虚拟环境Python: {e}"), &python_exe, )); } Err(_) => { return Err(AppError::virtual_environment_path_error( "虚拟环境Python测试超时".to_string(), &python_exe, )); } } Ok(()) } /// 安装MinerU(带进度跟踪) async fn install_mineru_with_progress(&self) -> Result<(), AppError> { info!("Anso MinerU"); self.send_progress("MinerU", InstallStage::Preparing, 0.0, "准备安装MinerU") .await; // 检测是否在中国大陆,如果是则使用国内镜像 let is_china = self.is_china_region().await; // 检查CUDA环境状态,决定安装哪个版本的MinerU let cuda_status = self.check_cuda_environment().await; let mineru_package = match cuda_status { Ok(cuda_info) if cuda_info.available && !cuda_info.devices.is_empty() => { info!("CUDA environment detected, install mineru[all] to support GPU acceleration"); "mineru[all]" } _ => { info!("CUDA environment not detected, install mineru[core] (CPU version only)"); "mineru[core]" } }; let venv_path = Path::new(&self.base_dir).join("venv"); let python_path = Self::get_venv_python_path(&venv_path); let mut install_cmd = Command::new("uv"); install_cmd .arg("pip") .arg("install") .arg("-U") .arg("--python") .arg(&python_path) .arg(mineru_package); // 如果在中国大陆,添加镜像配置 if is_china { info!("Mainland China environment detected, using Alibaba Cloud mirror source"); install_cmd .arg("-i") .arg("https://mirrors.aliyun.com/pypi/simple/") .arg("--trusted-host") .arg("mirrors.aliyun.com"); } //install_cmd 命令打印 info!("mineru installation command={:?}", &install_cmd); let install_cmd = install_cmd.output(); self.send_progress("MinerU", InstallStage::Downloading, 20.0, "下载MinerU包") .await; let output = timeout(Duration::from_secs(900), install_cmd) .await .map_err(|_| AppError::Environment("MinerU安装超时".to_string()))? .map_err(|e| AppError::Environment(format!("安装MinerU失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); self.send_progress( "MinerU", InstallStage::Failed(stderr.to_string()), 0.0, "安装失败", ) .await; return Err(AppError::Environment(format!("MinerU安装失败: {stderr}"))); } self.send_progress("MinerU", InstallStage::Configuring, 80.0, "配置MinerU环境") .await; // 如果在中国大陆,配置模型源 if is_china { if let Err(e) = self.configure_mineru_model_source().await { warn!("Failed to configure MinerU model source: {}", e); // 不阻断安装流程,只记录警告 } } self.send_progress("MinerU", InstallStage::Verifying, 90.0, "验证MinerU安装") .await; // 验证安装 match self.check_mineru_environment().await { Ok(_) => { self.send_progress("MinerU", InstallStage::Completed, 100.0, "MinerU安装完成") .await; info!("MinerU installation completed"); Ok(()) } Err(e) => { self.send_progress( "MinerU", InstallStage::Failed(e.to_string()), 0.0, "验证失败", ) .await; Err(AppError::Environment(format!("MinerU安装验证失败: {e}"))) } } } /// 安装MarkItDown(带进度跟踪) async fn install_markitdown_with_progress(&self) -> Result<(), AppError> { info!("InstallMarkItDown"); self.send_progress( "MarkItDown", InstallStage::Preparing, 0.0, "准备安装MarkItDown", ) .await; // 检测是否在中国大陆,如果是则使用国内镜像 let is_china = self.is_china_region().await; let venv_path = Path::new(&self.base_dir).join("venv"); let python_path = Self::get_venv_python_path(&venv_path); let mut install_cmd = Command::new("uv"); install_cmd .arg("pip") .arg("install") .arg("--python") .arg(&python_path) .arg("markitdown"); // 如果在中国大陆,添加镜像配置 if is_china { info!("Mainland China environment detected, using domestic mirror source"); install_cmd .arg("-i") .arg("https://mirrors.aliyun.com/pypi/simple/") .arg("--trusted-host") .arg("mirrors.aliyun.com"); } let install_cmd = install_cmd.output(); self.send_progress( "MarkItDown", InstallStage::Downloading, 20.0, "下载MarkItDown包", ) .await; let output = timeout(Duration::from_secs(600), install_cmd) .await .map_err(|_| AppError::Environment("MarkItDown安装超时".to_string()))? .map_err(|e| AppError::Environment(format!("安装MarkItDown失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); self.send_progress( "MarkItDown", InstallStage::Failed(stderr.to_string()), 0.0, "安装失败", ) .await; return Err(AppError::Environment(format!( "MarkItDown安装失败: {stderr}" ))); } self.send_progress( "MarkItDown", InstallStage::Verifying, 90.0, "验证MarkItDown安装", ) .await; // 验证安装 match self.check_markitdown_environment().await { Ok(_) => { self.send_progress( "MarkItDown", InstallStage::Completed, 100.0, "MarkItDown安装完成", ) .await; info!("MarkItDown installation completed"); Ok(()) } Err(e) => { self.send_progress( "MarkItDown", InstallStage::Failed(e.to_string()), 0.0, "验证失败", ) .await; Err(AppError::Environment(format!( "MarkItDown安装验证失败: {e}" ))) } } } /// 检测是否在中国大陆地区 async fn is_china_region(&self) -> bool { // 检查时区 if let Ok(tz) = std::env::var("TZ") { if tz.contains("Asia/Shanghai") || tz.contains("Asia/Beijing") { return true; } } // 检查语言环境 if let Ok(lang) = std::env::var("LANG") { if lang.contains("zh_CN") { return true; } } // 检查系统语言(macOS) if let Ok(output) = Command::new("defaults") .arg("read") .arg("-g") .arg("AppleLanguages") .output() .await { let output_str = String::from_utf8_lossy(&output.stdout); if output_str.contains("zh-Hans") || output_str.contains("zh-CN") { return true; } } // 尝试ping测试(简单的网络检测) if let Ok(output) = Command::new("ping") .arg("-c") .arg("1") .arg("-W") .arg("3000") .arg("baidu.com") .output() .await { if output.status.success() { return true; } } false } /// 配置MinerU模型源为ModelScope(中国大陆) async fn configure_mineru_model_source(&self) -> Result<(), AppError> { info!("Configure MinerU to use the ModelScope model source"); // 创建配置目录 let home_dir = std::env::var("HOME") .map_err(|_| AppError::Environment("无法获取HOME目录".to_string()))?; let config_dir = format!("{home_dir}/.mineru"); if let Err(e) = std::fs::create_dir_all(&config_dir) { warn!("Failed to create MinerU configuration directory: {}", e); } // 创建配置文件内容 let config_content = r#"{ "model_source": "modelscope", "default_source": "modelscope" }"#; let config_file = format!("{config_dir}/config.json"); if let Err(e) = std::fs::write(&config_file, config_content) { return Err(AppError::Environment(format!( "写入MinerU配置文件失败: {e}" ))); } info!( "MinerU model source configuration completed: {}", config_file ); Ok(()) } /// 验证环境完整性 #[instrument(skip(self))] pub async fn validate_environment(&self) -> Result { let status = self.check_environment().await?; let is_valid = status.is_ready(); let health_score = status.health_score(); if !is_valid { warn!( "Environment verification failed, health score: {}/100", health_score ); for issue in status.get_critical_issues() { error!( "Key questions [{}]: {} - {}", issue.component, issue.message, issue.suggestion ); } } else { info!( "Environmental verification passed, health score: {}/100", health_score ); } Ok(is_valid) } /// 自动激活虚拟环境(如果存在且未激活) #[instrument(skip(self))] pub async fn auto_activate_virtual_environment(&self) -> Result<(), AppError> { let venv_path = Path::new(&self.base_dir).join("venv"); // 检查虚拟环境是否存在 if !venv_path.exists() { debug!( "The virtual environment does not exist: {}", venv_path.display() ); return Ok(()); } // 检查虚拟环境是否已经激活 if let Ok(virtual_env) = std::env::var("VIRTUAL_ENV") { if virtual_env == venv_path.to_string_lossy() { debug!( "The virtual environment has been activated: {}", virtual_env ); return Ok(()); } } // 检查Python可执行文件是否存在 let python_exe = Self::get_venv_python_path(&venv_path); if !python_exe.exists() { debug!( "The Python executable file in the virtual environment does not exist: {}", python_exe.display() ); return Ok(()); } // 设置环境变量以模拟虚拟环境激活 info!( "Automatically activate virtual environment: {}", venv_path.display() ); // 计算虚拟环境bin目录路径 let venv_bin_path = if cfg!(windows) { venv_path.join("Scripts").to_string_lossy().to_string() } else { venv_path.join("bin").to_string_lossy().to_string() }; // 设置环境变量 unsafe { std::env::set_var("VIRTUAL_ENV", venv_path.to_string_lossy().to_string()); // 更新PATH环境变量,将虚拟环境的bin目录放在前面 let current_path = std::env::var("PATH").unwrap_or_default(); let new_path = if cfg!(windows) { format!("{venv_bin_path};{current_path}") } else { format!("{venv_bin_path}:{current_path}") }; std::env::set_var("PATH", new_path); // 设置Python相关环境变量 std::env::set_var("PYTHONPATH", venv_path.to_string_lossy().to_string()); } info!( "The virtual environment has been automatically activated, Python path: {}", python_exe.display() ); debug!( "VIRTUAL_ENV: {}", std::env::var("VIRTUAL_ENV").unwrap_or_default() ); debug!("PATH prefix: {}", venv_bin_path); Ok(()) } /// 生成详细环境报告 #[instrument(skip(self))] pub async fn generate_environment_report(&self) -> Result { let status = self.check_environment().await?; let mut report = String::new(); // 标题和概览 report.push_str("=== 环境检查报告 ===\n"); report.push_str(&format!("检查时间: {:?}\n", status.last_checked)); report.push_str(&format!("检查耗时: {:?}\n", status.check_duration)); report.push_str(&format!("健康评分: {}/100\n", status.health_score())); report.push_str(&format!( "环境状态: {}\n\n", if status.is_ready() { "就绪" } else { "未就绪" } )); // 组件状态 report.push_str("=== 组件状态 ===\n"); report.push_str(&format!( "Python: {} ({:?})\n", if status.python_available { "✓" } else { "✗" }, status.python_version.as_deref().unwrap_or("未知") )); if status.virtual_env_active { report.push_str(&format!( " 虚拟环境: ✓ ({:?})\n", status.virtual_env_path.as_deref().unwrap_or("未知路径") )); } report.push_str(&format!( "uv工具: {} ({:?})\n", if status.uv_available { "✓" } else { "✗" }, status.uv_version.as_deref().unwrap_or("未安装") )); report.push_str(&format!( "CUDA: {} ({:?})\n", if status.cuda_available { "✓" } else { "✗" }, status.cuda_version.as_deref().unwrap_or("不可用") )); if !status.cuda_devices.is_empty() { report.push_str(" CUDA设备:\n"); for device in &status.cuda_devices { report.push_str(&format!( " - GPU {}: {} ({}MB 可用)\n", device.id, device.name, device.memory_free / 1024 / 1024 )); } } report.push_str(&format!( "MinerU: {} ({:?})\n", if status.mineru_available { "✓" } else { "✗" }, status.mineru_version.as_deref().unwrap_or("未安装") )); report.push_str(&format!( "MarkItDown: {} ({:?})\n", if status.markitdown_available { "✓" } else { "✗" }, status.markitdown_version.as_deref().unwrap_or("未安装") )); // 问题列表 if !status.issues.is_empty() { report.push_str("\n=== 问题列表 ===\n"); for issue in &status.issues { let severity_icon = match issue.severity { IssueSeverity::Critical => "🔴", IssueSeverity::High => "🟠", IssueSeverity::Medium => "🟡", IssueSeverity::Low => "🔵", }; report.push_str(&format!( "{} [{}] {}: {}\n", severity_icon, issue.component, issue.message, issue.suggestion )); if issue.auto_fixable { report.push_str(" ↳ 可自动修复\n"); } } } // 警告列表 if !status.warnings.is_empty() { report.push_str("\n=== 警告列表 ===\n"); for warning in &status.warnings { report.push_str(&format!( "⚠️ [{}] {}\n", warning.component, warning.message )); report.push_str(&format!(" 影响: {}\n", warning.impact)); } } // 建议 report.push_str("\n=== 建议 ===\n"); if status.is_ready() { report.push_str("✅ 环境配置良好,可以正常使用文档解析服务\n"); } else { let auto_fixable = status.get_auto_fixable_issues(); if !auto_fixable.is_empty() { report.push_str("🔧 可以运行自动修复来解决以下问题:\n"); for issue in auto_fixable { report.push_str(&format!(" - {}: {}\n", issue.component, issue.suggestion)); } } let critical_issues = status.get_critical_issues(); if !critical_issues.is_empty() { report.push_str("❌ 需要手动解决以下关键问题:\n"); for issue in critical_issues { if !issue.auto_fixable { report .push_str(&format!(" - {}: {}\n", issue.component, issue.suggestion)); } } } } Ok(report) } /// 获取环境摘要信息 pub async fn get_environment_summary(&self) -> Result { let status = self.check_environment().await?; Ok(format!( "环境状态: {} | 健康评分: {}/100 | Python: {} | MinerU: {} | MarkItDown: {} | CUDA: {}", if status.is_ready() { "就绪" } else { "未就绪" }, status.health_score(), if status.python_available { "✓" } else { "✗" }, if status.mineru_available { "✓" } else { "✗" }, if status.markitdown_available { "✓" } else { "✗" }, if status.cuda_available { "✓" } else { "✗" } )) } /// 检查当前目录是否适合创建虚拟环境(公共接口) pub async fn check_current_directory_readiness( &self, ) -> Result { self.validate_current_directory_setup().await } /// 获取目录验证报告的格式化字符串 pub async fn get_directory_validation_report(&self) -> Result { let result = self.validate_current_directory_setup().await?; Ok(self.format_directory_validation_report(&result)) } /// 格式化目录验证报告 fn format_directory_validation_report(&self, result: &DirectoryValidationResult) -> String { let mut report = String::new(); report.push_str("=== 当前目录验证报告 ===\n"); report.push_str(&format!("目录: {}\n", result.current_directory.display())); report.push_str(&format!("虚拟环境路径: {}\n", result.venv_path.display())); report.push_str(&format!( "验证状态: {}\n\n", if result.is_valid { "✓ 通过" } else { "✗ 失败" } )); if !result.issues.is_empty() { report.push_str("=== 发现的问题 ===\n"); for (i, issue) in result.issues.iter().enumerate() { report.push_str(&format!( "{}. [{}] {}\n", i + 1, format!("{:?}", issue.severity).to_uppercase(), issue.message )); report.push_str(&format!(" 建议: {}\n", issue.fix_suggestion)); if issue.auto_fixable { report.push_str(" 状态: 可自动修复\n"); } report.push('\n'); } } if !result.warnings.is_empty() { report.push_str("=== 警告信息 ===\n"); for (i, warning) in result.warnings.iter().enumerate() { report.push_str(&format!("{}. {}\n", i + 1, warning.message)); report.push_str(&format!(" 影响: {}\n\n", warning.impact)); } } if !result.cleanup_options.is_empty() { report.push_str("=== 可用的清理选项 ===\n"); for (i, option) in result.cleanup_options.iter().enumerate() { report.push_str(&format!( "{}. {} (风险: {:?})\n", i + 1, option.description, option.risk_level )); report.push_str(&format!(" 命令: {}\n\n", option.command)); } } if !result.recommendations.is_empty() { report.push_str("=== 推荐操作 ===\n"); for (i, recommendation) in result.recommendations.iter().enumerate() { report.push_str(&format!("{}. {}\n", i + 1, recommendation)); } } report } } #[cfg(test)] mod tests { use super::*; use tempfile::TempDir; #[tokio::test] async fn test_environment_manager_creation() { let temp_dir = TempDir::new().unwrap(); let manager = EnvironmentManager::new( "/usr/bin/python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 基本创建测试 assert_eq!(manager.python_path, "/usr/bin/python3"); assert_eq!(manager.retry_config.max_attempts, 3); assert_eq!(manager.cache_ttl, Duration::from_secs(300)); } #[tokio::test] #[ignore = "Depends on global current directory, fails when other tests change it"] async fn test_for_current_directory_factory() { // 测试当前目录工厂方法 let manager = EnvironmentManager::for_current_directory(); assert!(manager.is_ok()); let manager = manager.unwrap(); // 验证路径设置正确 let current_dir = std::env::current_dir().unwrap(); assert_eq!(manager.base_dir, current_dir.to_string_lossy().to_string()); // 验证Python路径根据平台正确设置 let expected_python_path = EnvironmentManager::get_venv_python_path(¤t_dir.join("venv")); assert_eq!( manager.python_path, expected_python_path.to_string_lossy().to_string() ); // 验证默认配置 assert_eq!(manager.retry_config.max_attempts, 3); assert_eq!(manager.cache_ttl, Duration::from_secs(300)); assert!(manager.progress_sender.is_none()); } #[tokio::test] #[ignore = "Depends on global current directory, fails when other tests change it"] async fn test_for_current_directory_with_progress_factory() { let (tx, _rx) = mpsc::unbounded_channel(); // 测试带进度跟踪的当前目录工厂方法 let manager = EnvironmentManager::for_current_directory_with_progress(tx); assert!(manager.is_ok()); let manager = manager.unwrap(); // 验证路径设置正确 let current_dir = std::env::current_dir().unwrap(); assert_eq!(manager.base_dir, current_dir.to_string_lossy().to_string()); // 验证Python路径根据平台正确设置 let expected_python_path = EnvironmentManager::get_venv_python_path(¤t_dir.join("venv")); assert_eq!( manager.python_path, expected_python_path.to_string_lossy().to_string() ); // 验证进度发送器已设置 assert!(manager.progress_sender.is_some()); } #[tokio::test] async fn test_environment_check() { let temp_dir = TempDir::new().unwrap(); let manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 环境检查不应该失败(即使某些工具不可用) let result = manager.check_environment().await; assert!(result.is_ok()); let status = result.unwrap(); assert!(status.health_score() <= 100); } #[tokio::test] async fn test_uv_availability_check() { let temp_dir = TempDir::new().unwrap(); let env_manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 测试UV可用性检查 let result = env_manager.is_uv_available().await; assert!(result.is_ok()); // 检查返回的状态类型 match result.unwrap() { UvAvailabilityStatus::Available { version, compatibility, } => { assert!(!version.is_empty()); assert!(!compatibility.minimum_version.is_empty()); assert!(!compatibility.current_version.is_empty()); } UvAvailabilityStatus::IncompatibleVersion { version, issue } => { assert!(!version.is_empty()); assert!(!issue.is_empty()); } UvAvailabilityStatus::ExecutionFailed { error } => { assert!(!error.is_empty()); } UvAvailabilityStatus::NotInstalled { error } => { assert!(!error.is_empty()); } } } #[tokio::test] async fn test_uv_version_parsing() { let temp_dir = TempDir::new().unwrap(); let env_manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 测试版本解析 assert_eq!( env_manager.extract_uv_version("uv 0.4.15"), Some((0, 4, 15)) ); assert_eq!(env_manager.extract_uv_version("0.4.15"), Some((0, 4, 15))); assert_eq!(env_manager.extract_uv_version("1.0.0"), Some((1, 0, 0))); assert_eq!(env_manager.extract_uv_version("invalid"), None); // 测试版本兼容性检查 let compatibility = env_manager.check_uv_version_compatibility("uv 0.4.15"); assert!(compatibility.is_ok()); let compat = compatibility.unwrap(); assert!(compat.is_compatible); // 0.4.15 >= 0.1.0 assert_eq!(compat.minimum_version, "0.1.0"); assert_eq!(compat.current_version, "uv 0.4.15"); } #[tokio::test] async fn test_uv_installation_method_detection() { let temp_dir = TempDir::new().unwrap(); let env_manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 测试安装方法检测 let method = env_manager.determine_best_uv_installation_method().await; // 确保返回了一个有效的安装方法 match method { UvInstallationMethod::CurlScript | UvInstallationMethod::PowerShellScript | UvInstallationMethod::PipInstall | UvInstallationMethod::SystemPackageManager => { // 所有方法都是有效的 } } } #[tokio::test] async fn test_retry_config() { let temp_dir = TempDir::new().unwrap(); let retry_config = RetryConfig { max_attempts: 5, base_delay: Duration::from_millis(100), max_delay: Duration::from_secs(10), backoff_multiplier: 1.5, }; let manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ) .with_retry_config(retry_config.clone()); assert_eq!(manager.retry_config.max_attempts, 5); assert_eq!(manager.retry_config.backoff_multiplier, 1.5); } #[tokio::test] async fn test_environment_status_health_score() { let mut status = EnvironmentStatus::default(); // 初始状态应该得分很低 assert_eq!(status.health_score(), 0); // 添加基础组件 status.python_available = true; status.mineru_available = true; status.markitdown_available = true; assert_eq!(status.health_score(), 60); // 添加工具支持 status.uv_available = true; status.virtual_env_active = true; assert_eq!(status.health_score(), 80); // 添加CUDA支持 status.cuda_available = true; status.cuda_devices.push(CudaDevice { id: 0, name: "Test GPU".to_string(), memory_total: 8 * 1024 * 1024 * 1024, memory_free: 4 * 1024 * 1024 * 1024, compute_capability: "8.6".to_string(), }); assert_eq!(status.health_score(), 90); } #[tokio::test] async fn test_cross_platform_path_functions() { use std::path::Path; let venv_path = Path::new("test_venv"); // 测试Python路径生成 let python_path = EnvironmentManager::get_venv_python_path(venv_path); if cfg!(windows) { assert_eq!(python_path, venv_path.join("Scripts").join("python.exe")); } else { assert_eq!(python_path, venv_path.join("bin").join("python")); } // 测试可执行文件路径生成 let mineru_path = EnvironmentManager::get_venv_executable_path(venv_path, "mineru"); if cfg!(windows) { assert_eq!(mineru_path, venv_path.join("Scripts").join("mineru.exe")); } else { assert_eq!(mineru_path, venv_path.join("bin").join("mineru")); } // 测试激活脚本路径 let activation_script = EnvironmentManager::get_venv_activation_script(venv_path); if cfg!(windows) { assert_eq!( activation_script, venv_path.join("Scripts").join("activate.bat") ); } else { assert_eq!(activation_script, venv_path.join("bin").join("activate")); } // 测试系统Python可执行文件列表 let python_executables = EnvironmentManager::get_system_python_executable(); assert!(!python_executables.is_empty()); if cfg!(windows) { assert!(python_executables.contains(&"python.exe".to_string())); assert!(python_executables.contains(&"python3.exe".to_string())); } else { assert!(python_executables.contains(&"python3".to_string())); assert!(python_executables.contains(&"python".to_string())); } } #[tokio::test] async fn test_cross_platform_environment_variables() { let temp_dir = tempfile::TempDir::new().unwrap(); let manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); let venv_path = temp_dir.path().join("venv"); let env_vars = manager.get_cross_platform_env_vars(&venv_path); // 验证VIRTUAL_ENV变量 assert_eq!( env_vars.get("VIRTUAL_ENV").unwrap(), &venv_path.to_string_lossy().to_string() ); // 验证PATH变量包含正确的路径 let path_var = env_vars.get("PATH").unwrap(); if cfg!(windows) { assert!(path_var.contains(&venv_path.join("Scripts").to_string_lossy().to_string())); } else { assert!(path_var.contains(&venv_path.join("bin").to_string_lossy().to_string())); } } #[tokio::test] async fn test_virtual_environment_activation_commands() { let status = EnvironmentStatus::default(); // 测试基本激活命令 let activation_cmd = status.get_activation_command(); if cfg!(windows) { assert_eq!(activation_cmd, ".\\venv\\Scripts\\activate.bat"); } else { assert_eq!(activation_cmd, "source ./venv/bin/activate"); } // 测试PowerShell激活命令(仅Windows) let powershell_cmd = status.get_powershell_activation_command(); if cfg!(windows) { assert_eq!( powershell_cmd, Some(".\\venv\\Scripts\\Activate.ps1".to_string()) ); } else { assert_eq!(powershell_cmd, None); } } #[tokio::test] async fn test_virtual_environment_info() { let temp_dir = tempfile::TempDir::new().unwrap(); let manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); let venv_path = temp_dir.path().join("venv"); // 测试虚拟环境信息获取(即使虚拟环境不存在) let venv_info_result = manager.get_virtual_environment_info(&venv_path).await; assert!(venv_info_result.is_ok()); let venv_info = venv_info_result.unwrap(); assert_eq!(venv_info.path, venv_path); assert!(!venv_info.is_valid); // 因为虚拟环境不存在 // 验证平台特定路径 if cfg!(windows) { assert_eq!( venv_info.python_executable, venv_path.join("Scripts").join("python.exe") ); assert_eq!( venv_info.pip_executable, venv_path.join("Scripts").join("pip.exe") ); assert_eq!( venv_info.activation_script, venv_path.join("Scripts").join("activate.bat") ); assert_eq!(venv_info.platform, "windows"); } else { assert_eq!( venv_info.python_executable, venv_path.join("bin").join("python") ); assert_eq!(venv_info.pip_executable, venv_path.join("bin").join("pip")); assert_eq!( venv_info.activation_script, venv_path.join("bin").join("activate") ); assert_eq!(venv_info.platform, "unix"); } } #[tokio::test] async fn test_system_python_detection() { let temp_dir = tempfile::TempDir::new().unwrap(); let manager = EnvironmentManager::new( "python3".to_string(), temp_dir.path().to_string_lossy().to_string(), ); // 测试系统Python查找 let system_python = manager.find_system_python().await; // 注意:这个测试可能在某些环境中失败,如果系统没有安装Python // 但我们至少可以验证函数不会panic if let Some(python_exe) = system_python { assert!(!python_exe.is_empty()); // 验证返回的是我们期望的可执行文件名之一 let expected_names = EnvironmentManager::get_system_python_executable(); assert!(expected_names.contains(&python_exe)); } } }