mod chat; mod config; mod db; mod error; mod forms; mod handlers; mod jobs; mod providers; mod retrieval; mod routes; mod state; mod tickets; use std::sync::Arc; use config::AppConfig; use providers::help_center::nxtgauge_help_center_provider::NxtgaugeHelpCenterProvider; use providers::llm::ollama_provider::OllamaAiProvider; use providers::tickets::nxtgauge_ticket_provider::NxtgaugeTicketProvider; use retrieval::embeddings::ollama_embedding_provider::OllamaEmbeddingProvider; use state::AppState; use tracing::{info, warn}; #[tokio::main] async fn main() { init_tracing(); let cfg = AppConfig::from_env(); let database = match db::Database::connect(cfg.database_url.as_deref()).await { Ok(db) => db, Err(err) => { warn!("database initialization failed; continuing without DB: {err}"); db::Database::disabled() } }; let ai_provider = Arc::new(OllamaAiProvider::new( cfg.ollama_base_url.clone(), cfg.ollama_chat_model.clone(), )); let embedding_provider = Arc::new(OllamaEmbeddingProvider::new( cfg.ollama_base_url.clone(), cfg.ollama_embed_model.clone(), )); let http_client = reqwest::Client::new(); let help_center_provider = Arc::new(NxtgaugeHelpCenterProvider::new( http_client.clone(), cfg.nxtgauge_users_url.clone(), )); let ticket_provider = Arc::new(NxtgaugeTicketProvider::new( http_client, cfg.nxtgauge_users_url.clone(), cfg.tickets_source.clone(), )); let state = AppState::new( cfg.clone(), database, ai_provider, help_center_provider, ticket_provider, ); let app = routes::build_router(state); let bind_addr = cfg.socket_addr(); info!("starting nxtgauge-ai-assistant on {bind_addr}"); let listener = tokio::net::TcpListener::bind(bind_addr) .await .expect("failed to bind server socket"); axum::serve(listener, app).await.expect("server crashed"); } fn init_tracing() { let default_filter = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()); tracing_subscriber::fmt() .with_env_filter(default_filter) .with_target(false) .compact() .init(); }