autonomic-dbre / dbre /elo_system.py
ZeroiJ's picture
Initial commit: Autonomic DBRE - Self-Improving Database Reliability Engineer
b59a07e
import json
import os
import time
from typing import List, Dict, Tuple
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import io
import base64
class ELOSystem:
"""Core ELO rating calculation system."""
def __init__(self, k_factor: int = 32, initial_elo: float = 1000.0):
self.k_factor = k_factor
self.initial_elo = initial_elo
def calculate_expected_score(self, elo_a: float, elo_b: float) -> float:
"""Calculate expected win probability for player A vs player B."""
return 1.0 / (1.0 + 10.0 ** ((elo_b - elo_a) / 400.0))
def update_elo(self, winner_elo: float, loser_elo: float) -> Tuple[float, float]:
"""Update ELO ratings after a match. Returns (new_winner_elo, new_loser_elo)."""
expected_winner = self.calculate_expected_score(winner_elo, loser_elo)
expected_loser = 1.0 - expected_winner
new_winner = winner_elo + self.k_factor * (1.0 - expected_winner)
new_loser = loser_elo + self.k_factor * (0.0 - expected_loser)
return round(new_winner, 2), round(new_loser, 2)
class PlaybookELOTracker:
"""Tracks ELO ratings for playbook versions with persistence."""
def __init__(self, storage_path: str = "./elo_history/"):
self.storage_path = storage_path
self.elo_system = ELOSystem()
self.history: List[Dict] = []
os.makedirs(storage_path, exist_ok=True)
self._load()
def _load(self) -> None:
"""Load ELO history from disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
if os.path.exists(history_file):
with open(history_file, "r") as f:
self.history = json.load(f)
def _save(self) -> None:
"""Save ELO history to disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
with open(history_file, "w") as f:
json.dump(self.history, f, indent=2, default=str)
def register_playbook(self, version: str, elo: float = 1000.0) -> None:
"""Register a new playbook version with initial ELO."""
entry = {
"version": version,
"elo": elo,
"timestamp": time.time(),
"matches_played": 0,
"matches_won": 0
}
self.history.append(entry)
self._save()
def get_current_elo(self, version: str) -> float:
"""Get the current ELO for a playbook version."""
for entry in reversed(self.history):
if entry["version"] == version:
return entry["elo"]
return self.elo_system.initial_elo
def record_matchup(self, version_a: str, version_b: str, winner: str) -> Tuple[float, float]:
"""Record a match between two playbook versions. Returns (new_elo_a, new_elo_b)."""
elo_a = self.get_current_elo(version_a)
elo_b = self.get_current_elo(version_b)
if winner == version_a:
new_a, new_b = self.elo_system.update_elo(elo_a, elo_b)
elif winner == version_b:
new_b, new_a = self.elo_system.update_elo(elo_b, elo_a)
else:
raise ValueError(f"Winner must be one of the two versions: {version_a} or {version_b}")
self._update_entry(version_a, new_a, version_a == winner)
self._update_entry(version_b, new_b, version_b == winner)
return new_a, new_b
def _update_entry(self, version: str, new_elo: float, won: bool) -> None:
"""Update a single entry in history."""
for entry in reversed(self.history):
if entry["version"] == version:
entry["elo"] = new_elo
entry["matches_played"] += 1
if won:
entry["matches_won"] += 1
entry["timestamp"] = time.time()
break
else:
self.register_playbook(version, new_elo)
for entry in reversed(self.history):
if entry["version"] == version:
entry["matches_played"] = 1
if won:
entry["matches_won"] = 1
break
self._save()
def get_elo_history(self) -> List[Dict]:
"""Return full ELO history for plotting."""
return self.history
def get_current_champion(self) -> str:
"""Return the version with the highest ELO."""
if not self.history:
return "none"
return max(self.history, key=lambda x: x["elo"])["version"]
def get_elo_curve_data(self) -> Dict:
"""Return data formatted for plotting: {versions: [], elos: [], timestamps: []}."""
sorted_history = sorted(self.history, key=lambda x: x["timestamp"])
return {
"versions": [h["version"] for h in sorted_history],
"elos": [h["elo"] for h in sorted_history],
"timestamps": [h["timestamp"] for h in sorted_history],
"matches_played": [h["matches_played"] for h in sorted_history],
"matches_won": [h["matches_won"] for h in sorted_history]
}
class ELOSystem:
"""Core ELO rating calculation system."""
def __init__(self, k_factor: int = 32, initial_elo: float = 1000.0):
self.k_factor = k_factor
self.initial_elo = initial_elo
def calculate_expected_score(self, elo_a: float, elo_b: float) -> float:
"""Calculate expected win probability for player A vs player B."""
return 1.0 / (1.0 + 10.0 ** ((elo_b - elo_a) / 400.0))
def update_elo(self, winner_elo: float, loser_elo: float) -> Tuple[float, float]:
"""Update ELO ratings after a match. Returns (new_winner_elo, new_loser_elo)."""
expected_winner = self.calculate_expected_score(winner_elo, loser_elo)
expected_loser = 1.0 - expected_winner
new_winner = winner_elo + self.k_factor * (1.0 - expected_winner)
new_loser = loser_elo + self.k_factor * (0.0 - expected_loser)
return round(new_winner, 2), round(new_loser, 2)
class PlaybookELOTracker:
"""Tracks ELO ratings for playbook versions with persistence."""
def __init__(self, storage_path: str = "./elo_history/"):
self.storage_path = storage_path
self.elo_system = ELOSystem()
self.history: List[Dict] = []
os.makedirs(storage_path, exist_ok=True)
self._load()
def _load(self) -> None:
"""Load ELO history from disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
if os.path.exists(history_file):
with open(history_file, "r") as f:
self.history = json.load(f)
def _save(self) -> None:
"""Save ELO history to disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
with open(history_file, "w") as f:
json.dump(self.history, f, indent=2, default=str)
def register_playbook(self, version: str, elo: float = 1000.0) -> None:
"""Register a new playbook version with initial ELO."""
entry = {
"version": version,
"elo": elo,
"timestamp": time.time(),
"matches_played": 0,
"matches_won": 0
}
self.history.append(entry)
self._save()
def get_current_elo(self, version: str) -> float:
"""Get the current ELO for a playbook version."""
for entry in reversed(self.history):
if entry["version"] == version:
return entry["elo"]
return self.elo_system.initial_elo
def record_matchup(self, version_a: str, version_b: str, winner: str) -> Tuple[float, float]:
"""Record a match between two playbook versions. Returns (new_elo_a, new_elo_b)."""
elo_a = self.get_current_elo(version_a)
elo_b = self.get_current_elo(version_b)
if winner == version_a:
new_a, new_b = self.elo_system.update_elo(elo_a, elo_b)
elif winner == version_b:
new_b, new_a = self.elo_system.update_elo(elo_b, elo_a)
else:
raise ValueError(f"Winner must be one of the two versions: {version_a} or {version_b}")
self._update_entry(version_a, new_a, version_a == winner)
self._update_entry(version_b, new_b, version_b == winner)
return new_a, new_b
def _update_entry(self, version: str, new_elo: float, won: bool) -> None:
"""Update a single entry in history."""
for entry in reversed(self.history):
if entry["version"] == version:
entry["elo"] = new_elo
entry["matches_played"] += 1
if won:
entry["matches_won"] += 1
entry["timestamp"] = time.time()
break
else:
self.register_playbook(version, new_elo)
for entry in reversed(self.history):
if entry["version"] == version:
entry["matches_played"] = 1
if won:
entry["matches_won"] = 1
break
self._save()
def get_elo_history(self) -> List[Dict]:
"""Return full ELO history for plotting."""
return self.history
def get_current_champion(self) -> str:
"""Return the version with the highest ELO."""
if not self.history:
return "none"
return max(self.history, key=lambda x: x["elo"])["version"]
def get_elo_curve_data(self) -> Dict:
"""Return data formatted for plotting: {versions: [], elos: [], timestamps: []}."""
sorted_history = sorted(self.history, key=lambda x: x["timestamp"])
return {
"versions": [h["version"] for h in sorted_history],
"elos": [h["elo"] for h in sorted_history],
"timestamps": [h["timestamp"] for h in sorted_history],
"matches_played": [h["matches_played"] for h in sorted_history],
"matches_won": [h["matches_won"] for h in sorted_history]
}
class ELOSystem:
"""Core ELO rating calculation system."""
def __init__(self, k_factor: int = 32, initial_elo: float = 1000.0):
self.k_factor = k_factor
self.initial_elo = initial_elo
def calculate_expected_score(self, elo_a: float, elo_b: float) -> float:
"""Calculate expected win probability for player A vs player B."""
return 1.0 / (1.0 + 10.0 ** ((elo_b - elo_a) / 400.0))
def update_elo(self, winner_elo: float, loser_elo: float) -> Tuple[float, float]:
"""Update ELO ratings after a match. Returns (new_winner_elo, new_loser_elo)."""
expected_winner = self.calculate_expected_score(winner_elo, loser_elo)
expected_loser = 1.0 - expected_winner
new_winner = winner_elo + self.k_factor * (1.0 - expected_winner)
new_loser = loser_elo + self.k_factor * (0.0 - expected_loser)
return round(new_winner, 2), round(new_loser, 2)
class PlaybookELOTracker:
"""Tracks ELO ratings for playbook versions with persistence."""
def __init__(self, storage_path: str = "./elo_history/"):
self.storage_path = storage_path
self.elo_system = ELOSystem()
self.history: List[Dict] = []
os.makedirs(storage_path, exist_ok=True)
self._load()
def _load(self) -> None:
"""Load ELO history from disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
if os.path.exists(history_file):
with open(history_file, "r") as f:
self.history = json.load(f)
def _save(self) -> None:
"""Save ELO history to disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
with open(history_file, "w") as f:
json.dump(self.history, f, indent=2, default=str)
def register_playbook(self, version: str, elo: float = 1000.0) -> None:
"""Register a new playbook version with initial ELO."""
entry = {
"version": version,
"elo": elo,
"timestamp": time.time(),
"matches_played": 0,
"matches_won": 0
}
self.history.append(entry)
self._save()
def get_current_elo(self, version: str) -> float:
"""Get the current ELO for a playbook version."""
for entry in reversed(self.history):
if entry["version"] == version:
return entry["elo"]
return self.elo_system.initial_elo
def record_matchup(self, version_a: str, version_b: str, winner: str) -> Tuple[float, float]:
"""Record a match between two playbook versions. Returns (new_elo_a, new_elo_b)."""
elo_a = self.get_current_elo(version_a)
elo_b = self.get_current_elo(version_b)
if winner == version_a:
new_a, new_b = self.elo_system.update_elo(elo_a, elo_b)
elif winner == version_b:
new_b, new_a = self.elo_system.update_elo(elo_b, elo_a)
else:
raise ValueError(f"Winner must be one of the two versions: {version_a} or {version_b}")
self._update_entry(version_a, new_a, version_a == winner)
self._update_entry(version_b, new_b, version_b == winner)
return new_a, new_b
def _update_entry(self, version: str, new_elo: float, won: bool) -> None:
"""Update a single entry in history."""
for entry in reversed(self.history):
if entry["version"] == version:
entry["elo"] = new_elo
entry["matches_played"] += 1
if won:
entry["matches_won"] += 1
entry["timestamp"] = time.time()
break
else:
self.register_playbook(version, new_elo)
for entry in reversed(self.history):
if entry["version"] == version:
entry["matches_played"] = 1
if won:
entry["matches_won"] = 1
break
self._save()
def get_elo_history(self) -> List[Dict]:
"""Return full ELO history for plotting."""
return self.history
def get_current_champion(self) -> str:
"""Return the version with the highest ELO."""
if not self.history:
return "none"
return max(self.history, key=lambda x: x["elo"])["version"]
def get_elo_curve_data(self) -> Dict:
"""Return data formatted for plotting: {versions: [], elos: [], timestamps: []}."""
sorted_history = sorted(self.history, key=lambda x: x["timestamp"])
return {
"versions": [h["version"] for h in sorted_history],
"elos": [h["elo"] for h in sorted_history],
"timestamps": [h["timestamp"] for h in sorted_history],
"matches_played": [h["matches_played"] for h in sorted_history],
"matches_won": [h["matches_won"] for h in sorted_history]
}
class ELOSystem:
"""Core ELO rating calculation system."""
def __init__(self, k_factor: int = 32, initial_elo: float = 1000.0):
self.k_factor = k_factor
self.initial_elo = initial_elo
def calculate_expected_score(self, elo_a: float, elo_b: float) -> float:
"""Calculate expected win probability for player A vs player B."""
return 1.0 / (1.0 + 10.0 ** ((elo_b - elo_a) / 400.0))
def update_elo(self, winner_elo: float, loser_elo: float) -> Tuple[float, float]:
"""Update ELO ratings after a match. Returns (new_winner_elo, new_loser_elo)."""
expected_winner = self.calculate_expected_score(winner_elo, loser_elo)
expected_loser = 1.0 - expected_winner
new_winner = winner_elo + self.k_factor * (1.0 - expected_winner)
new_loser = loser_elo + self.k_factor * (0.0 - expected_loser)
return round(new_winner, 2), round(new_loser, 2)
class PlaybookELOTracker:
"""Tracks ELO ratings for playbook versions with persistence."""
def __init__(self, storage_path: str = "./elo_history/"):
self.storage_path = storage_path
self.elo_system = ELOSystem()
self.history: List[Dict] = []
os.makedirs(storage_path, exist_ok=True)
self._load()
def _load(self) -> None:
"""Load ELO history from disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
if os.path.exists(history_file):
with open(history_file, "r") as f:
self.history = json.load(f)
def _save(self) -> None:
"""Save ELO history to disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
with open(history_file, "w") as f:
json.dump(self.history, f, indent=2, default=str)
def register_playbook(self, version: str, elo: float = 1000.0) -> None:
"""Register a new playbook version with initial ELO."""
entry = {
"version": version,
"elo": elo,
"timestamp": time.time(),
"matches_played": 0,
"matches_won": 0
}
self.history.append(entry)
self._save()
def get_current_elo(self, version: str) -> float:
"""Get the current ELO for a playbook version."""
for entry in reversed(self.history):
if entry["version"] == version:
return entry["elo"]
return self.elo_system.initial_elo
def record_matchup(self, version_a: str, version_b: str, winner: str) -> Tuple[float, float]:
"""Record a match between two playbook versions. Returns (new_elo_a, new_elo_b)."""
elo_a = self.get_current_elo(version_a)
elo_b = self.get_current_elo(version_b)
if winner == version_a:
new_a, new_b = self.elo_system.update_elo(elo_a, elo_b)
elif winner == version_b:
new_b, new_a = self.elo_system.update_elo(elo_b, elo_a)
else:
raise ValueError(f"Winner must be one of the two versions: {version_a} or {version_b}")
self._update_entry(version_a, new_a, version_a == winner)
self._update_entry(version_b, new_b, version_b == winner)
return new_a, new_b
def _update_entry(self, version: str, new_elo: float, won: bool) -> None:
"""Update a single entry in history."""
for entry in reversed(self.history):
if entry["version"] == version:
entry["elo"] = new_elo
entry["matches_played"] += 1
if won:
entry["matches_won"] += 1
entry["timestamp"] = time.time()
break
else:
self.register_playbook(version, new_elo)
for entry in reversed(self.history):
if entry["version"] == version:
entry["matches_played"] = 1
if won:
entry["matches_won"] = 1
break
self._save()
def get_elo_history(self) -> List[Dict]:
"""Return full ELO history for plotting."""
return self.history
def get_current_champion(self) -> str:
"""Return the version with the highest ELO."""
if not self.history:
return "none"
return max(self.history, key=lambda x: x["elo"])["version"]
def get_elo_curve_data(self) -> Dict:
"""Return data formatted for plotting: {versions: [], elos: [], timestamps: []}."""
sorted_history = sorted(self.history, key=lambda x: x["timestamp"])
return {
"versions": [h["version"] for h in sorted_history],
"elos": [h["elo"] for h in sorted_history],
"timestamps": [h["timestamp"] for h in sorted_history],
"matches_played": [h["matches_played"] for h in sorted_history],
"matches_won": [h["matches_won"] for h in sorted_history]
}
class ELOSystem:
"""Core ELO rating calculation system."""
def __init__(self, k_factor: int = 32, initial_elo: float = 1000.0):
self.k_factor = k_factor
self.initial_elo = initial_elo
def calculate_expected_score(self, elo_a: float, elo_b: float) -> float:
"""Calculate expected win probability for player A vs player B."""
return 1.0 / (1.0 + 10.0 ** ((elo_b - elo_a) / 400.0))
def update_elo(self, winner_elo: float, loser_elo: float) -> Tuple[float, float]:
"""Update ELO ratings after a match. Returns (new_winner_elo, new_loser_elo)."""
expected_winner = self.calculate_expected_score(winner_elo, loser_elo)
expected_loser = 1.0 - expected_winner
new_winner = winner_elo + self.k_factor * (1.0 - expected_winner)
new_loser = loser_elo + self.k_factor * (0.0 - expected_loser)
return round(new_winner, 2), round(new_loser, 2)
class PlaybookELOTracker:
"""Tracks ELO ratings for playbook versions with persistence."""
def __init__(self, storage_path: str = "./elo_history/"):
self.storage_path = storage_path
self.elo_system = ELOSystem()
self.history: List[Dict] = []
os.makedirs(storage_path, exist_ok=True)
self._load()
def _load(self) -> None:
"""Load ELO history from disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
if os.path.exists(history_file):
with open(history_file, "r") as f:
self.history = json.load(f)
def _save(self) -> None:
"""Save ELO history to disk."""
history_file = os.path.join(self.storage_path, "elo_history.json")
with open(history_file, "w") as f:
json.dump(self.history, f, indent=2, default=str)
def register_playbook(self, version: str, elo: float = 1000.0) -> None:
"""Register a new playbook version with initial ELO."""
entry = {
"version": version,
"elo": elo,
"timestamp": time.time(),
"matches_played": 0,
"matches_won": 0
}
self.history.append(entry)
self._save()
def get_current_elo(self, version: str) -> float:
"""Get the current ELO for a playbook version."""
for entry in reversed(self.history):
if entry["version"] == version:
return entry["elo"]
return self.elo_system.initial_elo
def record_matchup(self, version_a: str, version_b: str, winner: str) -> Tuple[float, float]:
"""Record a match between two playbook versions. Returns (new_elo_a, new_elo_b)."""
elo_a = self.get_current_elo(version_a)
elo_b = self.get_current_elo(version_b)
if winner == version_a:
new_a, new_b = self.elo_system.update_elo(elo_a, elo_b)
elif winner == version_b:
new_b, new_a = self.elo_system.update_elo(elo_b, elo_a)
else:
raise ValueError(f"Winner must be one of the two versions: {version_a} or {version_b}")
self._update_entry(version_a, new_a, version_a == winner)
self._update_entry(version_b, new_b, version_b == winner)
return new_a, new_b
def _update_entry(self, version: str, new_elo: float, won: bool) -> None:
"""Update a single entry in history."""
for entry in reversed(self.history):
if entry["version"] == version:
entry["elo"] = new_elo
entry["matches_played"] += 1
if won:
entry["matches_won"] += 1
entry["timestamp"] = time.time()
break
else:
self.register_playbook(version, new_elo)
for entry in reversed(self.history):
if entry["version"] == version:
entry["matches_played"] = 1
if won:
entry["matches_won"] = 1
break
self._save()
def get_elo_history(self) -> List[Dict]:
"""Return full ELO history for plotting."""
return self.history
def get_current_champion(self) -> str:
"""Return the version with the highest ELO."""
if not self.history:
return "none"
return max(self.history, key=lambda x: x["elo"])["version"]
def get_elo_curve_data(self) -> Dict:
"""Return data formatted for plotting: {versions: [], elos: [], timestamps: []}."""
sorted_history = sorted(self.history, key=lambda x: x["timestamp"])
return {
"versions": [h["version"] for h in sorted_history],
"elos": [h["elo"] for h in sorted_history],
"timestamps": [h["timestamp"] for h in sorted_history],
"matches_played": [h["matches_played"] for h in sorted_history],
"matches_won": [h["matches_won"] for h in sorted_history]
}
def plot_elo_curve(history: List[Dict]) -> str:
"""Generate a dark-themed ELO curve plot and return as base64 PNG string.
Args:
history: List of ELO history entries with version, elo, timestamp keys
Returns:
Base64 encoded PNG image string
"""
matplotlib.use('Agg')
plt.style.use('dark_background')
sorted_history = sorted(history, key=lambda x: x.get("timestamp", 0))
versions = [h.get("version", "") for h in sorted_history]
elos = [h.get("elo", 1000) for h in sorted_history]
fig, ax = plt.subplots(figsize=(12, 6), facecolor='#1a1a2e')
ax.set_facecolor('#1a1a2e')
ax.plot(range(len(elos)), elos, color='#00ff88', linewidth=2, marker='o',
markersize=8, markerfacecolor='#00ff88', markeredgecolor='white', markeredgewidth=1)
for i, (v, e) in enumerate(zip(versions, elos)):
ax.annotate(v, (i, e), textcoords="offset points", xytext=(0, 15),
ha='center', fontsize=9, color='white', fontweight='bold')
ax.set_xlabel('Match Number', color='white', fontsize=12, fontweight='bold')
ax.set_ylabel('ELO Rating', color='white', fontsize=12, fontweight='bold')
ax.set_title('🧬 Playbook ELO Evolution', color='#00ff88', fontsize=14, fontweight='bold')
ax.tick_params(axis='x', colors='white', labelsize=10)
ax.tick_params(axis='y', colors='white', labelsize=10)
ax.grid(True, alpha=0.3, color='gray', linestyle='--')
ax.spines['bottom'].set_color('white')
ax.spines['top'].set_color('white')
ax.spines['right'].set_color('white')
ax.spines['left'].set_color('white')
plt.tight_layout()
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=100, bbox_inches='tight', facecolor='#1a1a2e')
plt.close()
buf.seek(0)
return base64.b64encode(buf.read()).decode('utf-8')