use std::sync::Arc;
use std::time::Duration;
use kimun_core::NoteVault;
use kimun_server_client::{
RagClient,
sync::{RagSync, ServerCapability},
};
use tokio::task::JoinHandle;
use super::RagStatus;
use super::client::server_config;
use crate::components::events::{AppEvent, AppTx};
use crate::settings::SharedSettings;
const SYNC_INTERVAL: Duration = Duration::from_secs(10);
const RECONCILE_EVERY_N_TICKS: u32 = 30;
pub fn spawn_rag_sync(
vault: Arc<NoteVault>,
settings: &SharedSettings,
tx: AppTx,
) -> Option<JoinHandle<()>> {
let (url, token) = server_config(settings)?;
Some(tokio::spawn(async move {
let mut interval = tokio::time::interval(SYNC_INTERVAL);
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut sync: Option<RagSync> = None;
let mut ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
let mut auth_failed = false;
loop {
interval.tick().await;
if sync.is_none() {
match vault.vault_id().await {
Ok(id) => {
let client = RagClient::new(url.clone(), token.clone(), id.to_string());
sync = Some(RagSync::new(vault.clone(), client));
}
Err(e) => {
log::warn!("RAG: cannot read vault id (will retry): {e}");
let _ = tx.send(AppEvent::RagStatus(RagStatus::Offline));
continue;
}
}
}
let sync = sync.as_ref().expect("sync established above");
let probe = match sync.probe().await {
Some(p) => p,
None => {
let _ = tx.send(AppEvent::RagStatus(RagStatus::Offline));
ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
auth_failed = false;
continue;
}
};
if probe.capability == ServerCapability::Unconfigured {
let _ = tx.send(AppEvent::RagStatus(RagStatus::NotConfigured));
ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
continue;
}
if probe.auth_required && token.is_none() {
let _ = tx.send(AppEvent::RagStatus(RagStatus::Unauthorized));
ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
continue;
}
let llm_available = probe.capability.llm_available();
if !auth_failed {
let _ = tx.send(AppEvent::RagStatus(RagStatus::Syncing { llm_available }));
}
if !sync.index_ready() {
ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
continue;
}
let result = if ticks_since_reconcile >= RECONCILE_EVERY_N_TICKS {
ticks_since_reconcile = 0;
sync.tick().await } else {
ticks_since_reconcile += 1;
sync.drain().await };
let status = match result {
Ok(true) => {
auth_failed = false;
RagStatus::Online { llm_available }
}
Ok(false) => {
ticks_since_reconcile = RECONCILE_EVERY_N_TICKS;
auth_failed = false;
RagStatus::Syncing { llm_available }
}
Err(e) if e.is_auth() => {
log::warn!("RAG server rejected the configured token: {e}");
auth_failed = true;
RagStatus::Unauthorized
}
Err(e) => {
log::debug!("RAG sync failed: {e}");
auth_failed = false;
RagStatus::Offline
}
};
let _ = tx.send(AppEvent::RagStatus(status));
}
}))
}