Newer
Older
TelosDB / src-tauri / src / lib.rs
pub mod db;
pub mod entities;
pub mod llama;
pub mod mcp;

use crate::llama::LlamaClient;
use dotenvy::dotenv;
use sea_orm::DatabaseConnection;
use std::env;
use std::path::PathBuf;
use std::sync::Arc;
use tauri::menu::{Menu, MenuItem};
use tauri::tray::{TrayIconBuilder, TrayIconEvent};
use tauri::Manager;

pub struct AppState {
    pub db: DatabaseConnection,
    pub llama: Arc<LlamaClient>,
}

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .on_window_event(|window, event| {
            if let tauri::WindowEvent::CloseRequested { api, .. } = event {
                api.prevent_close();
                let _ = window.hide();
            }
        })
        .setup(|app| {
            dotenv().ok();

            let app_handle = app.handle().clone();
            tauri::async_runtime::block_on(async move {
                let db_path = "vector.db";
                
                // 拡張機能 (vec0.dll) のパスを動的に解決する
                let exe_dir = env::current_exe().map(|p| p.parent().unwrap().to_path_buf()).unwrap_or_else(|_| env::current_dir().unwrap());
                
                let candidates = [
                    exe_dir.join("vec0.dll"), // 実行ファイルと同階層
                    exe_dir.join("../node_modules/sqlite-vec-windows-x64/vec0.dll"), // Tauri dev (target/debug/..)
                    exe_dir.join("../../node_modules/sqlite-vec-windows-x64/vec0.dll"), // target/debug/../..
                    exe_dir.join("../../../node_modules/sqlite-vec-windows-x64/vec0.dll"), // root
                    PathBuf::from("../node_modules/sqlite-vec-windows-x64/vec0.dll"), // 従来の相対パス
                ];

                let mut extension_path = candidates[0].to_str().unwrap().to_string();
                for cand in &candidates {
                    if cand.exists() {
                        extension_path = cand.to_str().unwrap().to_string();
                        println!("Found sqlite-vec at: {}", extension_path);
                        break;
                    }
                }

                let conn = db::init_db(db_path, &extension_path)
                    .await
                    .expect("Failed to init database");
                let llama_base_url = env::var("LLAMA_CPP_BASE_URL")
                    .unwrap_or_else(|_| "http://localhost:8080".to_string());
                let embedding_model = env::var("LLAMA_CPP_EMBEDDING_MODEL")
                    .unwrap_or_else(|_| "nomic-embed-text".to_string());
                let completion_model =
                    env::var("LLAMA_CPP_MODEL").unwrap_or_else(|_| "mistral".to_string());

                let llama = LlamaClient::new(llama_base_url, embedding_model, completion_model);

                let state = Arc::new(AppState {
                    db: conn,
                    llama: Arc::new(llama),
                });

                app_handle.manage(state.clone());

                // Start MCP Server (SSE)
                let mcp_port = env::var("MCP_PORT")
                    .unwrap_or_else(|_| "3000".to_string())
                    .parse::<u16>()
                    .unwrap_or(3000);

                tokio::spawn(async move {
                    mcp::start_mcp_server(state, mcp_port).await;
                });
            });

            if cfg!(debug_assertions) {
                app.handle().plugin(
                    tauri_plugin_log::Builder::default()
                        .level(log::LevelFilter::Info)
                        .build(),
                )?;
            }

            let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
            let show_i = MenuItem::with_id(app, "show", "Show", true, None::<&str>)?;
            let menu = Menu::with_items(app, &[&show_i, &quit_i])?;

            let _tray = TrayIconBuilder::new()
                .icon(app.default_window_icon().unwrap().clone())
                .menu(&menu)
                .on_menu_event(|app, event| match event.id.as_ref() {
                    "quit" => {
                        app.exit(0);
                    }
                    "show" => {
                        if let Some(window) = app.get_webview_window("main") {
                            let _ = window.show();
                            let _ = window.set_focus();
                        }
                    }
                    _ => {}
                })
                .on_tray_icon_event(|tray, event| {
                    if let TrayIconEvent::DoubleClick { .. } = event {
                        let app = tray.app_handle();
                        if let Some(window) = app.get_webview_window("main") {
                            let _ = window.show();
                            let _ = window.set_focus();
                        }
                    }
                })
                .build(app)?;

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}