Spaces:
Sleeping
Sleeping
File size: 8,203 Bytes
9a8cd11 ebedc9d b820370 9a8cd11 ebedc9d 9a8cd11 ebedc9d b820370 ebedc9d 9a8cd11 b820370 9a8cd11 ebedc9d 9a8cd11 b820370 ebedc9d 9a8cd11 ebedc9d 9a8cd11 b820370 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d b820370 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d b820370 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d b820370 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d 9a8cd11 ebedc9d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | # ui.py β Visual agent dashboard for PipelineEnv
# Mounts a Gradio app at /ui on top of the FastAPI server.
# Uses a built-in deterministic agent β no external LLM required.
import json
import os
import time
import gradio as gr
from server.pipeline_environment import PipelineEnvironment
from models import PipelineAction, RepairAction
# ββ Config βββββββββββββββββββββββββββββββββββββββββββββ
USE_LLM = os.getenv("USE_LLM", "").lower() == "true"
API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
MODEL_NAME = os.getenv("MODEL_NAME") or "meta-llama/Llama-3.1-8B-Instruct"
API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY") or "dummy"
env = PipelineEnvironment()
# Deterministic action plans for each task
ACTION_PLANS = {
"easy": ["fix_test"],
"medium": ["fix_docker_config", "set_env_var"],
"hard": ["rollback_commit", "add_dependency", "fix_yaml_config"],
}
ACTION_ICONS = {
"fix_test": "\U0001f9ea",
"set_env_var": "\U0001f511",
"fix_docker_config": "\U0001f40b",
"fix_yaml_config": "\U0001f4c4",
"retry_stage": "\U0001f504",
"rollback_commit": "\u23ea",
"add_dependency": "\U0001f4e6",
"no_op": "\U0001f4a4",
}
# ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββ
def _render_env(obs, step, max_s):
lines = []
name = obs.get("pipeline_name", "pipeline")
lines.append(
f'<div style="color:#58a6ff;font-weight:700;margin-bottom:6px">'
f'\U0001f4e6 Pipeline: {name} | Step {step}/{max_s}</div>'
)
for s in obs.get("stages", []):
icon = {"passing": "\u2705", "failing": "\u274c", "skipped": "\u23ed\ufe0f"}.get(s["status"], "?")
err = f' <span style="color:#f85149;font-size:11px">({s["error"]})</span>' if s.get("error") else ""
lines.append(f'<span style="color:#cdd9e5">{icon} {s["name"]:<10} {s["status"]:<10}</span>{err}')
return "<br>".join(lines)
def _render_health(score):
pct = int(score * 100)
color = "#3fb950" if score >= 0.8 else "#d29922" if score >= 0.4 else "#f85149"
return (
f'<div style="height:12px;background:#21262d;border-radius:4px;margin:4px 0">'
f'<div style="height:12px;width:{pct}%;background:{color};border-radius:4px;transition:width .3s"></div></div>'
f'<div style="font-size:12px;color:#8b949e;text-align:right">Health: {score:.2f} / 1.0 ({pct}%)</div>'
)
def _deterministic_action(task_id, step_num):
"""Built-in agent that knows the correct action sequence."""
plan = ACTION_PLANS.get(task_id, [])
if step_num - 1 < len(plan):
return {"action": plan[step_num - 1], "target": None, "value": None}
return {"action": "no_op", "target": None, "value": None}
# ββ Yielding generator ββββββββββββββββββββββββββββββββββ
def run_task(task_id):
obs = env.reset(task_id).model_dump()
max_s = obs["max_steps"]
term_lines = []
action_log = []
rewards = []
success = False
current_score = 0.0
# reset banner
term_lines.append("\U0001f680 RESET \u2014 starting episode")
term_lines.append(f" Task : {task_id}")
term_lines.append(f" Desc : {obs['task_description']}")
term_lines.append(f" Steps : {max_s}")
term_lines.append(f" Health: {obs['health_score']:.2f}")
def _snapshot():
te = "<br>".join(term_lines)
term_box = f'<div style="font-family:JetBrains Mono,Fira Code,monospace;font-size:13px;line-height:1.6;color:#c9d1d9;background:#0d1117;padding:12px;border-radius:6px;white-space:pre-wrap;max-height:340px;overflow-y:auto">{te}</div>'
al = "<br>".join(f'<span style="color:#58a6ff;font-family:monospace;font-size:12px">{a}</span>' for a in action_log) if action_log else '<span style="color:#8b949e;font-size:12px">Waiting\u2026</span>'
sm = f'<span style="color:#8b949e;font-size:12px">Running\u2026 step {len(action_log)}/{max_s} | health {obs["health_score"]:.2f} | score {current_score:.3f}</span>'
return _render_env(obs, len(action_log), max_s), _render_health(obs["health_score"]), term_box, al, sm
yield _snapshot()
time.sleep(0.4)
for step_num in range(1, max_s + 1):
# Use deterministic agent
ad = _deterministic_action(task_id, step_num)
aname = ad.get("action", "no_op")
icon = ACTION_ICONS.get(aname, "\u2699")
action_log.append(f"{icon} Step {step_num}: {aname}")
term_lines.append(f' \u279c Action \u279c {aname}')
try:
ra = getattr(RepairAction, aname)
except Exception:
ra = RepairAction.no_op
result = env.step(PipelineAction(action=ra, target=ad.get("target"), value=ad.get("value")))
rwd = result.get("reward", 0) or 0.0
done = result.get("done", False)
obs = result.get("observation", obs)
info = result.get("info", {})
current_score = info.get("grader_score", current_score)
rewards.append(rwd)
if rwd > 0:
term_lines.append(f'<span style="color:#3fb950"> reward {rwd:+.3f} health \u2192 {obs["health_score"]:.2f}</span>')
elif rwd < 0:
term_lines.append(f'<span style="color:#f85149"> reward {rwd:+.3f} health \u2192 {obs["health_score"]:.2f}</span>')
else:
term_lines.append(f' reward {rwd:+.3f} health \u2192 {obs["health_score"]:.2f}')
yield _snapshot()
time.sleep(0.3)
if done:
success = current_score >= 0.99
break
# --- final summary ---
total_r = sum(rewards)
avg_r = total_r / len(rewards) if rewards else 0
sc = "#3fb950" if success else "#f85149"
st = "\u2705 PIPELINE HEALED" if success else "\u274c FAILED TO HEAL"
term_lines.append(f'<span style="color:{sc};font-weight:700;font-size:14px">{st}</span>')
term_lines.append(f' Steps: {len(action_log)} | Total reward: {total_r:+.3f} | Score: {current_score:.3f}')
term_lines.append(f' Agent: Deterministic (built-in)')
yield _snapshot()
# ββ Gradio layout ββββββββββββββββββββββββββββββββββββββ
with gr.Blocks(title="PipelineEnv \u2014 Agent Dashboard") as demo:
gr.HTML('<style>body{font-family:JetBrains Mono,Fira Code,monospace !important;}</style>')
gr.HTML(
'<div style="text-align:center;padding:20px 0 8px">'
'<div style="font-size:28px;font-weight:700">\U0001f527 PipelineEnv</div>'
'<div style="color:#8b949e;font-size:14px;margin-top:4px">RL Agent \u2022 CI/CD Self-Healing Dashboard</div></div>'
)
with gr.Row():
task_dd = gr.Dropdown(choices=["easy", "medium", "hard"], value="easy", label="Task")
run_btn = gr.Button("\u25b6 Run Agent", variant="primary")
with gr.Row():
with gr.Column(scale=1):
gr.HTML('<div style="color:#8b949e;font-size:12px;margin-bottom:4px">PIPELINE STAGES</div>')
pipeline_disp = gr.HTML('<div style="color:#8b949e">Select a task and click Run Agent.</div>')
gr.HTML('<div style="color:#8b949e;font-size:12px;margin:12px 0 4px">HEALTH</div>')
health_disp = gr.HTML("")
with gr.Column(scale=1):
gr.HTML('<div style="color:#8b949e;font-size:12px;margin-bottom:4px">ACTION LOG</div>')
action_disp = gr.HTML('<div style="color:#8b949e;font-size:12px">Waiting\u2026</div>')
gr.HTML('<div style="color:#8b949e;font-size:12px;margin:12px 0 4px">SUMMARY</div>')
summary_disp = gr.HTML("")
gr.HTML('<div style="color:#8b949e;font-size:12px;margin-bottom:4px">TERMINAL</div>')
terminal_disp = gr.HTML("")
run_btn.click(
fn=run_task,
inputs=[task_dd],
outputs=[pipeline_disp, health_disp, terminal_disp, action_disp, summary_disp],
)
if __name__ == "__main__":
demo.launch(server_port=7861)
|