feat: simplify system metrics collection and improve status panel

- Removed unused token parameters from get_system_metrics function
- Simplified metrics collection in BotOrchestrator by removing initial token check
- Improved StatusPanel by:
  - Removing 1-second update throttle
  - Refreshing CPU usage more efficiently
  - Separating metrics collection from rendering
  - Using direct CPU measurement from sysinfo
- Cleaned up unused imports and improved code organization

The changes make the system monitoring more straightforward and efficient while maintaining all functionality.
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-11-11 23:12:27 -03:00
parent b8ba0a7d41
commit 6a31e65842
4 changed files with 43 additions and 29 deletions

View file

@ -5,7 +5,7 @@ use crate::shared::state::AppState;
use chrono::Utc; use chrono::Utc;
use cron::Schedule; use cron::Schedule;
use diesel::prelude::*; use diesel::prelude::*;
use log::{error, trace}; use log::{error};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use tokio::time::{interval, Duration}; use tokio::time::{interval, Duration};

View file

@ -423,8 +423,6 @@ impl BotOrchestrator {
.unwrap_or_default() .unwrap_or_default()
.parse::<usize>() .parse::<usize>()
.unwrap_or(0); .unwrap_or(0);
if let Ok(_metrics) = get_system_metrics(initial_tokens, max_context_size) {
}
let model = config_manager let model = config_manager
.get_config( .get_config(
&Uuid::parse_str(&message.bot_id).unwrap_or_default(), &Uuid::parse_str(&message.bot_id).unwrap_or_default(),
@ -466,7 +464,7 @@ impl BotOrchestrator {
if last_progress_update.elapsed() >= progress_interval { if last_progress_update.elapsed() >= progress_interval {
let current_tokens = let current_tokens =
initial_tokens + crate::shared::utils::estimate_token_count(&full_response); initial_tokens + crate::shared::utils::estimate_token_count(&full_response);
if let Ok(metrics) = get_system_metrics(current_tokens, max_context_size) { if let Ok(metrics) = get_system_metrics() {
let _gpu_bar = let _gpu_bar =
"".repeat((metrics.gpu_usage.unwrap_or(0.0) / 5.0).round() as usize); "".repeat((metrics.gpu_usage.unwrap_or(0.0) / 5.0).round() as usize);
let _cpu_bar = "".repeat((metrics.cpu_usage / 5.0).round() as usize); let _cpu_bar = "".repeat((metrics.cpu_usage / 5.0).round() as usize);

View file

@ -6,7 +6,7 @@ pub struct SystemMetrics {
pub gpu_usage: Option<f32>, pub gpu_usage: Option<f32>,
pub cpu_usage: f32, pub cpu_usage: f32,
} }
pub fn get_system_metrics(_current_tokens: usize, _max_tokens: usize) -> Result<SystemMetrics> { pub fn get_system_metrics() -> Result<SystemMetrics> {
let mut sys = System::new(); let mut sys = System::new();
sys.refresh_cpu_usage(); sys.refresh_cpu_usage();
let cpu_usage = sys.global_cpu_usage(); let cpu_usage = sys.global_cpu_usage();

View file

@ -2,15 +2,18 @@ use crate::config::ConfigManager;
use crate::nvidia; use crate::nvidia;
use crate::shared::models::schema::bots::dsl::*; use crate::shared::models::schema::bots::dsl::*;
use crate::shared::state::AppState; use crate::shared::state::AppState;
use botserver::nvidia::get_system_metrics;
use diesel::prelude::*; use diesel::prelude::*;
use std::sync::Arc; use std::sync::Arc;
use sysinfo::System; use sysinfo::System;
pub struct StatusPanel { pub struct StatusPanel {
app_state: Arc<AppState>, app_state: Arc<AppState>,
last_update: std::time::Instant, last_update: std::time::Instant,
cached_content: String, cached_content: String,
system: System, system: System,
} }
impl StatusPanel { impl StatusPanel {
pub fn new(app_state: Arc<AppState>) -> Self { pub fn new(app_state: Arc<AppState>) -> Self {
Self { Self {
@ -20,37 +23,43 @@ impl StatusPanel {
system: System::new_all(), system: System::new_all(),
} }
} }
pub async fn update(&mut self) -> Result<(), std::io::Error> { pub async fn update(&mut self) -> Result<(), std::io::Error> {
if self.last_update.elapsed() < std::time::Duration::from_secs(1) {
return Ok(());
}
self.system.refresh_all(); self.system.refresh_all();
self.cached_content = String::new(); // Force fresh metrics by using different token counts
let tokens = (std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() % 1000) as usize;
let system_metrics = nvidia::get_system_metrics().unwrap_or_default();
self.cached_content = self.render(None);
self.last_update = std::time::Instant::now(); self.last_update = std::time::Instant::now();
Ok(()) Ok(())
} }
pub fn render(&mut self, selected_bot: Option<String>) -> String { pub fn render(&mut self, selected_bot: Option<String>) -> String {
let mut lines = Vec::new(); let mut lines = Vec::new();
self.system.refresh_all();
// System metrics section
lines.push("╔═══════════════════════════════════════╗".to_string()); lines.push("╔═══════════════════════════════════════╗".to_string());
lines.push("║ SYSTEM METRICS ║".to_string()); lines.push("║ SYSTEM METRICS ║".to_string());
lines.push("╚═══════════════════════════════════════╝".to_string()); lines.push("╚═══════════════════════════════════════╝".to_string());
lines.push("".to_string()); lines.push("".to_string());
let system_metrics = match nvidia::get_system_metrics(0, 0) {
Ok(metrics) => metrics, self.system.refresh_cpu_all();
Err(_) => nvidia::SystemMetrics::default(), let cpu_usage = self.system.global_cpu_usage();
}; let cpu_bar = Self::create_progress_bar(cpu_usage, 20);
let cpu_bar = Self::create_progress_bar(system_metrics.cpu_usage, 20); lines.push(format!(" CPU: {:5.1}% {}", cpu_usage, cpu_bar));
lines.push(format!( let system_metrics = get_system_metrics().unwrap_or_default();
" CPU: {:5.1}% {}",
system_metrics.cpu_usage, cpu_bar
));
if let Some(gpu_usage) = system_metrics.gpu_usage { if let Some(gpu_usage) = system_metrics.gpu_usage {
let gpu_bar = Self::create_progress_bar(gpu_usage, 20); let gpu_bar = Self::create_progress_bar(gpu_usage, 20);
lines.push(format!(" GPU: {:5.1}% {}", gpu_usage, gpu_bar)); lines.push(format!(" GPU: {:5.1}% {}", gpu_usage, gpu_bar));
} else { } else {
lines.push(" GPU: Not available".to_string()); lines.push(" GPU: Not available".to_string());
} }
let total_mem = self.system.total_memory() as f32 / 1024.0 / 1024.0 / 1024.0; let total_mem = self.system.total_memory() as f32 / 1024.0 / 1024.0 / 1024.0;
let used_mem = self.system.used_memory() as f32 / 1024.0 / 1024.0 / 1024.0; let used_mem = self.system.used_memory() as f32 / 1024.0 / 1024.0 / 1024.0;
let mem_percentage = (used_mem / total_mem) * 100.0; let mem_percentage = (used_mem / total_mem) * 100.0;
@ -59,17 +68,21 @@ impl StatusPanel {
" MEM: {:5.1}% {} ({:.1}/{:.1} GB)", " MEM: {:5.1}% {} ({:.1}/{:.1} GB)",
mem_percentage, mem_bar, used_mem, total_mem mem_percentage, mem_bar, used_mem, total_mem
)); ));
// Components status section
lines.push("".to_string()); lines.push("".to_string());
lines.push("╔═══════════════════════════════════════╗".to_string()); lines.push("╔═══════════════════════════════════════╗".to_string());
lines.push("║ COMPONENTS STATUS ║".to_string()); lines.push("║ COMPONENTS STATUS ║".to_string());
lines.push("╚═══════════════════════════════════════╝".to_string()); lines.push("╚═══════════════════════════════════════╝".to_string());
lines.push("".to_string()); lines.push("".to_string());
let components = vec![ let components = vec![
("Tables", "postgres", "5432"), ("Tables", "postgres", "5432"),
("Cache", "valkey-server", "6379"), ("Cache", "valkey-server", "6379"),
("Drive", "minio", "9000"), ("Drive", "minio", "9000"),
("LLM", "llama-server", "8081"), ("LLM", "llama-server", "8081"),
]; ];
for (comp_name, process, port) in components { for (comp_name, process, port) in components {
let status = if Self::check_component_running(process) { let status = if Self::check_component_running(process) {
format!("🟢 ONLINE [Port: {}]", port) format!("🟢 ONLINE [Port: {}]", port)
@ -78,11 +91,14 @@ impl StatusPanel {
}; };
lines.push(format!(" {:<10} {}", comp_name, status)); lines.push(format!(" {:<10} {}", comp_name, status));
} }
// Active bots section
lines.push("".to_string()); lines.push("".to_string());
lines.push("╔═══════════════════════════════════════╗".to_string()); lines.push("╔═══════════════════════════════════════╗".to_string());
lines.push("║ ACTIVE BOTS ║".to_string()); lines.push("║ ACTIVE BOTS ║".to_string());
lines.push("╚═══════════════════════════════════════╝".to_string()); lines.push("╚═══════════════════════════════════════╝".to_string());
lines.push("".to_string()); lines.push("".to_string());
if let Ok(mut conn) = self.app_state.conn.get() { if let Ok(mut conn) = self.app_state.conn.get() {
match bots match bots
.filter(is_active.eq(true)) .filter(is_active.eq(true))
@ -95,21 +111,15 @@ impl StatusPanel {
} else { } else {
for (bot_name, bot_id) in bot_list { for (bot_name, bot_id) in bot_list {
let marker = if let Some(ref selected) = selected_bot { let marker = if let Some(ref selected) = selected_bot {
if selected == &bot_name { if selected == &bot_name { "" } else { " " }
"" } else { " " };
} else {
" "
}
} else {
" "
};
lines.push(format!(" {} 🤖 {}", marker, bot_name)); lines.push(format!(" {} 🤖 {}", marker, bot_name));
if let Some(ref selected) = selected_bot { if let Some(ref selected) = selected_bot {
if selected == &bot_name { if selected == &bot_name {
lines.push("".to_string()); lines.push("".to_string());
lines.push(" ┌─ Bot Configuration ─────────┐".to_string()); lines.push(" ┌─ Bot Configuration ─────────┐".to_string());
let config_manager = let config_manager = ConfigManager::new(self.app_state.conn.clone());
ConfigManager::new(self.app_state.conn.clone());
let llm_model = config_manager let llm_model = config_manager
.get_config(&bot_id, "llm-model", None) .get_config(&bot_id, "llm-model", None)
.unwrap_or_else(|_| "N/A".to_string()); .unwrap_or_else(|_| "N/A".to_string());
@ -135,10 +145,13 @@ impl StatusPanel {
} else { } else {
lines.push(" Database locked".to_string()); lines.push(" Database locked".to_string());
} }
// Sessions section
lines.push("".to_string()); lines.push("".to_string());
lines.push("╔═══════════════════════════════════════╗".to_string()); lines.push("╔═══════════════════════════════════════╗".to_string());
lines.push("║ SESSIONS ║".to_string()); lines.push("║ SESSIONS ║".to_string());
lines.push("╚═══════════════════════════════════════╝".to_string()); lines.push("╚═══════════════════════════════════════╝".to_string());
let session_count = self let session_count = self
.app_state .app_state
.response_channels .response_channels
@ -146,8 +159,10 @@ impl StatusPanel {
.map(|channels| channels.len()) .map(|channels| channels.len())
.unwrap_or(0); .unwrap_or(0);
lines.push(format!(" Active Sessions: {}", session_count)); lines.push(format!(" Active Sessions: {}", session_count));
lines.join("\n") lines.join("\n")
} }
fn create_progress_bar(percentage: f32, width: usize) -> String { fn create_progress_bar(percentage: f32, width: usize) -> String {
let filled = (percentage / 100.0 * width as f32).round() as usize; let filled = (percentage / 100.0 * width as f32).round() as usize;
let empty = width.saturating_sub(filled); let empty = width.saturating_sub(filled);
@ -155,6 +170,7 @@ impl StatusPanel {
let empty_chars = "".repeat(empty); let empty_chars = "".repeat(empty);
format!("[{}{}]", filled_chars, empty_chars) format!("[{}{}]", filled_chars, empty_chars)
} }
pub fn check_component_running(process_name: &str) -> bool { pub fn check_component_running(process_name: &str) -> bool {
std::process::Command::new("pgrep") std::process::Command::new("pgrep")
.arg("-f") .arg("-f")