use llmosafe::llmosafe_body::ResourceGuard;
use llmosafe::llmosafe_integration::{EscalationPolicy, SafetyContext};
use std::sync::Mutex;
use std::time::{Duration, Instant};
struct ResourceHistory {
measurements: Vec<(Instant, u8)>,
window_secs: u64,
cooldown_secs: u64,
last_check: Option<Instant>,
}
impl ResourceHistory {
fn new(window_secs: u64, cooldown_secs: u64) -> Self {
Self {
measurements: Vec::with_capacity(60),
window_secs,
cooldown_secs,
last_check: None,
}
}
fn record(&mut self, pressure: u8) -> f64 {
let now = Instant::now();
let cutoff = now - Duration::from_secs(self.window_secs);
self.measurements.retain(|(t, _)| *t > cutoff);
self.measurements.push((now, pressure));
if self.measurements.is_empty() {
return pressure as f64;
}
self.measurements
.iter()
.map(|(_, p)| *p as f64)
.sum::<f64>()
/ self.measurements.len() as f64
}
fn rolling_average(&self) -> Option<f64> {
if self.measurements.is_empty() {
return None;
}
Some(
self.measurements
.iter()
.map(|(_, p)| *p as f64)
.sum::<f64>()
/ self.measurements.len() as f64,
)
}
fn is_in_cooldown(&self) -> bool {
if let Some(last) = self.last_check {
last.elapsed() < Duration::from_secs(self.cooldown_secs)
} else {
false
}
}
fn mark_checked(&mut self) {
self.last_check = Some(Instant::now());
}
}
static RESOURCE_HISTORY: Mutex<Option<ResourceHistory>> = Mutex::new(None);
pub struct LlmoSafeGuard {
guard: ResourceGuard,
policy: EscalationPolicy,
}
impl LlmoSafeGuard {
pub fn new() -> Self {
let guard = ResourceGuard::auto(0.8);
Self {
guard,
policy: EscalationPolicy::default(),
}
}
pub fn with_memory_ceiling_bytes(memory_ceiling_bytes: usize) -> Self {
Self {
guard: ResourceGuard::new(memory_ceiling_bytes),
policy: EscalationPolicy::default(),
}
}
pub fn check(&self) -> Result<(), String> {
let mut history = RESOURCE_HISTORY
.lock()
.unwrap_or_else(|e| e.into_inner());
if history.is_none() {
*history = Some(ResourceHistory::new(30, 1)); }
let hist = history.as_mut().unwrap();
if hist.is_in_cooldown() {
if let Some(avg) = hist.rolling_average() {
if avg > 80.0 {
return Err(format!(
"Resource pressure averaging {:.1}% over last 30s (cooldown active)",
avg
));
}
}
return Ok(());
}
let pressure = self.guard.pressure();
let avg = hist.record(pressure);
hist.mark_checked();
if pressure > 80 {
return Err(format!("Resource pressure at {}% (ceiling: 80%)", pressure));
}
if avg > 80.0 {
return Err(format!(
"Rolling average resource pressure at {:.1}% (ceiling: 80%)",
avg
));
}
self.guard
.check()
.map(|_| ())
.map_err(|e| format!("Resource guard check failed: {}", e))
}
pub fn execute<F, T>(&self, f: F) -> Result<T, String>
where
F: FnOnce() -> Result<T, String>,
{
self.check()?;
f()
}
pub fn current_rss_bytes(&self) -> usize {
ResourceGuard::current_rss_bytes()
}
pub fn system_memory_bytes(&self) -> usize {
ResourceGuard::system_memory_bytes()
}
pub fn system_cpu_load(&self) -> u8 {
ResourceGuard::system_cpu_load()
}
pub fn raw_entropy(&self) -> u16 {
self.guard.raw_entropy()
}
pub fn pressure(&self) -> u8 {
self.guard.pressure()
}
pub fn safety_context(&self) -> SafetyContext {
SafetyContext::new(self.policy.clone())
}
}
impl Default for LlmoSafeGuard {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn guard_reports_system_memory() {
let guard = LlmoSafeGuard::new();
let mem = guard.system_memory_bytes();
assert!(mem > 0, "System memory should be > 0");
}
#[test]
fn guard_reports_rss() {
let rss = LlmoSafeGuard::new().current_rss_bytes();
assert!(rss > 0, "RSS should be > 0 for running process");
}
#[test]
fn check_passes_under_normal_load() {
let guard = LlmoSafeGuard::new();
let result = guard.check();
if let Err(e) = result {
eprintln!("System under pressure: {}", e);
}
}
#[test]
fn pressure_is_bounded() {
let guard = LlmoSafeGuard::new();
let p = guard.pressure();
assert!(p <= 100, "Pressure should be 0-100, got {}", p);
}
#[test]
fn entropy_is_bounded() {
let guard = LlmoSafeGuard::new();
let e = guard.raw_entropy();
assert!(e <= 1000, "Entropy should be 0-1000, got {}", e);
}
#[test]
fn test_resource_history_rolling_average() {
let mut hist = ResourceHistory::new(30, 1);
hist.record(50);
hist.record(60);
hist.record(70);
let avg = hist.rolling_average().unwrap();
assert!((avg - 60.0).abs() < 0.1, "Rolling avg should be ~60, got {}", avg);
}
#[test]
fn test_resource_history_cooldown() {
let mut hist = ResourceHistory::new(30, 1);
hist.record(90);
hist.mark_checked();
assert!(hist.is_in_cooldown(), "Should be in cooldown immediately after check");
}
}