cloudcost1 / app.py
Arghadee's picture
Update app.py
1fd4b60 verified
Raw
History Blame Contribute Delete
27 kB
import gradio as gr
import random
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
from datetime import datetime
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn
from environment import CloudCostEnv
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# FASTAPI โ€” Checker endpoints (DO NOT REMOVE)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
fapp = FastAPI()
_env = CloudCostEnv()
class StepRequest(BaseModel):
action: int
@fapp.post("/reset")
async def api_reset():
state = await _env.reset()
return state
@fapp.post("/step")
async def api_step(req: StepRequest):
state, reward, done = await _env.step(req.action)
return {"state": state, "reward": reward, "done": done}
@fapp.get("/state")
async def api_state():
return await _env.state()
@fapp.get("/health")
async def health():
return {"status": "ok"}
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# COLOR SYSTEM
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
C = {
"bg": "#06091a",
"panel": "#0b1120",
"panel2": "#0f172a",
"border": "#1e3a5f",
"cyan": "#00e5ff",
"green": "#00ff9d",
"amber": "#ffb300",
"red": "#ff3d5a",
"purple": "#c084fc",
"blue": "#60a5fa",
"muted": "#334d6e",
"text": "#e2eeff",
"dim": "#5a7a9a",
}
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# CHART HELPERS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def style_ax(ax, fig, title=""):
fig.patch.set_facecolor(C["panel"])
ax.set_facecolor(C["panel"])
ax.tick_params(colors=C["dim"], labelsize=9)
ax.xaxis.label.set_color(C["dim"])
ax.yaxis.label.set_color(C["dim"])
for sp in ax.spines.values():
sp.set_edgecolor(C["border"])
ax.grid(True, color=C["border"], lw=0.5, linestyle="--", alpha=0.5)
if title:
ax.set_title(title, color=C["text"], fontsize=11, fontweight="bold", pad=10)
def _leg(ax):
ax.legend(fontsize=8, facecolor=C["panel2"], labelcolor=C["text"],
edgecolor=C["border"], loc="upper right", framealpha=0.9)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# CHARTS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def make_gauge_chart(value, label, color, seed):
fig, ax = plt.subplots(figsize=(3.8, 3.8), subplot_kw=dict(polar=True))
fig.patch.set_facecolor(C["panel"])
ax.set_facecolor(C["panel"])
pct = max(0, min(value, 100)) / 100
theta = np.linspace(np.pi * 1.25, np.pi * 1.25 - 2.5 * np.pi * pct, 300)
r_in, r_out = 0.60, 0.90
t_bg = np.linspace(np.pi * 1.25, np.pi * 1.25 - 2.5 * np.pi, 300)
ax.fill_between(t_bg, r_in, r_out, color=C["muted"], alpha=0.25, zorder=1)
ax.fill_between(theta, r_in, r_out, color=color, alpha=0.92, zorder=2)
if len(theta) > 0:
ax.scatter([theta[-1]], [(r_in + r_out) / 2], s=80, color="white", zorder=5, alpha=0.9)
np.random.seed(seed % 40)
for t in t_bg[::30]:
ax.plot([t, t], [r_out + 0.02, r_out + 0.07], color=C["dim"], lw=1.0, alpha=0.6)
ax.text(0, 0, f"{int(value)}%", ha="center", va="center",
fontsize=22, fontweight="bold", color=color, transform=ax.transData)
ax.text(0, -0.28, label, ha="center", va="center",
fontsize=10, color=C["dim"], transform=ax.transData)
ax.set_ylim(0, 1)
ax.axis("off")
plt.tight_layout(pad=0.3)
return fig
def make_radial_burst(value, label, color, seed):
np.random.seed(seed % 40 + 5)
n = 180
ang = np.linspace(0, 2 * np.pi, n, endpoint=False)
r = 0.3 + 0.6 * np.abs(np.random.normal(0, max(value, 1) / 120, n))
r = np.clip(r, 0, 1)
fig, ax = plt.subplots(figsize=(3.8, 3.8), subplot_kw=dict(polar=True))
fig.patch.set_facecolor(C["panel"])
ax.set_facecolor(C["panel"])
for i in range(n):
ax.plot([ang[i], ang[i]], [0, r[i]], color=color, lw=1.0, alpha=0.3 + 0.7 * r[i])
ax.scatter(ang, r, s=4, color=color, alpha=0.7, zorder=3)
theta_fill = np.linspace(0, 2 * np.pi, 300)
ax.fill(theta_fill, [0.28] * 300, color=C["panel2"], zorder=2)
ax.fill(theta_fill, [0.27] * 300, color=color, alpha=0.15, zorder=2)
ax.text(0, 0, str(value), ha="center", va="center",
fontsize=18, fontweight="bold", color=color, transform=ax.transData)
ax.text(0, -0.5, label, ha="center", va="center",
fontsize=9, color=C["dim"], transform=ax.transData)
ax.set_ylim(0, 1.1)
ax.axis("off")
plt.tight_layout(pad=0.3)
return fig
def make_area_chart(user_load, seed):
np.random.seed(seed % 60)
t = np.arange(80)
base = user_load * 8
y = base + 160 * np.sin(t / 10) + np.random.normal(0, 25, 80)
y = np.clip(y, 0, 1000)
spk = np.where(y > np.percentile(y, 88))[0]
fig, ax = plt.subplots(figsize=(5.8, 3.0))
style_ax(ax, fig, "Traffic Load โ€” Area View")
ax.fill_between(t, y, alpha=0.35, color=C["cyan"])
ax.plot(t, y, color=C["cyan"], lw=1.8)
ax.scatter(spk, y[spk], color=C["red"], s=28, zorder=5, label="Load spike")
ax.set_xlabel("Time steps", fontsize=9)
ax.set_ylabel("req/s", fontsize=9)
_leg(ax)
plt.tight_layout(pad=1.0)
return fig
def make_neon_bar(auto_scaling, seed):
np.random.seed(seed % 60 + 1)
cats = ["t0","t1","t2","t3","t4","t5","t6","t7","t8","t9","t10","t11"]
vals = np.random.uniform(30, 100, 12)
if auto_scaling:
colors = [C["green"] if v > 70 else C["amber"] if v > 45 else C["red"] for v in vals]
else:
colors = [C["red"] if v > 60 else C["muted"] for v in vals]
fig, ax = plt.subplots(figsize=(5.8, 3.0))
style_ax(ax, fig, "Server Utilisation โ€” Neon Bars")
bars = ax.bar(cats, vals, color=colors, width=0.65, edgecolor=C["bg"], linewidth=0.4)
for bar, v in zip(bars, vals):
ax.text(bar.get_x() + bar.get_width()/2, v + 1.5, f"{int(v)}",
ha="center", color=C["dim"], fontsize=7)
ax.set_ylabel("Utilisation %", fontsize=9)
ax.set_ylim(0, 115)
p1 = mpatches.Patch(color=C["green"], label="Optimal")
p2 = mpatches.Patch(color=C["amber"], label="Warning")
p3 = mpatches.Patch(color=C["red"], label="Critical")
ax.legend(handles=[p1,p2,p3], fontsize=8, facecolor=C["panel2"],
labelcolor=C["text"], edgecolor=C["border"])
plt.tight_layout(pad=1.0)
return fig
def make_waveform_cost(budget_on, spot_avail, seed):
np.random.seed(seed % 60 + 2)
t = np.arange(120)
burn = 0.80 * np.exp(-t/40) + 0.08
burn += 0.04 * np.sin(t/6) + np.random.normal(0, 0.015, 120)
if budget_on:
burn = np.clip(burn, None, 0.55)
if spot_avail > 50:
burn *= 0.72
cap = 0.50
fig, ax = plt.subplots(figsize=(5.8, 3.0))
style_ax(ax, fig, "Cost Burn โ€” Waveform")
ax.plot(t, burn, color=C["purple"], lw=1.8)
ax.fill_between(t, burn, alpha=0.20, color=C["purple"])
ax.axhline(cap, color=C["amber"], lw=1.2, ls="--", label=f"Budget ${cap}/hr")
ax.fill_between(t, burn, cap, where=(burn > cap), color=C["red"], alpha=0.22, label="Over budget")
ax.set_xlabel("Time steps", fontsize=9)
ax.set_ylabel("$/hr", fontsize=9)
_leg(ax)
plt.tight_layout(pad=1.0)
return fig
def make_latency_wave(chaos_monkey, seed):
np.random.seed(seed % 60 + 3)
t = np.arange(120)
lat = 35 + 20*np.sin(t/12) + np.random.normal(0, 5, 120)
spk = []
if chaos_monkey:
spk = np.random.choice(120, size=8, replace=False)
lat[spk] += np.random.uniform(80, 170, 8)
lat = np.clip(lat, 0, 240)
sla = 100
fig, ax = plt.subplots(figsize=(5.8, 3.0))
style_ax(ax, fig, "Latency Waveform (99th pct SLA)")
ax.plot(t, lat, color=C["blue"], lw=1.8)
ax.fill_between(t, lat, alpha=0.18, color=C["blue"])
ax.axhline(sla, color=C["red"], lw=1.0, ls=":", label=f"SLA {sla} ms")
ax.fill_between(t, lat, sla, where=(lat > sla), color=C["red"], alpha=0.22, label="SLA breach")
if len(spk):
ax.scatter(spk, lat[spk], color=C["red"], s=35, marker="x", lw=2, zorder=5, label="Chaos spike")
ax.set_xlabel("Time steps", fontsize=9)
ax.set_ylabel("Latency (ms)", fontsize=9)
_leg(ax)
plt.tight_layout(pad=1.0)
return fig
def make_reward_curve(auto_scaling, seed):
np.random.seed(seed % 60 + 4)
eps = np.arange(1, 201)
if auto_scaling:
r = 950*(1 - np.exp(-eps/55)) + np.random.normal(0, 18, 200)
else:
r = 380*(1 - np.exp(-eps/90)) + np.random.normal(0, 12, 200)
rc = np.cumsum(np.clip(r/180, 0, None))
col = C["green"] if auto_scaling else C["muted"]
fig, ax = plt.subplots(figsize=(5.8, 3.0))
style_ax(ax, fig, "Cumulative RL Reward")
ax.plot(eps, rc, color=col, lw=2.0)
ax.fill_between(eps, rc, alpha=0.15, color=col)
lbl = "RL Agent (ON)" if auto_scaling else "Static (no RL)"
ax.legend([lbl], fontsize=8, facecolor=C["panel2"], labelcolor=C["text"], edgecolor=C["border"])
ax.set_xlabel("Episodes", fontsize=9)
ax.set_ylabel("Cumulative reward", fontsize=9)
plt.tight_layout(pad=1.0)
return fig
def make_before_after(auto_scaling):
cats = ["Static\n(Before RL)", "RL Agent\n(After)"]
spend = [1250, 680 if auto_scaling else 1050]
dtime = [35, 8 if auto_scaling else 22]
colors = [C["muted"], C["green"] if auto_scaling else C["amber"]]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(5.8, 3.0))
for ax in (ax1, ax2):
style_ax(ax, fig)
b1 = ax1.bar(cats, spend, color=colors, width=0.5, edgecolor=C["bg"], linewidth=0.5)
ax1.set_title("Total Spend ($)", color=C["text"], fontsize=10, fontweight="bold")
ax1.set_ylabel("USD", fontsize=9)
for bar, v in zip(b1, spend):
ax1.text(bar.get_x()+bar.get_width()/2, v+18, f"${v}",
ha="center", color=C["text"], fontsize=9, fontweight="bold")
b2 = ax2.bar(cats, dtime, color=colors, width=0.5, edgecolor=C["bg"], linewidth=0.5)
ax2.set_title("Downtime Risk (%)", color=C["text"], fontsize=10, fontweight="bold")
ax2.set_ylabel("Risk %", fontsize=9)
for bar, v in zip(b2, dtime):
ax2.text(bar.get_x()+bar.get_width()/2, v+0.4, f"{v}%",
ha="center", color=C["text"], fontsize=9, fontweight="bold")
saved = round((1 - spend[1]/1250)*100)
fig.suptitle(f"RL saved {saved}% cost | Risk reduced by {dtime[0]-dtime[1]}%",
color=C["green"] if auto_scaling else C["amber"], fontsize=10, y=1.04)
plt.tight_layout(pad=1.0)
return fig
def make_whatif(user_load, spot_avail):
loads = np.arange(10, 101, 10)
cost_od = 0.085 * loads / 10
cost_spot = cost_od * (1 - spot_avail/220)
lat_base = 18 + loads * 1.15
lat_rl = lat_base * 0.82
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(5.8, 3.0))
for ax in (ax1, ax2):
style_ax(ax, fig)
ax1.plot(loads, cost_od, color=C["red"], lw=2.0, label="On-demand")
ax1.plot(loads, cost_spot, color=C["green"], lw=2.0, ls="--", label="With spot")
ax1.axvline(user_load, color=C["amber"], lw=1.2, ls=":", label=f"Now {int(user_load)}%")
ax1.set_title("Predicted Cost", color=C["text"], fontsize=10, fontweight="bold")
ax1.set_xlabel("Load (%)", fontsize=9)
ax1.set_ylabel("$/hr", fontsize=9)
_leg(ax1)
ax2.plot(loads, lat_base, color=C["amber"], lw=2.0, label="No RL")
ax2.plot(loads, lat_rl, color=C["cyan"], lw=2.0, ls="--", label="With RL")
ax2.axhline(100, color=C["red"], lw=1.0, ls=":", label="SLA 100ms")
ax2.axvline(user_load, color=C["amber"], lw=1.2, ls=":")
ax2.set_title("Predicted Latency", color=C["text"], fontsize=10, fontweight="bold")
ax2.set_xlabel("Load (%)", fontsize=9)
ax2.set_ylabel("ms", fontsize=9)
_leg(ax2)
plt.tight_layout(pad=1.0)
return fig
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# AI DECISION ENGINE
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
DECISIONS = {
"scale_up": ("Scale Up +2 servers", "High latency spike detected", C["red"]),
"scale_down": ("Scale Down -1 server", "Load dropping โ€” saving cost", C["green"]),
"spot_switch": ("Switch to Spot Instances","Budget threshold approaching", C["amber"]),
"hold": ("Hold โ€” no change needed", "System within normal thresholds",C["cyan"]),
"chaos_resp": ("Emergency Scale Up +4", "Chaos event detected!", C["red"]),
}
XAI = {
"scale_up": ["Latency > 80 ms threshold", "Load trend +12% rising", "Budget still safe"],
"scale_down": ["Load < 35%", "Servers under-utilised", "Cost optimisation trigger"],
"spot_switch": ["Burn rate > $0.45/hr", "Spot availability > 60%", "Budget constraint active"],
"hold": ["All metrics nominal", "No threshold breached", "Agent confidence stable"],
"chaos_resp": ["Multiple server failures", "Latency > 150 ms", "Load spike > 200%"],
}
def get_decision(user_load, spot_avail, chaos_monkey, budget_on, latency):
if chaos_monkey and latency > 120: key = "chaos_resp"
elif user_load > 80 and latency > 80: key = "scale_up"
elif user_load < 35: key = "scale_down"
elif budget_on and spot_avail > 50: key = "spot_switch"
else: key = "hold"
action, reason, color = DECISIONS[key]
conf = random.randint(85, 99) if key != "hold" else random.randint(70, 88)
return key, action, reason, conf, XAI[key]
def health_badge(latency, load, drisk):
if latency > 120 or load > 850 or drisk == "High":
return "๐Ÿ”ด CRITICAL โ€” System Under Stress", "red"
elif latency > 70 or load > 600 or drisk == "Medium":
return "๐ŸŸก WARNING โ€” Elevated Load", "amber"
return "๐ŸŸข HEALTHY โ€” All Systems Nominal", "green"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# MAIN SIMULATION
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
_tick = 0
def run_simulation(scenario, user_load, spot_avail,
chaos_monkey, budget_on, auto_scaling):
global _tick
_tick += 1
seed = _tick
t = datetime.now().strftime("%H:%M:%S")
load = int(user_load * 9.5)
servers = random.randint(8, 20)
burn = round(random.uniform(0.18, 0.44 if budget_on else 0.82), 2)
latency = random.randint(22, 70 if not chaos_monkey else 165)
score = round(random.uniform(0.83, 1.0) if auto_scaling
else random.uniform(0.50, 0.77), 2)
spend_str = "$680" if auto_scaling else "$1,250"
savings = f"{round((1-680/1250)*100)}%" if auto_scaling else "0%"
drisk = ("Low" if latency < 60 and load < 600
else "Medium" if latency < 110
else "High")
key, action, reason, conf, xai = get_decision(
user_load, spot_avail, chaos_monkey, budget_on, latency)
health_txt, _ = health_badge(latency, load, drisk)
decision_md = f"""### ๐Ÿง  AI Decision\n**โ†’ {action}**\n\n| | |\n|---|---|\n| Confidence | `{conf}%` |\n| Reason | {reason} |\n| Scenario | {scenario.split(':')[0]} |\n| Time | `{t}` |\n"""
xai_md = "### ๐Ÿ’ก Why did AI decide this?\n" + "".join(f"- โœ” {r}\n" for r in xai)
health_md = f"""### {health_txt}\n\n| Metric | Value |\n|---|---|\n| Latency | `{latency} ms` |\n| Load | `{load} req/s` |\n| Downtime risk | `{drisk}` |\n| Agent score | `{score} / 1.0` |\n"""
pred_cost = round(burn * (user_load / 48), 2)
pred_lat = round(latency * (user_load / 58), 1)
risk_lbl = ("๐Ÿ”ด HIGH" if pred_lat > 100 or pred_cost > 0.65
else "๐ŸŸก MEDIUM" if pred_lat > 60
else "๐ŸŸข LOW")
whatif_md = f"""### ๐Ÿ”ฎ What-If Simulation\n*If Load = {int(user_load)}% continues...*\n\n| Prediction | Value |\n|---|---|\n| Predicted cost | `${pred_cost}/hr` |\n| Predicted latency | `{pred_lat} ms` |\n| Risk level | {risk_lbl} |\n| Estimated savings | `{savings}` |\n"""
log = f"[{t}] {scenario}\n"
log += f"[{t}] Load: {load} req/s | Servers: {servers} | Burn: ${burn}/hr\n"
log += f"[{t}] Latency: {latency} ms | Risk: {drisk}\n"
log += f"[{t}] DECISION: {action} (conf {conf}%)\n"
log += f"[{t}] Score: {score} / 1.0 | Savings: {savings}"
gauge_fig = make_gauge_chart(int(user_load), "User Load", C["cyan"], seed)
burst_fig = make_radial_burst(load, "req/s", C["purple"], seed)
burst2_fig = make_radial_burst(int(score*100), "Scoreร—100", C["green"], seed+1)
area_fig = make_area_chart(user_load, seed)
bar_fig = make_neon_bar(auto_scaling, seed)
wave_fig = make_waveform_cost(budget_on, spot_avail, seed)
lat_fig = make_latency_wave(chaos_monkey, seed)
reward_fig = make_reward_curve(auto_scaling, seed)
before_fig = make_before_after(auto_scaling)
whatif_fig = make_whatif(int(user_load), int(spot_avail))
return (
f"๐Ÿš€ {load} req/s",
f"๐Ÿ–ฅ๏ธ {servers} instances",
f"๐Ÿ’ธ ${burn}/hr",
f"โšก {latency} ms",
f"๐Ÿ’ฐ {savings} saved",
f"๐Ÿ† {score} / 1.0",
decision_md,
xai_md,
health_md,
whatif_md,
log,
spend_str,
drisk,
score,
gauge_fig,
burst_fig,
burst2_fig,
area_fig,
bar_fig,
wave_fig,
lat_fig,
reward_fig,
before_fig,
whatif_fig,
)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# CSS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
CSS = """
body, .gradio-container {
background: #06091a !important;
color: #e2eeff !important;
font-family: 'Inter', sans-serif !important;
}
.gr-panel, .gr-box, .gr-form, .gr-block {
background: rgba(11,17,32,0.85) !important;
border: 0.5px solid #1e3a5f !important;
border-radius: 14px !important;
}
.hero-metric textarea, .hero-metric input {
font-size: 22px !important; font-weight: 800 !important;
color: #00e5ff !important; text-align: center !important;
}
.card-decision { background: rgba(255,61,90,0.07) !important; border: 1px solid rgba(255,61,90,0.35) !important; border-radius: 14px !important; padding: 6px 10px !important; }
.card-xai { background: rgba(0,229,255,0.05) !important; border: 1px solid rgba(0,229,255,0.25) !important; border-radius: 14px !important; padding: 6px 10px !important; }
.card-health { background: rgba(0,255,157,0.06) !important; border: 1px solid rgba(0,255,157,0.28) !important; border-radius: 14px !important; padding: 6px 10px !important; }
.card-whatif { background: rgba(192,132,252,0.06) !important; border: 1px solid rgba(192,132,252,0.28) !important; border-radius: 14px !important; padding: 6px 10px !important; }
.gr-button-primary {
background: linear-gradient(135deg, rgba(0,229,255,0.15), rgba(0,255,157,0.10)) !important;
border: 1.5px solid #00e5ff !important; color: #00e5ff !important;
font-size: 15px !important; font-weight: 700 !important;
border-radius: 12px !important; padding: 14px 0 !important;
}
.log-panel textarea { font-family: monospace !important; font-size: 11.5px !important; color: #7ab0d0 !important; background: #050810 !important; }
"""
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# GRADIO UI
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
with gr.Blocks(title="โ˜๏ธ Cloud-Cost Guardian AI") as demo:
gr.Markdown("# โ˜๏ธ Cloud-Cost Guardian AI\n#### RL-Powered Autonomous Infrastructure โ€” Live Intelligence Dashboard\n---")
gr.Markdown("## ๐Ÿ“Š Live System Metrics")
with gr.Row(equal_height=True):
load_out = gr.Textbox(label="๐Ÿš€ Incoming Load", interactive=False, elem_classes="hero-metric")
savings_out = gr.Textbox(label="๐Ÿ’ฐ Cost Saved", interactive=False, elem_classes="hero-metric")
score_out = gr.Textbox(label="๐Ÿ† Agent Score", interactive=False, elem_classes="hero-metric")
with gr.Row(equal_height=True):
server_out = gr.Textbox(label="๐Ÿ–ฅ๏ธ Servers", interactive=False)
burn_out = gr.Textbox(label="๐Ÿ’ธ Burn Rate", interactive=False)
latency_out = gr.Textbox(label="โšก Latency", interactive=False)
gr.Markdown("---")
with gr.Row():
with gr.Column(scale=1, min_width=260):
gr.Markdown("## ๐ŸŽฎ Controls")
scenario = gr.Dropdown(
choices=["Scenario 1: Steady Load (Easy)",
"Scenario 2: Daily Peak Surges (Medium)",
"Scenario 3: Chaos Mode (Hard)"],
value="Scenario 2: Daily Peak Surges (Medium)", label="Scenario")
user_load = gr.Slider(0, 100, value=75, step=1, label="User Load Scale (%)")
spot_avail = gr.Slider(0, 100, value=40, step=1, label="Spot Availability (%)")
chaos_monkey = gr.Checkbox(value=False, label="๐Ÿ’ Chaos Monkey โ€” Hard Mode")
budget_constr= gr.Checkbox(value=True, label="๐Ÿ’ฐ Budget Constraint Active")
auto_scaling = gr.Checkbox(value=True, label="๐Ÿค– RL Auto-Scaling Agent ON")
run_btn = gr.Button("๐Ÿš€ RUN SIMULATION", variant="primary", size="lg")
with gr.Column(scale=1, min_width=260):
decision_out = gr.Markdown("### ๐Ÿง  AI Decision\n*Click Run to start...*", elem_classes="card-decision")
xai_out = gr.Markdown("### ๐Ÿ’ก Explainable AI\n*Waiting...*", elem_classes="card-xai")
with gr.Column(scale=1, min_width=260):
health_out = gr.Markdown("### ๐ŸŸข Health Status\n*Click Run...*", elem_classes="card-health")
whatif_out = gr.Markdown("### ๐Ÿ”ฎ What-If Simulation\n*Adjust sliders...*",elem_classes="card-whatif")
gr.Markdown("---")
with gr.Row():
with gr.Column(scale=3):
log_out = gr.Textbox(lines=5, label="๐Ÿ“‹ Agent Action Log", interactive=False, elem_classes="log-panel")
with gr.Column(scale=1):
spend_out2 = gr.Textbox(label="Total Spend", interactive=False)
risk_out = gr.Textbox(label="Downtime Risk", interactive=False)
score_num = gr.Number( label="Agent Score / 1.0", interactive=False)
gr.Markdown("---")
gr.Markdown("## ๐ŸŽฏ Radial Intelligence โ€” Live Gauges")
with gr.Row():
gauge_plot = gr.Plot(label="Load Gauge")
burst_plot = gr.Plot(label="Request Burst")
burst2_plot = gr.Plot(label="Score Burst")
gr.Markdown("---")
gr.Markdown("## ๐Ÿ“ˆ Traffic & Utilisation")
with gr.Row():
area_plot = gr.Plot(label="Traffic Load โ€” Area")
bar_plot = gr.Plot(label="Server Utilisation โ€” Neon Bars")
gr.Markdown("---")
gr.Markdown("## ๐Ÿ’ก Cost & Performance Waveforms")
with gr.Row():
wave_plot = gr.Plot(label="Cost Burn Waveform")
lat_plot = gr.Plot(label="Latency Waveform + SLA")
gr.Markdown("---")
gr.Markdown("## ๐Ÿง  RL Intelligence & Impact")
with gr.Row():
reward_plot = gr.Plot(label="Cumulative RL Reward")
before_plot = gr.Plot(label="Before vs After RL")
whatif_plot = gr.Plot(label="What-If: Cost & Latency")
gr.Markdown("> ๐Ÿ’ก **Pro tip:** Turn on **Chaos Monkey** + set Load to **90%** for maximum drama!")
run_btn.click(
fn=run_simulation,
inputs=[scenario, user_load, spot_avail, chaos_monkey, budget_constr, auto_scaling],
outputs=[
load_out, server_out, burn_out, latency_out, savings_out, score_out,
decision_out, xai_out, health_out, whatif_out,
log_out, spend_out2, risk_out,
score_num,
gauge_plot, burst_plot, burst2_plot,
area_plot, bar_plot,
wave_plot, lat_plot,
reward_plot, before_plot, whatif_plot,
]
)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# MOUNT GRADIO INTO FASTAPI โ€” checker gets /reset /step /state
# Dashboard available at /ui
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
app = gr.mount_gradio_app(fapp, demo, path="/")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)