diff --git a/crates/ui/src/main.rs b/crates/ui/src/main.rs index 8206f33..24d8c35 100644 --- a/crates/ui/src/main.rs +++ b/crates/ui/src/main.rs @@ -109,8 +109,30 @@ async fn fetch_health(path: &str) -> Result { resp.text().await.map_err(|e| e.to_string()) } +/// Cached schema context — built once, reused across questions. +static SCHEMA_CACHE: std::sync::OnceLock>> = std::sync::OnceLock::new(); + +async fn get_schema_context_cached(datasets: &[Dataset]) -> String { + // Check cache first + { + let cache = SCHEMA_CACHE.get_or_init(|| std::sync::Mutex::new(None)); + if let Ok(guard) = cache.lock() { + if let Some(ref cached) = *guard { + return cached.clone(); + } + } + } + // Build and cache + let ctx = get_schema_context(datasets).await; + if let Some(cache) = SCHEMA_CACHE.get() { + if let Ok(mut guard) = cache.lock() { + *guard = Some(ctx.clone()); + } + } + ctx +} + /// Get schema context for datasets (used for AI SQL generation). -/// Limits to core tables to keep prompt size reasonable. async fn get_schema_context(datasets: &[Dataset]) -> String { let core_tables = ["candidates", "clients", "job_orders", "placements", "timesheets", "call_log", "email_log"]; let mut ctx = String::from("DATABASE SCHEMA:\n\n"); @@ -163,7 +185,7 @@ enum Tab { #[component] fn App() -> Element { - let mut active_tab = use_signal(|| Tab::Dashboard); + let mut active_tab = use_signal(|| Tab::Ask); let mut datasets = use_signal(Vec::::new); let mut ds_loading = use_signal(|| true); @@ -268,7 +290,7 @@ fn AskPanel(datasets: Vec) -> Element { // Step 1: Get schema context step.set("reading schemas...".into()); - let schema_ctx = get_schema_context(&ds).await; + let schema_ctx = get_schema_context_cached(&ds).await; // Step 2: Generate SQL step.set("writing SQL...".into()); @@ -351,7 +373,7 @@ fn AskPanel(datasets: Vec) -> Element { generated_sql.set(None); result.set(None); step.set("reading schemas...".into()); - let schema_ctx = get_schema_context(&ds).await; + let schema_ctx = get_schema_context_cached(&ds).await; step.set("writing SQL...".into()); let prompt = format!( "You are a SQL assistant. You write Apache DataFusion SQL (PostgreSQL-compatible).\n\n\ @@ -673,8 +695,14 @@ fn DashboardPanel() -> Element { }; // Auto-load on mount - // Auto-load on mount - use_effect(move || { do_load(); }); + // Load on first render (user clicks Dashboard tab) + let mut loaded = use_signal(|| false); + use_effect(move || { + if !*loaded.read() { + loaded.set(true); + do_load(); + } + }); rsx! { div { class: "panel",