fester/backend/metrics/fester_intelligence.py

148 lines
3.6 KiB
Python

from metrics.target_scoring import TargetScorer
from metrics.target_labeler import TargetLabeler
from metrics.profile_store import save
class LiveTargetState:
def __init__(self, target_key):
self.target = target_key
# -----------------------------
# RUNNING STATE (incremental)
# -----------------------------
self.cpu_sum = 0
self.temp_sum = 0
self.samples = 0
self.peak_temp = 0
self.actions = 0
self.cache_hits = 0
self.failures = 0
# -----------------------------
# CACHED DERIVED VALUES
# -----------------------------
self.cached_score = None
self.cached_labels = None
self.dirty = True
class FesterIntelligence:
def __init__(self):
self.states = {}
self.scorer = TargetScorer()
self.labeler = TargetLabeler()
# -----------------------------
# INIT
# -----------------------------
def start_target(self, target_key):
self.states[target_key] = LiveTargetState(target_key)
# -----------------------------
# FAST UPDATES (O(1))
# -----------------------------
def sample(self, target_key, node):
s = self.states[target_key]
cpu = node.get("cpu_load", 0)
temp = node.get("temp", 0)
s.cpu_sum += cpu
s.temp_sum += temp
s.samples += 1
if temp > s.peak_temp:
s.peak_temp = temp
s.dirty = True
def record_event(self, target_key, event):
s = self.states[target_key]
if event == "action":
s.actions += 1
elif event == "cache_hit":
s.cache_hits += 1
elif event == "failure":
s.failures += 1
s.dirty = True
# -----------------------------
# FAST SNAPSHOT (NO RECOMPUTE UNLESS DIRTY)
# -----------------------------
def snapshot(self, target_key):
s = self.states.get(target_key)
if not s:
return None
# -----------------------------
# RETURN CACHED RESULT
# -----------------------------
if not s.dirty and s.cached_score is not None:
return {
"score": s.cached_score,
"labels": s.cached_labels
}
# -----------------------------
# COMPUTE AGGREGATES
# -----------------------------
avg_cpu = s.cpu_sum / s.samples if s.samples else 0
avg_temp = s.temp_sum / s.samples if s.samples else 0
cache_ratio = s.cache_hits / s.actions if s.actions else 0
profile = {
"duration": 0, # intentionally not scheduler-relevant anymore
"avg_cpu": avg_cpu,
"avg_temp": avg_temp,
"peak_temp": s.peak_temp,
"cache_ratio": cache_ratio,
"failures": s.failures
}
score = self.scorer.score(profile)
labels = self.labeler.label(score)
# -----------------------------
# CACHE RESULT
# -----------------------------
s.cached_score = score
s.cached_labels = labels
s.dirty = False
return {
"score": score,
"labels": labels
}
# -----------------------------
# FINALIZE (ARCHIVAL ONLY)
# -----------------------------
def finalize(self, target_key):
s = self.states[target_key]
snapshot = self.snapshot(target_key)
save({
"target": target_key,
"score": snapshot["score"],
"labels": snapshot["labels"]
})
return snapshot