Files
xianren_studio/apps/desktop/src/lib.rs
T

157 lines
5.8 KiB
Rust
Raw Normal View History

mod commands;
use std::sync::Arc;
use tokio::sync::RwLock;
use tauri::{Emitter, Manager};
use xianren_core::CoreApp;
use xianren_core::models as models_db;
use xianren_core::settings as settings_db;
use xianren_engine::EngineManager;
use std::path::{Path, PathBuf};
pub struct App {
pub core: Arc<CoreApp>,
pub engine: EngineManager,
pub engine_base: Arc<RwLock<Option<String>>>,
pub server: tokio::sync::Mutex<Option<xianren_api::ApiServer>>,
}
pub fn run() {
let data_dir = dirs::data_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("XianrenStudio");
let logs_dir = data_dir.join("logs");
let _ = std::fs::create_dir_all(&logs_dir);
let file_appender = tracing_appender::rolling::daily(&logs_dir, "app.log");
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "info".into()),
)
.with_writer(non_blocking)
.with_ansi(false)
.init();
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.setup(|app| {
let core = CoreApp::init(None)
.map_err(|e| -> Box<dyn std::error::Error> { e.to_string().into() })?;
tauri::Manager::manage(
app,
App {
core: Arc::new(core),
engine: EngineManager::new(),
engine_base: Arc::new(RwLock::new(None)),
server: tokio::sync::Mutex::new(None),
},
);
// 启动时自动扫描模型目录
let app_state = app.state::<App>();
let db = app_state.core.db.lock().unwrap();
// 校验引擎路径:旧版本可能残留指向无效 exe 的配置(缺 DLL),自动修复
let configured = settings_db::get(&db, "engine_bin").ok().flatten();
let configured_ok = configured
.as_ref()
.map(|p| engine_binary_usable(PathBuf::from(p).as_path()))
.unwrap_or(false);
if !configured_ok {
let mut found: Option<PathBuf> = None;
for dir in ["cpu", "vulkan", "cuda"] {
let candidate = app_state.core.engines_dir.join(dir).join("llama-server.exe");
if engine_binary_usable(&candidate) {
found = Some(candidate);
break;
}
}
if found.is_none() {
let candidate = app_state.core.engines_dir.join("llama-server-cpu.exe");
if engine_binary_usable(&candidate) {
found = Some(candidate);
}
}
if let Some(path) = found {
let _ = settings_db::set(&db, "engine_bin", &path.to_string_lossy());
tracing::info!(path = %path.display(), "engine binary auto-fixed");
} else {
tracing::warn!("no usable llama-server binary found");
}
}
let model_dir = settings_db::get(&db, "model_dir")
.ok()
.flatten()
.map(PathBuf::from)
.unwrap_or_else(|| app_state.core.models_dir.clone());
match models_db::scan_directory(&db, &model_dir) {
Ok(result) => {
tracing::info!(
added = result.added,
updated = result.updated,
missing = result.missing,
"auto scan complete"
);
}
Err(e) => {
tracing::warn!(error = %e, "auto scan failed");
}
}
let _ = app.emit("models://updated", ());
Ok(())
})
.invoke_handler(tauri::generate_handler![
commands::app_info,
commands::list_models,
commands::import_model,
commands::remove_model,
commands::scan_models,
commands::add_remote_model,
commands::search_models,
commands::list_model_files,
commands::settings_get,
commands::settings_set,
commands::list_conversations,
commands::create_conversation,
commands::delete_conversation,
commands::get_messages,
commands::engine_start,
commands::engine_stop,
commands::engine_status,
commands::deploy_model,
commands::chat_send,
commands::regenerate_message,
commands::edit_message,
commands::list_message_versions,
commands::apply_message_version,
commands::report_error,
commands::download_enqueue,
commands::server_start,
commands::server_stop,
commands::server_status,
commands::open_path,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
/// 判断引擎二进制是否可用:文件存在,且同目录包含 DLL(llama.cpp 发布版依赖同目录 DLL)。
fn engine_binary_usable(path: &Path) -> bool {
if !path.is_file() {
return false;
}
let Some(parent) = path.parent() else {
return false;
};
std::fs::read_dir(parent)
.map(|entries| {
entries.flatten().any(|entry| {
entry
.path()
.extension()
.map(|ext| ext.eq_ignore_ascii_case("dll"))
.unwrap_or(false)
})
})
.unwrap_or(false)
}