use std::time::{SystemTime, UNIX_EPOCH};
use tracing::info;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PowerState {
ACPower,
Battery,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ThermalState {
Cool,
Warm,
Hot,
Critical,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BatteryHealth {
Good,
Fair,
Poor,
}
#[derive(Clone, Debug)]
pub struct SystemState {
pub power_state: PowerState,
pub temperature_celsius: Option<f32>,
pub thermal_state: ThermalState,
pub battery_percent: Option<f32>,
pub battery_health: BatteryHealth,
pub cpu_load: f32,
pub memory_percent: f32,
pub timestamp: u64,
}
impl SystemState {
pub fn recommended_profile(&self) -> PerformanceProfile {
match (self.power_state, self.thermal_state) {
(PowerState::ACPower, ThermalState::Cool) => PerformanceProfile::Performance,
(PowerState::ACPower, ThermalState::Warm) => PerformanceProfile::Performance,
(PowerState::ACPower, ThermalState::Hot) => PerformanceProfile::Balanced,
(PowerState::ACPower, ThermalState::Critical) => PerformanceProfile::EnergyEfficient,
(PowerState::Battery, ThermalState::Cool) => PerformanceProfile::Balanced,
(PowerState::Battery, ThermalState::Warm) => PerformanceProfile::Balanced,
(PowerState::Battery, ThermalState::Hot) => PerformanceProfile::EnergyEfficient,
(PowerState::Battery, ThermalState::Critical) => PerformanceProfile::PowerSaver,
}
}
pub fn is_critical(&self) -> bool {
self.thermal_state == ThermalState::Critical
|| (self.power_state == PowerState::Battery
&& self.battery_percent.is_some_and(|p| p < 10.0))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PerformanceProfile {
Performance,
Balanced,
EnergyEfficient,
PowerSaver,
}
impl PerformanceProfile {
pub fn batch_size(&self) -> u32 {
match self {
Self::Performance => 256,
Self::Balanced => 128,
Self::EnergyEfficient => 64,
Self::PowerSaver => 32,
}
}
pub fn context_size(&self) -> u32 {
match self {
Self::Performance => 4096,
Self::Balanced => 2048,
Self::EnergyEfficient => 1024,
Self::PowerSaver => 512,
}
}
pub fn gpu_layers(&self) -> u32 {
match self {
Self::Performance => 999, Self::Balanced => 500, Self::EnergyEfficient => 200, Self::PowerSaver => 0, }
}
pub fn temperature_threshold_celsius(&self) -> f32 {
match self {
Self::Performance => 85.0,
Self::Balanced => 75.0,
Self::EnergyEfficient => 65.0,
Self::PowerSaver => 55.0,
}
}
pub fn max_tokens_per_sec(&self) -> u32 {
match self {
Self::Performance => 0, Self::Balanced => 100, Self::EnergyEfficient => 50, Self::PowerSaver => 20, }
}
}
pub struct SystemMonitor {
last_state: Option<SystemState>,
profile_change_count: u32,
}
impl SystemMonitor {
pub fn new() -> Self {
Self {
last_state: None,
profile_change_count: 0,
}
}
pub fn update_state(&mut self) -> SystemState {
let state = self.get_current_state();
if let Some(ref last) = self.last_state {
let old_profile = last.recommended_profile();
let new_profile = state.recommended_profile();
if old_profile != new_profile {
self.profile_change_count += 1;
info!(
"Performance profile changed: {:?} → {:?}",
old_profile, new_profile
);
}
}
self.last_state = Some(state.clone());
state
}
#[cfg(target_os = "macos")]
fn get_current_state(&self) -> SystemState {
use std::process::Command;
let mut state = SystemState {
power_state: PowerState::ACPower,
temperature_celsius: None,
thermal_state: ThermalState::Cool,
battery_percent: None,
battery_health: BatteryHealth::Good,
cpu_load: 0.0,
memory_percent: 0.0,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
};
if let Ok(output) = Command::new("pmset").args(["-g", "batt"]).output() {
let batt_info = String::from_utf8_lossy(&output.stdout);
if batt_info.contains("discharging") || batt_info.contains("Battery") {
state.power_state = PowerState::Battery;
}
if let Some(line) = batt_info.lines().next() {
if let Some(percent_str) = line.split('\t').next() {
if let Some(num_str) = percent_str
.split('%')
.next()
.and_then(|s| s.split_whitespace().last())
{
if let Ok(percent) = num_str.parse::<f32>() {
state.battery_percent = Some(percent);
}
}
}
}
}
#[cfg(target_arch = "aarch64")]
if let Ok(output) = Command::new("sysctl")
.arg("machdep.cpu.brand_string")
.output()
{
state.temperature_celsius = Some(50.0 + (state.cpu_load * 0.3));
}
if let Ok(output) = Command::new("top").args(["-l", "1", "-n", "0"]).output() {
let top_output = String::from_utf8_lossy(&output.stdout);
for line in top_output.lines() {
if line.contains("CPU usage:") {
if let Some(user_part) = line.split("user:").nth(1) {
if let Some(cpu_str) = user_part.split('%').next() {
if let Ok(cpu) = cpu_str.trim().parse::<f32>() {
state.cpu_load = cpu;
}
}
}
}
if line.contains("MemRegions:") {
state.memory_percent = 50.0; }
}
}
if let Some(temp) = state.temperature_celsius {
state.thermal_state = match temp {
t if t < 40.0 => ThermalState::Cool,
t if t < 60.0 => ThermalState::Warm,
t if t < 80.0 => ThermalState::Hot,
_ => ThermalState::Critical,
};
}
state.battery_health = match state.battery_percent {
Some(p) if p > 80.0 => BatteryHealth::Good,
Some(p) if p > 50.0 => BatteryHealth::Fair,
Some(_) => BatteryHealth::Poor,
None => BatteryHealth::Good, };
state
}
#[cfg(not(target_os = "macos"))]
fn get_current_state(&self) -> SystemState {
SystemState {
power_state: PowerState::ACPower,
temperature_celsius: None,
thermal_state: ThermalState::Cool,
battery_percent: None,
battery_health: BatteryHealth::Good,
cpu_load: 0.0,
memory_percent: 0.0,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
}
}
pub fn last_state(&self) -> Option<&SystemState> {
self.last_state.as_ref()
}
pub fn profile_change_count(&self) -> u32 {
self.profile_change_count
}
}
impl Default for SystemMonitor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_thermal_state_ordering() {
assert!(ThermalState::Cool < ThermalState::Warm);
assert!(ThermalState::Warm < ThermalState::Hot);
assert!(ThermalState::Hot < ThermalState::Critical);
}
#[test]
fn test_performance_profile_ac_cool() {
let state = SystemState {
power_state: PowerState::ACPower,
temperature_celsius: Some(35.0),
thermal_state: ThermalState::Cool,
battery_percent: None,
battery_health: BatteryHealth::Good,
cpu_load: 20.0,
memory_percent: 50.0,
timestamp: 0,
};
assert_eq!(state.recommended_profile(), PerformanceProfile::Performance);
assert_eq!(state.recommended_profile().gpu_layers(), 999);
}
#[test]
fn test_performance_profile_battery_hot() {
let state = SystemState {
power_state: PowerState::Battery,
temperature_celsius: Some(75.0),
thermal_state: ThermalState::Hot,
battery_percent: Some(30.0),
battery_health: BatteryHealth::Fair,
cpu_load: 80.0,
memory_percent: 75.0,
timestamp: 0,
};
assert_eq!(
state.recommended_profile(),
PerformanceProfile::EnergyEfficient
);
assert_eq!(state.recommended_profile().gpu_layers(), 200);
}
#[test]
fn test_critical_state_detection() {
let critical_temp = SystemState {
power_state: PowerState::ACPower,
temperature_celsius: Some(85.0),
thermal_state: ThermalState::Critical,
battery_percent: None,
battery_health: BatteryHealth::Good,
cpu_load: 90.0,
memory_percent: 80.0,
timestamp: 0,
};
assert!(critical_temp.is_critical());
let critical_battery = SystemState {
power_state: PowerState::Battery,
temperature_celsius: Some(50.0),
thermal_state: ThermalState::Cool,
battery_percent: Some(5.0),
battery_health: BatteryHealth::Poor,
cpu_load: 20.0,
memory_percent: 40.0,
timestamp: 0,
};
assert!(critical_battery.is_critical());
}
#[test]
fn test_performance_profile_configs() {
let profiles = [
PerformanceProfile::Performance,
PerformanceProfile::Balanced,
PerformanceProfile::EnergyEfficient,
PerformanceProfile::PowerSaver,
];
for profile in &profiles {
assert!(profile.batch_size() > 0);
assert!(profile.context_size() > 0);
assert!(profile.gpu_layers() <= 999);
assert!(profile.temperature_threshold_celsius() > 0.0);
}
assert!(
PerformanceProfile::Performance.batch_size()
> PerformanceProfile::PowerSaver.batch_size()
);
assert!(
PerformanceProfile::Performance.gpu_layers()
> PerformanceProfile::PowerSaver.gpu_layers()
);
}
#[test]
fn test_system_monitor_creation() {
let monitor = SystemMonitor::new();
assert_eq!(monitor.profile_change_count(), 0);
assert!(monitor.last_state().is_none());
}
#[test]
fn test_battery_health_detection() {
assert_eq!(
SystemState {
power_state: PowerState::Battery,
battery_percent: Some(90.0),
battery_health: BatteryHealth::Good,
..Default::default()
}
.battery_health,
BatteryHealth::Good
);
assert_eq!(
SystemState {
power_state: PowerState::Battery,
battery_percent: Some(40.0),
battery_health: BatteryHealth::Poor,
..Default::default()
}
.battery_health,
BatteryHealth::Poor
);
}
}
impl Default for SystemState {
fn default() -> Self {
Self {
power_state: PowerState::ACPower,
temperature_celsius: None,
thermal_state: ThermalState::Cool,
battery_percent: None,
battery_health: BatteryHealth::Good,
cpu_load: 0.0,
memory_percent: 0.0,
timestamp: 0,
}
}
}